@moxon/api 6.0.0-pre
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/.prettierignore +5 -0
- package/LICENSE +28 -0
- package/README.md +277 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +55 -0
- package/dist/app.js.map +1 -0
- package/dist/config/env.d.ts +22 -0
- package/dist/config/env.js +36 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/plans.d.ts +26 -0
- package/dist/config/plans.js +44 -0
- package/dist/config/plans.js.map +1 -0
- package/dist/config/versions.d.ts +18 -0
- package/dist/config/versions.js +248 -0
- package/dist/config/versions.js.map +1 -0
- package/dist/constants.d.ts +48 -0
- package/dist/constants.js +75 -0
- package/dist/constants.js.map +1 -0
- package/dist/middleware/cors.d.ts +2 -0
- package/dist/middleware/cors.js +31 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/error.d.ts +4 -0
- package/dist/middleware/error.js +8 -0
- package/dist/middleware/error.js.map +1 -0
- package/dist/middleware/json.d.ts +2 -0
- package/dist/middleware/json.js +8 -0
- package/dist/middleware/json.js.map +1 -0
- package/dist/middleware/logger.d.ts +26 -0
- package/dist/middleware/logger.js +65 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware/ratelimit.d.ts +2 -0
- package/dist/middleware/ratelimit.js +83 -0
- package/dist/middleware/ratelimit.js.map +1 -0
- package/dist/middleware/version.d.ts +3 -0
- package/dist/middleware/version.js +30 -0
- package/dist/middleware/version.js.map +1 -0
- package/dist/modules/v4/address.d.ts +20 -0
- package/dist/modules/v4/address.js +381 -0
- package/dist/modules/v4/address.js.map +1 -0
- package/dist/modules/v4/agent.d.ts +30 -0
- package/dist/modules/v4/agent.js +92 -0
- package/dist/modules/v4/agent.js.map +1 -0
- package/dist/modules/v4/algorithms.d.ts +90 -0
- package/dist/modules/v4/algorithms.js +279 -0
- package/dist/modules/v4/algorithms.js.map +1 -0
- package/dist/modules/v4/avatar.d.ts +23 -0
- package/dist/modules/v4/avatar.js +136 -0
- package/dist/modules/v4/avatar.js.map +1 -0
- package/dist/modules/v4/barcode.d.ts +27 -0
- package/dist/modules/v4/barcode.js +440 -0
- package/dist/modules/v4/barcode.js.map +1 -0
- package/dist/modules/v4/captcha.d.ts +22 -0
- package/dist/modules/v4/captcha.js +79 -0
- package/dist/modules/v4/captcha.js.map +1 -0
- package/dist/modules/v4/chat.d.ts +21 -0
- package/dist/modules/v4/chat.js +100 -0
- package/dist/modules/v4/chat.js.map +1 -0
- package/dist/modules/v4/color.d.ts +16 -0
- package/dist/modules/v4/color.js +59 -0
- package/dist/modules/v4/color.js.map +1 -0
- package/dist/modules/v4/convert.d.ts +15 -0
- package/dist/modules/v4/convert.js +116 -0
- package/dist/modules/v4/convert.js.map +1 -0
- package/dist/modules/v4/credit.d.ts +22 -0
- package/dist/modules/v4/credit.js +104 -0
- package/dist/modules/v4/credit.js.map +1 -0
- package/dist/modules/v4/cron.d.ts +17 -0
- package/dist/modules/v4/cron.js +202 -0
- package/dist/modules/v4/cron.js.map +1 -0
- package/dist/modules/v4/dice.d.ts +16 -0
- package/dist/modules/v4/dice.js +37 -0
- package/dist/modules/v4/dice.js.map +1 -0
- package/dist/modules/v4/domain.d.ts +6 -0
- package/dist/modules/v4/domain.js +83 -0
- package/dist/modules/v4/domain.js.map +1 -0
- package/dist/modules/v4/encode.d.ts +81 -0
- package/dist/modules/v4/encode.js +236 -0
- package/dist/modules/v4/encode.js.map +1 -0
- package/dist/modules/v4/geo.d.ts +30 -0
- package/dist/modules/v4/geo.js +51 -0
- package/dist/modules/v4/geo.js.map +1 -0
- package/dist/modules/v4/hash.d.ts +15 -0
- package/dist/modules/v4/hash.js +25 -0
- package/dist/modules/v4/hash.js.map +1 -0
- package/dist/modules/v4/hyperplanning.d.ts +19 -0
- package/dist/modules/v4/hyperplanning.js +80 -0
- package/dist/modules/v4/hyperplanning.js.map +1 -0
- package/dist/modules/v4/ip.d.ts +19 -0
- package/dist/modules/v4/ip.js +132 -0
- package/dist/modules/v4/ip.js.map +1 -0
- package/dist/modules/v4/levenshtein.d.ts +13 -0
- package/dist/modules/v4/levenshtein.js +37 -0
- package/dist/modules/v4/levenshtein.js.map +1 -0
- package/dist/modules/v4/palette.d.ts +19 -0
- package/dist/modules/v4/palette.js +56 -0
- package/dist/modules/v4/palette.js.map +1 -0
- package/dist/modules/v4/password.d.ts +25 -0
- package/dist/modules/v4/password.js +343 -0
- package/dist/modules/v4/password.js.map +1 -0
- package/dist/modules/v4/personal.d.ts +6 -0
- package/dist/modules/v4/personal.js +121 -0
- package/dist/modules/v4/personal.js.map +1 -0
- package/dist/modules/v4/placeholder.d.ts +30 -0
- package/dist/modules/v4/placeholder.js +160 -0
- package/dist/modules/v4/placeholder.js.map +1 -0
- package/dist/modules/v4/qrcode.d.ts +25 -0
- package/dist/modules/v4/qrcode.js +92 -0
- package/dist/modules/v4/qrcode.js.map +1 -0
- package/dist/modules/v4/regex.d.ts +23 -0
- package/dist/modules/v4/regex.js +48 -0
- package/dist/modules/v4/regex.js.map +1 -0
- package/dist/modules/v4/statistics.d.ts +20 -0
- package/dist/modules/v4/statistics.js +48 -0
- package/dist/modules/v4/statistics.js.map +1 -0
- package/dist/modules/v4/text.d.ts +43 -0
- package/dist/modules/v4/text.js +323 -0
- package/dist/modules/v4/text.js.map +1 -0
- package/dist/modules/v4/tic_tac_toe.d.ts +19 -0
- package/dist/modules/v4/tic_tac_toe.js +214 -0
- package/dist/modules/v4/tic_tac_toe.js.map +1 -0
- package/dist/modules/v4/time.d.ts +15 -0
- package/dist/modules/v4/time.js +124 -0
- package/dist/modules/v4/time.js.map +1 -0
- package/dist/modules/v4/token.d.ts +9 -0
- package/dist/modules/v4/token.js +45 -0
- package/dist/modules/v4/token.js.map +1 -0
- package/dist/modules/v4/username.d.ts +6 -0
- package/dist/modules/v4/username.js +92 -0
- package/dist/modules/v4/username.js.map +1 -0
- package/dist/modules/v4/validate.d.ts +34 -0
- package/dist/modules/v4/validate.js +75 -0
- package/dist/modules/v4/validate.js.map +1 -0
- package/dist/modules/v4.d.ts +32 -0
- package/dist/modules/v4.js +33 -0
- package/dist/modules/v4.js.map +1 -0
- package/dist/modules/v5/address.d.ts +5 -0
- package/dist/modules/v5/address.js +20 -0
- package/dist/modules/v5/address.js.map +1 -0
- package/dist/modules/v5/algorithms.d.ts +9 -0
- package/dist/modules/v5/algorithms.js +39 -0
- package/dist/modules/v5/algorithms.js.map +1 -0
- package/dist/modules/v5/asymmetric.d.ts +26 -0
- package/dist/modules/v5/asymmetric.js +74 -0
- package/dist/modules/v5/asymmetric.js.map +1 -0
- package/dist/modules/v5/barcode.d.ts +27 -0
- package/dist/modules/v5/barcode.js +424 -0
- package/dist/modules/v5/barcode.js.map +1 -0
- package/dist/modules/v5/base.d.ts +16 -0
- package/dist/modules/v5/base.js +41 -0
- package/dist/modules/v5/base.js.map +1 -0
- package/dist/modules/v5/captcha.d.ts +27 -0
- package/dist/modules/v5/captcha.js +136 -0
- package/dist/modules/v5/captcha.js.map +1 -0
- package/dist/modules/v5/case.d.ts +10 -0
- package/dist/modules/v5/case.js +57 -0
- package/dist/modules/v5/case.js.map +1 -0
- package/dist/modules/v5/chart.d.ts +53 -0
- package/dist/modules/v5/chart.js +385 -0
- package/dist/modules/v5/chart.js.map +1 -0
- package/dist/modules/v5/chat.d.ts +21 -0
- package/dist/modules/v5/chat.js +107 -0
- package/dist/modules/v5/chat.js.map +1 -0
- package/dist/modules/v5/csv.d.ts +29 -0
- package/dist/modules/v5/csv.js +143 -0
- package/dist/modules/v5/csv.js.map +1 -0
- package/dist/modules/v5/diff.d.ts +20 -0
- package/dist/modules/v5/diff.js +81 -0
- package/dist/modules/v5/diff.js.map +1 -0
- package/dist/modules/v5/evaluate.d.ts +14 -0
- package/dist/modules/v5/evaluate.js +284 -0
- package/dist/modules/v5/evaluate.js.map +1 -0
- package/dist/modules/v5/jwt.d.ts +11 -0
- package/dist/modules/v5/jwt.js +42 -0
- package/dist/modules/v5/jwt.js.map +1 -0
- package/dist/modules/v5/matrix.d.ts +77 -0
- package/dist/modules/v5/matrix.js +239 -0
- package/dist/modules/v5/matrix.js.map +1 -0
- package/dist/modules/v5/otp.d.ts +30 -0
- package/dist/modules/v5/otp.js +117 -0
- package/dist/modules/v5/otp.js.map +1 -0
- package/dist/modules/v5/pow.d.ts +29 -0
- package/dist/modules/v5/pow.js +49 -0
- package/dist/modules/v5/pow.js.map +1 -0
- package/dist/modules/v5/read.d.ts +20 -0
- package/dist/modules/v5/read.js +59 -0
- package/dist/modules/v5/read.js.map +1 -0
- package/dist/modules/v5/semver.d.ts +33 -0
- package/dist/modules/v5/semver.js +99 -0
- package/dist/modules/v5/semver.js.map +1 -0
- package/dist/modules/v5/symmetric.d.ts +15 -0
- package/dist/modules/v5/symmetric.js +88 -0
- package/dist/modules/v5/symmetric.js.map +1 -0
- package/dist/modules/v5/text.d.ts +5 -0
- package/dist/modules/v5/text.js +12 -0
- package/dist/modules/v5/text.js.map +1 -0
- package/dist/modules/v5/tic_tac_toe.d.ts +19 -0
- package/dist/modules/v5/tic_tac_toe.js +226 -0
- package/dist/modules/v5/tic_tac_toe.js.map +1 -0
- package/dist/modules/v5/url.d.ts +18 -0
- package/dist/modules/v5/url.js +51 -0
- package/dist/modules/v5/url.js.map +1 -0
- package/dist/modules/v5/uuid.d.ts +25 -0
- package/dist/modules/v5/uuid.js +40 -0
- package/dist/modules/v5/uuid.js.map +1 -0
- package/dist/modules/v5.d.ts +24 -0
- package/dist/modules/v5.js +25 -0
- package/dist/modules/v5.js.map +1 -0
- package/dist/modules/v6.d.ts +1 -0
- package/dist/modules/v6.js +2 -0
- package/dist/modules/v6.js.map +1 -0
- package/dist/routes/delete.d.ts +2 -0
- package/dist/routes/delete.js +73 -0
- package/dist/routes/delete.js.map +1 -0
- package/dist/routes/get.d.ts +2 -0
- package/dist/routes/get.js +857 -0
- package/dist/routes/get.js.map +1 -0
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.js +95 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/patch.d.ts +2 -0
- package/dist/routes/patch.js +45 -0
- package/dist/routes/patch.js.map +1 -0
- package/dist/routes/post.d.ts +2 -0
- package/dist/routes/post.js +604 -0
- package/dist/routes/post.js.map +1 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +14 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/types/storage.d.ts +52 -0
- package/dist/types/storage.js +2 -0
- package/dist/types/storage.js.map +1 -0
- package/dist/utils/challenge.d.ts +40 -0
- package/dist/utils/challenge.js +75 -0
- package/dist/utils/challenge.js.map +1 -0
- package/dist/utils/colors.d.ts +44 -0
- package/dist/utils/colors.js +93 -0
- package/dist/utils/colors.js.map +1 -0
- package/dist/utils/helpers.d.ts +55 -0
- package/dist/utils/helpers.js +82 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/response.d.ts +9 -0
- package/dist/utils/response.js +24 -0
- package/dist/utils/response.js.map +1 -0
- package/package.json +90 -0
- package/robots.txt +74 -0
- package/src/favicon.ico +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export type Matrix = number[][];
|
|
2
|
+
export interface MatrixResult {
|
|
3
|
+
operation: string;
|
|
4
|
+
result: Matrix | number;
|
|
5
|
+
dimensions: {
|
|
6
|
+
rows: number;
|
|
7
|
+
cols: number;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Adds two matrices element-wise.
|
|
12
|
+
*
|
|
13
|
+
* @param matrix - First matrix (2D array of finite numbers)
|
|
14
|
+
* @param matrix2 - Second matrix (must have the same dimensions as matrix)
|
|
15
|
+
* @returns MatrixResult with the resulting matrix and its dimensions
|
|
16
|
+
* @throws Error if either argument is not a valid matrix or dimensions do not match
|
|
17
|
+
*/
|
|
18
|
+
export declare function add(matrix: unknown, matrix2: unknown): MatrixResult;
|
|
19
|
+
/**
|
|
20
|
+
* Subtracts the second matrix from the first element-wise.
|
|
21
|
+
*
|
|
22
|
+
* @param matrix - Matrix to subtract from (2D array of finite numbers)
|
|
23
|
+
* @param matrix2 - Matrix to subtract (must have the same dimensions as matrix)
|
|
24
|
+
* @returns MatrixResult with the resulting matrix and its dimensions
|
|
25
|
+
* @throws Error if either argument is not a valid matrix or dimensions do not match
|
|
26
|
+
*/
|
|
27
|
+
export declare function subtract(matrix: unknown, matrix2: unknown): MatrixResult;
|
|
28
|
+
/**
|
|
29
|
+
* Multiplies two matrices using standard matrix multiplication.
|
|
30
|
+
*
|
|
31
|
+
* @param matrix - Left-hand matrix (2D array of finite numbers)
|
|
32
|
+
* @param matrix2 - Right-hand matrix; its row count must equal matrix's column count
|
|
33
|
+
* @returns MatrixResult with the resulting matrix and its dimensions
|
|
34
|
+
* @throws Error if either argument is not a valid matrix or inner dimensions are incompatible
|
|
35
|
+
*/
|
|
36
|
+
export declare function multiply(matrix: unknown, matrix2: unknown): MatrixResult;
|
|
37
|
+
/**
|
|
38
|
+
* Multiplies every element of a matrix by a scalar value.
|
|
39
|
+
*
|
|
40
|
+
* @param matrix - Matrix to scale (2D array of finite numbers)
|
|
41
|
+
* @param scalarVal - Finite number to multiply each element by
|
|
42
|
+
* @returns MatrixResult with the scaled matrix and its dimensions
|
|
43
|
+
* @throws Error if matrix is invalid or scalarVal is not a finite number
|
|
44
|
+
*/
|
|
45
|
+
export declare function scalar(matrix: unknown, scalarVal: unknown): MatrixResult;
|
|
46
|
+
/**
|
|
47
|
+
* Transposes a matrix, swapping its rows and columns.
|
|
48
|
+
*
|
|
49
|
+
* @param matrix - Matrix to transpose (2D array of finite numbers)
|
|
50
|
+
* @returns MatrixResult with the transposed matrix and its dimensions
|
|
51
|
+
* @throws Error if matrix is not a valid 2D array of finite numbers
|
|
52
|
+
*/
|
|
53
|
+
export declare function transpose(matrix: unknown): MatrixResult;
|
|
54
|
+
/**
|
|
55
|
+
* Computes the determinant of a square matrix using Gaussian elimination with partial pivoting.
|
|
56
|
+
*
|
|
57
|
+
* @param matrix - Square matrix (2D array of finite numbers)
|
|
58
|
+
* @returns MatrixResult with the determinant as a scalar result and the input dimensions
|
|
59
|
+
* @throws Error if matrix is not a valid square matrix
|
|
60
|
+
*/
|
|
61
|
+
export declare function determinant(matrix: unknown): MatrixResult;
|
|
62
|
+
/**
|
|
63
|
+
* Computes the inverse of a square matrix using Gauss-Jordan elimination.
|
|
64
|
+
*
|
|
65
|
+
* @param matrix - Square matrix (2D array of finite numbers)
|
|
66
|
+
* @returns MatrixResult with the inverted matrix and its dimensions
|
|
67
|
+
* @throws Error if matrix is not a valid square matrix or is singular (determinant is 0)
|
|
68
|
+
*/
|
|
69
|
+
export declare function inverse(matrix: unknown): MatrixResult;
|
|
70
|
+
/**
|
|
71
|
+
* Generates an n×n identity matrix.
|
|
72
|
+
*
|
|
73
|
+
* @param n - Size of the identity matrix (positive integer, max MAX_MATRIX_SIZE)
|
|
74
|
+
* @returns MatrixResult with the identity matrix and its dimensions
|
|
75
|
+
* @throws Error if n is not a positive integer or exceeds the maximum allowed size
|
|
76
|
+
*/
|
|
77
|
+
export declare function identity(n: unknown): MatrixResult;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { MAX_MATRIX_SIZE } from '../../constants.js';
|
|
2
|
+
function validateMatrix(m, name = 'matrix') {
|
|
3
|
+
if (!Array.isArray(m) || m.length === 0) {
|
|
4
|
+
throw new Error(`Field '${name}' must be a non-empty 2D array`);
|
|
5
|
+
}
|
|
6
|
+
const cols = m[0].length;
|
|
7
|
+
if (cols === 0)
|
|
8
|
+
throw new Error(`Field '${name}' rows must be non-empty`);
|
|
9
|
+
for (const row of m) {
|
|
10
|
+
if (!Array.isArray(row) || row.length !== cols) {
|
|
11
|
+
throw new Error(`All rows in '${name}' must have the same length`);
|
|
12
|
+
}
|
|
13
|
+
for (const cell of row) {
|
|
14
|
+
if (typeof cell !== 'number' || !isFinite(cell)) {
|
|
15
|
+
throw new Error(`Field '${name}' must contain finite numbers only`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (m.length > MAX_MATRIX_SIZE || cols > MAX_MATRIX_SIZE) {
|
|
20
|
+
throw new Error(`Matrix dimensions cannot exceed ${MAX_MATRIX_SIZE}×${MAX_MATRIX_SIZE}`);
|
|
21
|
+
}
|
|
22
|
+
return m;
|
|
23
|
+
}
|
|
24
|
+
function dims(m) {
|
|
25
|
+
return { rows: m.length, cols: m[0].length };
|
|
26
|
+
}
|
|
27
|
+
function assertSquare(m, op) {
|
|
28
|
+
const { rows, cols } = dims(m);
|
|
29
|
+
if (rows !== cols)
|
|
30
|
+
throw new Error(`Operation '${op}' requires a square matrix`);
|
|
31
|
+
}
|
|
32
|
+
function assertSameDimensions(a, b) {
|
|
33
|
+
if (a.length !== b.length || a[0].length !== b[0].length) {
|
|
34
|
+
throw new Error('Both matrices must have the same dimensions');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function copy(m) {
|
|
38
|
+
return m.map((row) => [...row]);
|
|
39
|
+
}
|
|
40
|
+
function clean(x) {
|
|
41
|
+
return x === 0 ? 0 : Number(x.toPrecision(12));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Adds two matrices element-wise.
|
|
45
|
+
*
|
|
46
|
+
* @param matrix - First matrix (2D array of finite numbers)
|
|
47
|
+
* @param matrix2 - Second matrix (must have the same dimensions as matrix)
|
|
48
|
+
* @returns MatrixResult with the resulting matrix and its dimensions
|
|
49
|
+
* @throws Error if either argument is not a valid matrix or dimensions do not match
|
|
50
|
+
*/
|
|
51
|
+
export function add(matrix, matrix2) {
|
|
52
|
+
const a = validateMatrix(matrix);
|
|
53
|
+
const b = validateMatrix(matrix2, 'matrix2');
|
|
54
|
+
assertSameDimensions(a, b);
|
|
55
|
+
const result = a.map((row, i) => row.map((v, j) => v + b[i][j]));
|
|
56
|
+
return { operation: 'add', result, dimensions: dims(result) };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Subtracts the second matrix from the first element-wise.
|
|
60
|
+
*
|
|
61
|
+
* @param matrix - Matrix to subtract from (2D array of finite numbers)
|
|
62
|
+
* @param matrix2 - Matrix to subtract (must have the same dimensions as matrix)
|
|
63
|
+
* @returns MatrixResult with the resulting matrix and its dimensions
|
|
64
|
+
* @throws Error if either argument is not a valid matrix or dimensions do not match
|
|
65
|
+
*/
|
|
66
|
+
export function subtract(matrix, matrix2) {
|
|
67
|
+
const a = validateMatrix(matrix);
|
|
68
|
+
const b = validateMatrix(matrix2, 'matrix2');
|
|
69
|
+
assertSameDimensions(a, b);
|
|
70
|
+
const result = a.map((row, i) => row.map((v, j) => v - b[i][j]));
|
|
71
|
+
return { operation: 'subtract', result, dimensions: dims(result) };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Multiplies two matrices using standard matrix multiplication.
|
|
75
|
+
*
|
|
76
|
+
* @param matrix - Left-hand matrix (2D array of finite numbers)
|
|
77
|
+
* @param matrix2 - Right-hand matrix; its row count must equal matrix's column count
|
|
78
|
+
* @returns MatrixResult with the resulting matrix and its dimensions
|
|
79
|
+
* @throws Error if either argument is not a valid matrix or inner dimensions are incompatible
|
|
80
|
+
*/
|
|
81
|
+
export function multiply(matrix, matrix2) {
|
|
82
|
+
const a = validateMatrix(matrix);
|
|
83
|
+
const b = validateMatrix(matrix2, 'matrix2');
|
|
84
|
+
const { rows: ar, cols: ac } = dims(a);
|
|
85
|
+
const { rows: br, cols: bc } = dims(b);
|
|
86
|
+
if (ac !== br) {
|
|
87
|
+
throw new Error(`Cannot multiply: columns of first matrix (${ac}) must equal rows of second (${br})`);
|
|
88
|
+
}
|
|
89
|
+
const result = Array.from({ length: ar }, () => new Array(bc).fill(0));
|
|
90
|
+
for (let i = 0; i < ar; i++) {
|
|
91
|
+
for (let j = 0; j < bc; j++) {
|
|
92
|
+
let sum = 0;
|
|
93
|
+
for (let k = 0; k < ac; k++) {
|
|
94
|
+
sum += a[i][k] * b[k][j];
|
|
95
|
+
}
|
|
96
|
+
result[i][j] = sum;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { operation: 'multiply', result, dimensions: dims(result) };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Multiplies every element of a matrix by a scalar value.
|
|
103
|
+
*
|
|
104
|
+
* @param matrix - Matrix to scale (2D array of finite numbers)
|
|
105
|
+
* @param scalarVal - Finite number to multiply each element by
|
|
106
|
+
* @returns MatrixResult with the scaled matrix and its dimensions
|
|
107
|
+
* @throws Error if matrix is invalid or scalarVal is not a finite number
|
|
108
|
+
*/
|
|
109
|
+
export function scalar(matrix, scalarVal) {
|
|
110
|
+
const a = validateMatrix(matrix);
|
|
111
|
+
if (typeof scalarVal !== 'number' || !isFinite(scalarVal)) {
|
|
112
|
+
throw new Error("Field 'scalar' must be a finite number");
|
|
113
|
+
}
|
|
114
|
+
const result = a.map((row) => row.map((v) => v * scalarVal));
|
|
115
|
+
return { operation: 'scalar', result, dimensions: dims(result) };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Transposes a matrix, swapping its rows and columns.
|
|
119
|
+
*
|
|
120
|
+
* @param matrix - Matrix to transpose (2D array of finite numbers)
|
|
121
|
+
* @returns MatrixResult with the transposed matrix and its dimensions
|
|
122
|
+
* @throws Error if matrix is not a valid 2D array of finite numbers
|
|
123
|
+
*/
|
|
124
|
+
export function transpose(matrix) {
|
|
125
|
+
const a = validateMatrix(matrix);
|
|
126
|
+
const { rows, cols } = dims(a);
|
|
127
|
+
const result = Array.from({ length: cols }, (_, j) => Array.from({ length: rows }, (__, i) => a[i][j]));
|
|
128
|
+
return { operation: 'transpose', result, dimensions: dims(result) };
|
|
129
|
+
}
|
|
130
|
+
function gaussElim(m) {
|
|
131
|
+
const n = m.length;
|
|
132
|
+
const lu = copy(m);
|
|
133
|
+
let sign = 1;
|
|
134
|
+
for (let col = 0; col < n; col++) {
|
|
135
|
+
// Partial pivoting
|
|
136
|
+
let maxRow = col;
|
|
137
|
+
for (let row = col + 1; row < n; row++) {
|
|
138
|
+
if (Math.abs(lu[row][col]) > Math.abs(lu[maxRow][col]))
|
|
139
|
+
maxRow = row;
|
|
140
|
+
}
|
|
141
|
+
if (maxRow !== col) {
|
|
142
|
+
[lu[col], lu[maxRow]] = [lu[maxRow], lu[col]];
|
|
143
|
+
sign *= -1;
|
|
144
|
+
}
|
|
145
|
+
const pivot = lu[col][col];
|
|
146
|
+
if (pivot === 0)
|
|
147
|
+
continue;
|
|
148
|
+
for (let row = col + 1; row < n; row++) {
|
|
149
|
+
const factor = lu[row][col] / pivot;
|
|
150
|
+
for (let k = col; k < n; k++) {
|
|
151
|
+
lu[row][k] -= factor * lu[col][k];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { lu, sign };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Computes the determinant of a square matrix using Gaussian elimination with partial pivoting.
|
|
159
|
+
*
|
|
160
|
+
* @param matrix - Square matrix (2D array of finite numbers)
|
|
161
|
+
* @returns MatrixResult with the determinant as a scalar result and the input dimensions
|
|
162
|
+
* @throws Error if matrix is not a valid square matrix
|
|
163
|
+
*/
|
|
164
|
+
export function determinant(matrix) {
|
|
165
|
+
const a = validateMatrix(matrix);
|
|
166
|
+
assertSquare(a, 'determinant');
|
|
167
|
+
const n = a.length;
|
|
168
|
+
if (n === 1)
|
|
169
|
+
return { operation: 'determinant', result: a[0][0], dimensions: dims(a) };
|
|
170
|
+
if (n === 2) {
|
|
171
|
+
const det = a[0][0] * a[1][1] - a[0][1] * a[1][0];
|
|
172
|
+
return { operation: 'determinant', result: det, dimensions: dims(a) };
|
|
173
|
+
}
|
|
174
|
+
const { lu, sign } = gaussElim(a);
|
|
175
|
+
let det = sign;
|
|
176
|
+
for (let i = 0; i < n; i++)
|
|
177
|
+
det *= lu[i][i];
|
|
178
|
+
const result = clean(det);
|
|
179
|
+
return { operation: 'determinant', result, dimensions: dims(a) };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Computes the inverse of a square matrix using Gauss-Jordan elimination.
|
|
183
|
+
*
|
|
184
|
+
* @param matrix - Square matrix (2D array of finite numbers)
|
|
185
|
+
* @returns MatrixResult with the inverted matrix and its dimensions
|
|
186
|
+
* @throws Error if matrix is not a valid square matrix or is singular (determinant is 0)
|
|
187
|
+
*/
|
|
188
|
+
export function inverse(matrix) {
|
|
189
|
+
const a = validateMatrix(matrix);
|
|
190
|
+
assertSquare(a, 'inverse');
|
|
191
|
+
const n = a.length;
|
|
192
|
+
// Build augmented matrix [A | I]
|
|
193
|
+
const aug = a.map((row, i) => [...row, ...Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))]);
|
|
194
|
+
// Gauss-Jordan elimination
|
|
195
|
+
for (let col = 0; col < n; col++) {
|
|
196
|
+
// Partial pivoting
|
|
197
|
+
let maxRow = col;
|
|
198
|
+
for (let row = col + 1; row < n; row++) {
|
|
199
|
+
if (Math.abs(aug[row][col]) > Math.abs(aug[maxRow][col]))
|
|
200
|
+
maxRow = row;
|
|
201
|
+
}
|
|
202
|
+
if (maxRow !== col)
|
|
203
|
+
[aug[col], aug[maxRow]] = [aug[maxRow], aug[col]];
|
|
204
|
+
const pivot = aug[col][col];
|
|
205
|
+
if (Math.abs(pivot) < 1e-12)
|
|
206
|
+
throw new Error('Matrix is singular (determinant is 0)');
|
|
207
|
+
for (let k = 0; k < 2 * n; k++)
|
|
208
|
+
aug[col][k] /= pivot;
|
|
209
|
+
for (let row = 0; row < n; row++) {
|
|
210
|
+
if (row === col)
|
|
211
|
+
continue;
|
|
212
|
+
const factor = aug[row][col];
|
|
213
|
+
for (let k = 0; k < 2 * n; k++) {
|
|
214
|
+
aug[row][k] -= factor * aug[col][k];
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Extract right half
|
|
219
|
+
const result = aug.map((row) => row.slice(n).map((v) => clean(v)));
|
|
220
|
+
return { operation: 'inverse', result, dimensions: dims(result) };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Generates an n×n identity matrix.
|
|
224
|
+
*
|
|
225
|
+
* @param n - Size of the identity matrix (positive integer, max MAX_MATRIX_SIZE)
|
|
226
|
+
* @returns MatrixResult with the identity matrix and its dimensions
|
|
227
|
+
* @throws Error if n is not a positive integer or exceeds the maximum allowed size
|
|
228
|
+
*/
|
|
229
|
+
export function identity(n) {
|
|
230
|
+
if (typeof n !== 'number' || !Number.isInteger(n) || n <= 0) {
|
|
231
|
+
throw new Error("Field 'scalar' must be a positive integer for the identity operation");
|
|
232
|
+
}
|
|
233
|
+
if (n > MAX_MATRIX_SIZE) {
|
|
234
|
+
throw new Error(`Identity size cannot exceed ${MAX_MATRIX_SIZE}`);
|
|
235
|
+
}
|
|
236
|
+
const result = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (__, j) => (i === j ? 1 : 0)));
|
|
237
|
+
return { operation: 'identity', result, dimensions: { rows: n, cols: n } };
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=matrix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrix.js","sourceRoot":"","sources":["../../../src/modules/v5/matrix.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface OtpSecretResult {
|
|
2
|
+
secret: string;
|
|
3
|
+
uri: string;
|
|
4
|
+
}
|
|
5
|
+
export interface OtpGenerateResult {
|
|
6
|
+
code: string;
|
|
7
|
+
remaining: number;
|
|
8
|
+
period: number;
|
|
9
|
+
}
|
|
10
|
+
export interface OtpVerifyResult {
|
|
11
|
+
valid: boolean;
|
|
12
|
+
drift: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generates or verifies TOTP/HOTP codes (RFC 4226 / RFC 6238).
|
|
16
|
+
*
|
|
17
|
+
* @param action - "secret", "generate", or "verify"
|
|
18
|
+
* @param options - Configuration options
|
|
19
|
+
* @returns Result depending on action
|
|
20
|
+
*/
|
|
21
|
+
export default function otp(action: string, options?: {
|
|
22
|
+
secret?: string;
|
|
23
|
+
code?: string;
|
|
24
|
+
algorithm?: string;
|
|
25
|
+
digits?: number;
|
|
26
|
+
period?: number;
|
|
27
|
+
counter?: number;
|
|
28
|
+
label?: string;
|
|
29
|
+
issuer?: string;
|
|
30
|
+
}): OtpSecretResult | OtpGenerateResult | OtpVerifyResult;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'crypto';
|
|
2
|
+
const BASE32_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
|
3
|
+
function encodeBase32(buffer) {
|
|
4
|
+
let bits = 0;
|
|
5
|
+
let value = 0;
|
|
6
|
+
let result = '';
|
|
7
|
+
for (const byte of buffer) {
|
|
8
|
+
value = (value << 8) | byte;
|
|
9
|
+
bits += 8;
|
|
10
|
+
while (bits >= 5) {
|
|
11
|
+
bits -= 5;
|
|
12
|
+
result += BASE32_CHARS[(value >>> bits) & 0x1f];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (bits > 0) {
|
|
16
|
+
result += BASE32_CHARS[(value << (5 - bits)) & 0x1f];
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
function decodeBase32(encoded) {
|
|
21
|
+
const cleaned = encoded.replace(/=+$/, '').toUpperCase();
|
|
22
|
+
let bits = 0;
|
|
23
|
+
let value = 0;
|
|
24
|
+
const bytes = [];
|
|
25
|
+
for (const char of cleaned) {
|
|
26
|
+
const idx = BASE32_CHARS.indexOf(char);
|
|
27
|
+
if (idx === -1)
|
|
28
|
+
throw new Error('Invalid base32 character');
|
|
29
|
+
value = (value << 5) | idx;
|
|
30
|
+
bits += 5;
|
|
31
|
+
if (bits >= 8) {
|
|
32
|
+
bits -= 8;
|
|
33
|
+
bytes.push((value >>> bits) & 0xff);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return Buffer.from(bytes);
|
|
37
|
+
}
|
|
38
|
+
function generateHotp(secret, counter, algorithm, digits) {
|
|
39
|
+
const counterBuf = Buffer.alloc(8);
|
|
40
|
+
counterBuf.writeBigUInt64BE(counter);
|
|
41
|
+
const hmac = createHmac(algorithm, secret).update(counterBuf).digest();
|
|
42
|
+
const offset = hmac[hmac.length - 1] & 0x0f;
|
|
43
|
+
const binary = ((hmac[offset] & 0x7f) << 24) |
|
|
44
|
+
((hmac[offset + 1] & 0xff) << 16) |
|
|
45
|
+
((hmac[offset + 2] & 0xff) << 8) |
|
|
46
|
+
(hmac[offset + 3] & 0xff);
|
|
47
|
+
const otp = binary % 10 ** digits;
|
|
48
|
+
return otp.toString().padStart(digits, '0');
|
|
49
|
+
}
|
|
50
|
+
function codesEqual(a, b) {
|
|
51
|
+
const ab = Buffer.from(a);
|
|
52
|
+
const bb = Buffer.from(b);
|
|
53
|
+
if (ab.length !== bb.length)
|
|
54
|
+
return false;
|
|
55
|
+
return timingSafeEqual(ab, bb);
|
|
56
|
+
}
|
|
57
|
+
const VALID_ALGORITHMS = new Set(['sha1', 'sha256', 'sha512']);
|
|
58
|
+
const VALID_DIGITS = new Set([6, 8]);
|
|
59
|
+
const VALID_PERIODS = new Set([15, 30, 60]);
|
|
60
|
+
/**
|
|
61
|
+
* Generates or verifies TOTP/HOTP codes (RFC 4226 / RFC 6238).
|
|
62
|
+
*
|
|
63
|
+
* @param action - "secret", "generate", or "verify"
|
|
64
|
+
* @param options - Configuration options
|
|
65
|
+
* @returns Result depending on action
|
|
66
|
+
*/
|
|
67
|
+
export default function otp(action, options = {}) {
|
|
68
|
+
if (!action || !['secret', 'generate', 'verify'].includes(action))
|
|
69
|
+
throw new Error('Action must be "secret", "generate", or "verify"');
|
|
70
|
+
const algorithm = options.algorithm ?? 'sha1';
|
|
71
|
+
const digits = options.digits ?? 6;
|
|
72
|
+
const period = options.period ?? 30;
|
|
73
|
+
if (!VALID_ALGORITHMS.has(algorithm))
|
|
74
|
+
throw new Error(`Algorithm must be one of: ${[...VALID_ALGORITHMS].join(', ')}`);
|
|
75
|
+
if (!VALID_DIGITS.has(digits))
|
|
76
|
+
throw new Error('Digits must be 6 or 8');
|
|
77
|
+
if (!VALID_PERIODS.has(period))
|
|
78
|
+
throw new Error('Period must be 15, 30, or 60');
|
|
79
|
+
if (action === 'secret') {
|
|
80
|
+
const label = options.label ?? 'user';
|
|
81
|
+
const issuer = options.issuer ?? 'API';
|
|
82
|
+
const secretBuf = randomBytes(20);
|
|
83
|
+
const secret = encodeBase32(secretBuf);
|
|
84
|
+
const uri = `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(label)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}&algorithm=${algorithm.toUpperCase()}&digits=${digits}&period=${period}`;
|
|
85
|
+
return { secret, uri };
|
|
86
|
+
}
|
|
87
|
+
if (!options.secret)
|
|
88
|
+
throw new Error('Secret is required for generate/verify');
|
|
89
|
+
if (options.counter !== undefined && (!Number.isInteger(options.counter) || options.counter < 0))
|
|
90
|
+
throw new Error('Counter must be a non-negative integer');
|
|
91
|
+
const secretBuf = decodeBase32(options.secret);
|
|
92
|
+
if (action === 'generate') {
|
|
93
|
+
const counter = options.counter !== undefined ? BigInt(options.counter) : BigInt(Math.floor(Date.now() / 1000 / period));
|
|
94
|
+
const code = generateHotp(secretBuf, counter, algorithm, digits);
|
|
95
|
+
const remaining = options.counter !== undefined ? period : period - (Math.floor(Date.now() / 1000) % period);
|
|
96
|
+
return { code, remaining, period };
|
|
97
|
+
}
|
|
98
|
+
// action === 'verify'
|
|
99
|
+
if (!options.code)
|
|
100
|
+
throw new Error('Code is required for verify');
|
|
101
|
+
const currentCounter = options.counter !== undefined ? BigInt(options.counter) : BigInt(Math.floor(Date.now() / 1000 / period));
|
|
102
|
+
for (let drift = 0; drift <= 1; drift++) {
|
|
103
|
+
const counter = currentCounter - BigInt(drift);
|
|
104
|
+
if (counter < 0n)
|
|
105
|
+
continue;
|
|
106
|
+
const code = generateHotp(secretBuf, counter, algorithm, digits);
|
|
107
|
+
if (codesEqual(code, options.code))
|
|
108
|
+
return { valid: true, drift: drift === 0 ? 0 : -drift };
|
|
109
|
+
}
|
|
110
|
+
for (let drift = 1; drift <= 1; drift++) {
|
|
111
|
+
const code = generateHotp(secretBuf, currentCounter + BigInt(drift), algorithm, digits);
|
|
112
|
+
if (codesEqual(code, options.code))
|
|
113
|
+
return { valid: true, drift };
|
|
114
|
+
}
|
|
115
|
+
return { valid: false, drift: 0 };
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=otp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otp.js","sourceRoot":"","sources":["../../../src/modules/v5/otp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ChallengeStorage } from '../../types/storage.js';
|
|
2
|
+
import type { ChallengeVerification } from '../../utils/challenge.js';
|
|
3
|
+
export interface PowChallenge {
|
|
4
|
+
algorithm: 'sha256';
|
|
5
|
+
salt: string;
|
|
6
|
+
difficulty: number;
|
|
7
|
+
expires: number;
|
|
8
|
+
token: string;
|
|
9
|
+
instructions: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Issues a proof-of-work challenge: the client must find a nonce whose hash
|
|
13
|
+
* starts with the requested number of hexadecimal zeros. Difficulty 4 averages
|
|
14
|
+
* about 32k hashes, unnoticeable for a person but costly to repeat at scale.
|
|
15
|
+
*
|
|
16
|
+
* @param difficulty - Leading hexadecimal zeros required (1-6, default 4)
|
|
17
|
+
* @returns Challenge with its salt, difficulty, expiry and signed token
|
|
18
|
+
* @throws Error if the difficulty is not a number or out of range
|
|
19
|
+
*/
|
|
20
|
+
export default function pow(difficulty?: number): PowChallenge;
|
|
21
|
+
/**
|
|
22
|
+
* Verifies a solved proof-of-work challenge, then consumes the token.
|
|
23
|
+
*
|
|
24
|
+
* @param token - Token issued by the challenge
|
|
25
|
+
* @param nonce - Nonce found by the client
|
|
26
|
+
* @param storage - Store of already consumed nonces
|
|
27
|
+
* @returns Verification result, with a reason when it fails
|
|
28
|
+
*/
|
|
29
|
+
export declare function verifyPow(token: string, nonce: string, storage: ChallengeStorage): ChallengeVerification;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { MAX_POW_DIFFICULTY, POW_DEFAULT_DIFFICULTY } from '../../constants.js';
|
|
3
|
+
import { readToken, signToken, verifyToken } from '../../utils/challenge.js';
|
|
4
|
+
const digest = (salt, nonce) => createHash('sha256').update(`${salt}${nonce}`).digest('hex');
|
|
5
|
+
/**
|
|
6
|
+
* Issues a proof-of-work challenge: the client must find a nonce whose hash
|
|
7
|
+
* starts with the requested number of hexadecimal zeros. Difficulty 4 averages
|
|
8
|
+
* about 32k hashes, unnoticeable for a person but costly to repeat at scale.
|
|
9
|
+
*
|
|
10
|
+
* @param difficulty - Leading hexadecimal zeros required (1-6, default 4)
|
|
11
|
+
* @returns Challenge with its salt, difficulty, expiry and signed token
|
|
12
|
+
* @throws Error if the difficulty is not a number or out of range
|
|
13
|
+
*/
|
|
14
|
+
export default function pow(difficulty = POW_DEFAULT_DIFFICULTY) {
|
|
15
|
+
if (isNaN(difficulty))
|
|
16
|
+
throw new Error('Difficulty must be a number');
|
|
17
|
+
if (!Number.isInteger(difficulty) || difficulty < 1 || difficulty > MAX_POW_DIFFICULTY) {
|
|
18
|
+
throw new Error(`Difficulty must be between 1 and ${MAX_POW_DIFFICULTY}`);
|
|
19
|
+
}
|
|
20
|
+
const salt = randomBytes(8).toString('hex');
|
|
21
|
+
const token = signToken('pow', `${salt}.${difficulty}`, { salt, difficulty });
|
|
22
|
+
return {
|
|
23
|
+
algorithm: 'sha256',
|
|
24
|
+
salt,
|
|
25
|
+
difficulty,
|
|
26
|
+
expires: readToken(token).exp,
|
|
27
|
+
token,
|
|
28
|
+
instructions: `Find nonce so that sha256(salt + nonce) starts with ${difficulty} hex zeros`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Verifies a solved proof-of-work challenge, then consumes the token.
|
|
33
|
+
*
|
|
34
|
+
* @param token - Token issued by the challenge
|
|
35
|
+
* @param nonce - Nonce found by the client
|
|
36
|
+
* @param storage - Store of already consumed nonces
|
|
37
|
+
* @returns Verification result, with a reason when it fails
|
|
38
|
+
*/
|
|
39
|
+
export function verifyPow(token, nonce, storage) {
|
|
40
|
+
const decoded = readToken(token);
|
|
41
|
+
if (!decoded || decoded.type !== 'pow' || !decoded.salt || !decoded.difficulty) {
|
|
42
|
+
return { valid: false, reason: 'invalid' };
|
|
43
|
+
}
|
|
44
|
+
if (!digest(decoded.salt, nonce).startsWith('0'.repeat(decoded.difficulty))) {
|
|
45
|
+
return { valid: false, reason: 'wrong' };
|
|
46
|
+
}
|
|
47
|
+
return verifyToken(token, `${decoded.salt}.${decoded.difficulty}`, storage);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=pow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pow.js","sourceRoot":"","sources":["../../../src/modules/v5/pow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAGhF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAW7E,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,KAAa,EAAU,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAErH;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,aAAqB,sBAAsB;IACnE,IAAI,KAAK,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACtE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,kBAAkB,EAAE,CAAC;QACrF,MAAM,IAAI,KAAK,CAAC,oCAAoC,kBAAkB,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAE9E,OAAO;QACH,SAAS,EAAE,QAAQ;QACnB,IAAI;QACJ,UAAU;QACV,OAAO,EAAE,SAAS,CAAC,KAAK,CAAE,CAAC,GAAG;QAC9B,KAAK;QACL,YAAY,EAAE,uDAAuD,UAAU,YAAY;KAC9F,CAAC;AACN,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,KAAa,EAAE,OAAyB;IAC7E,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC7E,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QAC1E,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC7C,CAAC;IAED,OAAO,WAAW,CAAC,KAAK,EAAE,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,CAAC;AAChF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ReadResult {
|
|
2
|
+
lang: string;
|
|
3
|
+
words: number;
|
|
4
|
+
sentences: number;
|
|
5
|
+
syllables: number;
|
|
6
|
+
fleschReadingEase: number;
|
|
7
|
+
fleschKincaidGrade: number | null;
|
|
8
|
+
readingTime: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Computes Flesch readability scores on a text. English uses the original
|
|
12
|
+
* Flesch coefficients; French uses the Kandel & Moles adaptation, which has no
|
|
13
|
+
* grade-level counterpart, so fleschKincaidGrade is null for French.
|
|
14
|
+
*
|
|
15
|
+
* @param text - The text to analyze
|
|
16
|
+
* @param lang - Language of the text: en (default) or fr
|
|
17
|
+
* @returns Word, sentence and syllable counts, Flesch scores and estimated reading time
|
|
18
|
+
* @throws Error if the text is missing or too long, or if the language is unsupported
|
|
19
|
+
*/
|
|
20
|
+
export default function read(text: string, lang?: string): ReadResult;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { MAX_READ_LENGTH } from '../../constants.js';
|
|
2
|
+
const LANGUAGES = ['en', 'fr'];
|
|
3
|
+
// English heuristic: vowel groups minus silent trailing "e", at least one syllable.
|
|
4
|
+
function countSyllablesEn(word) {
|
|
5
|
+
const groups = word
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/[^a-z]/g, '')
|
|
8
|
+
.replace(/e$/, '')
|
|
9
|
+
.match(/[aeiouy]+/g);
|
|
10
|
+
return Math.max(1, groups ? groups.length : 1);
|
|
11
|
+
}
|
|
12
|
+
// French heuristic: accented vowels count as vowels, a trailing "e"/"es" is silent.
|
|
13
|
+
function countSyllablesFr(word) {
|
|
14
|
+
const groups = word
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.replace(/[^a-zàâäéèêëîïôöùûüÿç]/g, '')
|
|
17
|
+
.replace(/es?$/, '')
|
|
18
|
+
.match(/[aeiouyàâäéèêëîïôöùûüÿ]+/g);
|
|
19
|
+
return Math.max(1, groups ? groups.length : 1);
|
|
20
|
+
}
|
|
21
|
+
const round2 = (n) => Math.round(n * 100) / 100;
|
|
22
|
+
/**
|
|
23
|
+
* Computes Flesch readability scores on a text. English uses the original
|
|
24
|
+
* Flesch coefficients; French uses the Kandel & Moles adaptation, which has no
|
|
25
|
+
* grade-level counterpart, so fleschKincaidGrade is null for French.
|
|
26
|
+
*
|
|
27
|
+
* @param text - The text to analyze
|
|
28
|
+
* @param lang - Language of the text: en (default) or fr
|
|
29
|
+
* @returns Word, sentence and syllable counts, Flesch scores and estimated reading time
|
|
30
|
+
* @throws Error if the text is missing or too long, or if the language is unsupported
|
|
31
|
+
*/
|
|
32
|
+
export default function read(text, lang = 'en') {
|
|
33
|
+
if (!text || !text.trim())
|
|
34
|
+
throw new Error('Please provide a text');
|
|
35
|
+
if (text.length > MAX_READ_LENGTH)
|
|
36
|
+
throw new Error(`Text must be ${MAX_READ_LENGTH} characters or fewer`);
|
|
37
|
+
if (!LANGUAGES.includes(lang))
|
|
38
|
+
throw new Error(`Language must be one of: ${LANGUAGES.join(', ')}`);
|
|
39
|
+
const wordList = text
|
|
40
|
+
.trim()
|
|
41
|
+
.split(/\s+/)
|
|
42
|
+
.filter((w) => /[\p{L}\p{N}]/u.test(w));
|
|
43
|
+
const words = wordList.length;
|
|
44
|
+
if (words === 0)
|
|
45
|
+
throw new Error('Please provide a text');
|
|
46
|
+
const sentences = text.split(/[.!?…]+/).filter((s) => s.trim()).length || 1;
|
|
47
|
+
const countSyllables = lang === 'fr' ? countSyllablesFr : countSyllablesEn;
|
|
48
|
+
const syllables = wordList.reduce((sum, word) => sum + countSyllables(word), 0);
|
|
49
|
+
const wordsPerSentence = words / sentences;
|
|
50
|
+
const syllablesPerWord = syllables / words;
|
|
51
|
+
const fleschReadingEase = lang === 'fr'
|
|
52
|
+
? round2(207 - 1.015 * wordsPerSentence - 73.6 * syllablesPerWord)
|
|
53
|
+
: round2(206.835 - 1.015 * wordsPerSentence - 84.6 * syllablesPerWord);
|
|
54
|
+
const fleschKincaidGrade = lang === 'fr' ? null : round2(0.39 * wordsPerSentence + 11.8 * syllablesPerWord - 15.59);
|
|
55
|
+
const minutes = words / 200;
|
|
56
|
+
const readingTime = minutes < 1 ? `${Math.ceil(minutes * 60)}s` : `${Math.round(minutes)}min`;
|
|
57
|
+
return { lang, words, sentences, syllables, fleschReadingEase, fleschKincaidGrade, readingTime };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=read.js.map
|