@tma.js/init-data-node 1.4.0 → 2.0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsing-Cj_BXCnv.cjs","sources":["../../src/errors.ts","../../src/buf-converters.ts","../../src/hashToken.ts","../../src/signDataFp.ts","../../src/signFp.ts","../../src/validation.ts","../../src/parsing.ts"],"sourcesContent":["import { errorClass, errorClassWithData } from 'error-kid';\n\nexport class AuthDateInvalidError extends errorClassWithData<\n { value: string | undefined },\n [value?: string]\n>(\n 'AuthDateInvalidError',\n value => ({ value }),\n value => [`\"auth_date\" is invalid: ${value || 'value is missing'}`],\n) {\n}\n\nexport class SignatureInvalidError extends errorClass('SignatureInvalidError') {\n}\n\nexport class HexStringLengthInvalidError extends errorClass(\n 'HexStringLengthInvalidError',\n) {\n}\n\nexport class SignatureMissingError extends errorClass<[thirdParty: boolean]>(\n 'SignatureMissingError',\n thirdParty => [`\"${thirdParty ? 'signature' : 'hash'}\" parameter is missing`],\n) {\n}\n\nexport class ExpiredError extends errorClassWithData<\n { issuedAt: Date; expiresAt: Date },\n [issuedAt: Date, expiresAt: Date, now: Date]\n>(\n 'ExpiredError',\n (issuedAt, expiresAt) => ({ issuedAt, expiresAt }),\n (issuedAt, expiresAt, now) => [\n `Init data expired. Issued at ${issuedAt.toISOString()}, expires at ${expiresAt.toISOString()}, now is ${now.toISOString()}`,\n ],\n) {\n}\n","import * as E from 'fp-ts/Either';\n\nimport { HexStringLengthInvalidError } from './errors.js';\n\n/**\n * Converts a hex string to ArrayBuffer.\n * @param hexString - value to convert.\n */\nexport function hexToArrayBuffer(hexString: string): E.Either<\n InstanceType<typeof HexStringLengthInvalidError>,\n ArrayBuffer\n> {\n if (hexString.length % 2 !== 0) {\n return E.left(new HexStringLengthInvalidError());\n }\n const buffer = new ArrayBuffer(hexString.length / 2);\n const uint8Array = new Uint8Array(buffer);\n for (let i = 0; i < hexString.length; i += 2) {\n uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);\n }\n return E.right(buffer);\n}\n\n/**\n * Converts array buffer to hex.\n * @param arrBuf - buffer to convert\n */\nexport function arrayBufferToHex(arrBuf: ArrayBuffer): string {\n return new Uint8Array(arrBuf).reduce((acc, byte) => {\n // Convert byte to hex and pad with zero if needed (e.g., \"0a\" instead of \"a\")\n return acc + byte.toString(16).padStart(2, '0');\n }, '');\n}\n\nexport function bufferToArrayBuffer(buf: Buffer) {\n const ab = new ArrayBuffer(buf.length);\n buf.copy(new Uint8Array(ab));\n return ab;\n}\n","import type { CreateHmacFn, Text } from './types.js';\n\nexport function hashToken<H extends CreateHmacFn<any>>(token: Text, createHmac: H): ReturnType<H> {\n return createHmac(token, 'WebAppData') as ReturnType<H>;\n}\n","import * as E from 'fp-ts/Either';\nimport * as TE from 'fp-ts/TaskEither';\nimport { pipe } from 'fp-ts/lib/function.js';\n\nimport { arrayBufferToHex, hexToArrayBuffer } from './buf-converters.js';\nimport { hashToken } from './hashToken.js';\nimport type { CreateHmacFn, Text } from './types.js';\n\nexport interface SignDataOptions {\n /**\n * True, if token is already hashed and doesn't require hashing using HMAC-SHA-256.\n */\n tokenHashed?: boolean;\n}\n\nexport type SignDataError = ReturnType<typeof hexToArrayBuffer> extends E.Either<infer U, any>\n ? U\n : never;\n\nexport function signDataFp(\n async: false,\n data: Text,\n key: Text,\n createHmac: CreateHmacFn<false>,\n options?: SignDataOptions,\n): E.Either<SignDataError, string>;\n\nexport function signDataFp(\n async: true,\n data: Text,\n key: Text,\n createHmac: CreateHmacFn<true>,\n options?: SignDataOptions,\n): TE.TaskEither<SignDataError, string>;\n\nexport function signDataFp(\n async: boolean,\n data: Text,\n key: Text,\n createHmac: CreateHmacFn<boolean>,\n options: SignDataOptions = {},\n):\n | E.Either<SignDataError, string>\n | TE.TaskEither<SignDataError, string> {\n const keyHmac = options.tokenHashed\n ? typeof key === 'string'\n // If a hashed token was passed, we assume that it is a HEX string. Not to mess with\n // the createHmac function, we should convert this HEX string to ArrayBuffer. Otherwise,\n // incorrect behavior will be met.\n ? hexToArrayBuffer(key)\n : E.right(key)\n // Otherwise we are hashing the token, but we want it to be a monad.\n : pipe(\n E.right(hashToken(key, createHmac)),\n E.match(() => null as never, v => {\n return v instanceof Promise\n ? TE.tryCatch(() => v, err => err as SignDataError)\n : E.right(v);\n }),\n );\n\n if (async || typeof keyHmac === 'function') {\n return pipe(\n typeof keyHmac === 'function' ? keyHmac : TE.fromEither(keyHmac),\n TE.chain(v => TE.tryCatch(\n () => Promise.resolve(createHmac(data, v)).then(arrayBufferToHex),\n err => err as SignDataError,\n )),\n );\n }\n return pipe(\n keyHmac,\n // In this branch createHmac can't be asynchronous. If it is, keyHmac would be Promise and the\n // result would be returned in the previous \"if\" statement.\n E.chain(v => E.right(\n arrayBufferToHex((createHmac as CreateHmacFn<false>)(data, v)),\n )),\n );\n}\n","import { type InitDataLike, serializeInitDataQuery } from '@tma.js/transformers';\nimport * as E from 'fp-ts/Either';\nimport * as TE from 'fp-ts/TaskEither';\nimport { pipe } from 'fp-ts/lib/function.js';\n\nimport type { Text } from './types.js';\n\nexport type SignableData = Omit<InitDataLike, 'auth_date' | 'hash' | 'signature'>;\n\nexport interface SignOptions {\n /**\n * True, if token is already hashed and doesn't require hashing using HMAC-SHA-256.\n */\n tokenHashed?: boolean;\n}\n\ninterface SignDataFpArg<Async extends boolean, Left> {\n (data: Text, key: Text, options?: SignOptions): Async extends true\n ? TE.TaskEither<Left, string>\n : E.Either<Left, string>;\n}\n\n/**\n * Signs specified init data.\n * @param data - init data to sign.\n * @param authDate - date, when this init data should be signed.\n * @param key - private key.\n * @param signData - function signing data.\n * @param options - additional options.\n * @returns Signed init data presented as query parameters.\n */\nexport function signFp<Left>(\n data: SignableData,\n key: Text,\n authDate: Date,\n signData: SignDataFpArg<false, Left>,\n options?: SignOptions,\n): E.Either<Left, string>;\n\n/**\n * Signs specified init data.\n * @param data - init data to sign.\n * @param authDate - date, when this init data should be signed.\n * @param key - private key.\n * @param signData - function signing data.\n * @param options - additional options.\n * @returns Signed init data presented as query parameters.\n */\nexport function signFp<Left>(\n data: SignableData,\n key: Text,\n authDate: Date,\n signData: SignDataFpArg<true, Left>,\n options?: SignOptions,\n): TE.TaskEither<Left, string>;\n\nexport function signFp<Left>(\n data: SignableData,\n key: Text,\n authDate: Date,\n signData: SignDataFpArg<boolean, Left>,\n options?: SignOptions,\n): E.Either<Left, string> | TE.TaskEither<Left, string> {\n const query = new URLSearchParams(serializeInitDataQuery({ ...data, auth_date: authDate }));\n\n // Convert search params to pairs and sort the final array.\n const pairs = [...query.entries()]\n .map(([name, value]) => `${name}=${value}`)\n .sort();\n\n // Compute sign, append it to the params and return.\n const queryWithHash = (signature: string): string => {\n query.append('hash', signature);\n return query.toString();\n };\n\n const eitherHash = signData(pairs.join('\\n'), key, options);\n return typeof eitherHash === 'function'\n ? pipe(eitherHash, TE.chain(hash => TE.right(queryWithHash(hash))))\n : pipe(eitherHash, E.chain(hash => E.right(queryWithHash(hash))));\n}\n","import * as E from 'fp-ts/Either';\nimport * as TE from 'fp-ts/TaskEither';\nimport { pipe } from 'fp-ts/function';\nimport {\n BetterPromise,\n type BetterPromiseOptions,\n type BetterPromiseRejectReason,\n} from 'better-promises';\n\nimport {\n AuthDateInvalidError,\n ExpiredError,\n SignatureInvalidError,\n SignatureMissingError,\n} from './errors.js';\nimport { Text } from './types.js';\n\ntype OmittedPromiseOptions = Omit<BetterPromiseOptions, 'abortOnResolve' | 'abortOnReject'>;\n\nexport type ValidateValue = string | URLSearchParams;\nexport type Validate3rdValue = string | URLSearchParams;\n\nexport type ValidateError =\n | SignatureMissingError\n | SignatureInvalidError\n | AuthDateInvalidError\n | ExpiredError;\nexport type ValidateAsyncError = ValidateError | BetterPromiseRejectReason;\nexport type Validate3rdError =\n | SignatureMissingError\n | SignatureInvalidError\n | AuthDateInvalidError\n | ExpiredError\n | BetterPromiseRejectReason;\n\ninterface ValidateSignDataFpArg<Async extends boolean, Left> {\n (data: Text, key: Text, options?: ValidateOptions): Async extends true\n ? TE.TaskEither<Left, string>\n : E.Either<Left, string>;\n}\n\ninterface SharedValidateOptions {\n /**\n * Time in seconds which states, how long from creation time init data is considered valid.\n *\n * In other words, in case when authDate + expiresIn is before current time, init data is\n * recognized as expired.\n *\n * In case this value is equal to 0, the function does not check init data expiration.\n * @default 86400 (1 day)\n */\n expiresIn?: number;\n}\n\nexport interface ValidateOptions extends SharedValidateOptions {\n /**\n * True, if token is already hashed.\n * @default false\n */\n tokenHashed?: boolean;\n}\n\nexport interface ValidateAsyncOptions extends ValidateOptions, OmittedPromiseOptions {\n}\n\nexport interface Validate3rdOptions extends SharedValidateOptions, OmittedPromiseOptions {\n /**\n * When true, uses the test environment public key to validate init data.\n * @default false\n */\n test?: boolean;\n}\n\n/**\n * Validates passed init data using a publicly known Ee25519 key.\n * @param value - value to check.\n * @param botId - bot identifier\n * @param options - additional validation options.\n */\nexport function validate3rdFp(\n value: Validate3rdValue,\n botId: number,\n options: Validate3rdOptions = {},\n): TE.TaskEither<Validate3rdError, void> {\n // Init data required params.\n let authDate: Date | undefined;\n let authDateString: string | undefined;\n let signature: string | undefined;\n\n // All search params pairs presented as `k=v`.\n const pairs: string[] = [];\n\n // Iterate over all key-value pairs of parsed parameters and find required\n // parameters.\n (typeof value === 'string' ? new URLSearchParams(value) : value).forEach((value, key) => {\n if (key === 'hash') {\n return;\n }\n if (key === 'signature') {\n signature = value;\n return;\n }\n if (key === 'auth_date') {\n authDateString = value;\n const authDateNum = parseInt(value, 10);\n if (!Number.isNaN(authDateNum)) {\n authDate = new Date(authDateNum * 1000);\n }\n }\n\n pairs.push(`${key}=${value}`);\n });\n\n // Signature and auth date always required.\n if (!signature) {\n return TE.left(new SignatureMissingError(true));\n }\n\n if (!authDate) {\n return TE.left(new AuthDateInvalidError(authDateString));\n }\n\n // In case, expiration time passed, we do additional parameters check.\n const { expiresIn = 86400 } = options;\n if (expiresIn > 0) {\n // Check if init data expired.\n const expiresAtTs = authDate.getTime() + (expiresIn * 1000);\n const nowTs = Date.now();\n if (expiresAtTs < nowTs) {\n return TE.left(new ExpiredError(authDate, new Date(expiresAtTs), new Date(nowTs)));\n }\n }\n\n return pipe(\n TE.tryCatch(\n () => {\n return BetterPromise.fn(async () => {\n return crypto.subtle.verify(\n 'Ed25519',\n await crypto.subtle.importKey(\n 'raw',\n Buffer.from(\n options.test\n ? '40055058a4ee38156a06562e52eece92a771bcd8346a8c4615cb7376eddf72ec'\n : 'e7bf03a2fa4602af4580703d88dda5bb59f32ed8b02a56c187fe7d34caed242d',\n 'hex',\n ),\n 'Ed25519',\n false,\n ['verify'],\n ),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n Buffer.from(signature as string, 'base64'),\n Buffer.from(`${botId}:WebAppData\\n${pairs.sort().join('\\n')}`),\n );\n }, options);\n },\n (e: unknown) => e,\n ),\n TE.chainW(isVerified => {\n return isVerified ? TE.right(undefined) : TE.left(new SignatureInvalidError());\n }),\n );\n}\n\n/**\n * @see validate3rdFp\n */\nexport function validate3rd(\n value: Validate3rdValue,\n botId: number,\n options?: Validate3rdOptions,\n): BetterPromise<void> {\n return BetterPromise.fn(async () => {\n await pipe(\n validate3rdFp(value, botId, options),\n TE.mapLeft(error => {\n throw error;\n }),\n )();\n });\n}\n\n/**\n * @param value - value to check.\n * @param botId - bot identifier\n * @param options - additional validation options.\n * @returns True is specified init data is signed by Telegram.\n */\nexport function isValid3rdFp(\n value: Validate3rdValue,\n botId: number,\n options?: Validate3rdOptions,\n): TE.TaskEither<void, boolean> {\n return pipe(validate3rdFp(value, botId, options), TE.match(\n () => E.right(false),\n () => E.right(true),\n ));\n}\n\n/**\n * @see isValid3rdFp\n */\nexport function isValid3rd(\n value: Validate3rdValue,\n botId: number,\n options?: Validate3rdOptions,\n): BetterPromise<boolean> {\n return BetterPromise.fn(() => pipe(\n isValid3rdFp(value, botId, options),\n TE.match(() => false, v => v),\n )());\n}\n\nexport function validateFp<Left>(\n async: false,\n value: ValidateValue,\n token: Text,\n signData: ValidateSignDataFpArg<false, Left>,\n options?: ValidateOptions,\n): E.Either<Left | ValidateError, void>;\n\nexport function validateFp<Left>(\n async: true,\n value: ValidateValue,\n token: Text,\n signData: ValidateSignDataFpArg<true, Left>,\n options?: ValidateAsyncOptions,\n): TE.TaskEither<Left | ValidateAsyncError, void>;\n\nexport function validateFp<Left>(\n async: boolean,\n value: ValidateValue,\n token: Text,\n signData: ValidateSignDataFpArg<boolean, Left>,\n options: ValidateOptions | ValidateAsyncOptions = {},\n):\n | E.Either<Left | ValidateError, void>\n | TE.TaskEither<Left | ValidateAsyncError, void> {\n // Init data required params.\n let authDate: Date | undefined;\n let authDateString: string | undefined;\n let hash: string | undefined;\n\n // All search params pairs presented as `k=v`.\n const pairs: string[] = [];\n\n // Iterate over all key-value pairs of parsed parameters and find required\n // parameters.\n (typeof value === 'string' ? new URLSearchParams(value) : value).forEach((value, key) => {\n if (key === 'hash') {\n hash = value;\n return;\n }\n\n if (key === 'auth_date') {\n authDateString = value;\n const authDateNum = parseInt(value, 10);\n if (!Number.isNaN(authDateNum)) {\n authDate = new Date(authDateNum * 1000);\n }\n }\n\n pairs.push(`${key}=${value}`);\n });\n\n // Hash and auth date always required.\n if (!hash) {\n return (async ? TE.left : E.left)(new SignatureMissingError(false));\n }\n\n if (!authDate) {\n return (async ? TE.left : E.left)(new AuthDateInvalidError(authDateString));\n }\n\n // In case, expiration time passed, we do additional parameters check.\n const { expiresIn = 86400 } = options;\n if (expiresIn > 0) {\n // Check if init data expired.\n const expiresAtTs = authDate.getTime() + (expiresIn * 1000);\n const nowTs = Date.now();\n if (expiresAtTs < nowTs) {\n return (async ? TE.left : E.left)(\n new ExpiredError(authDate, new Date(expiresAtTs), new Date(nowTs)),\n );\n }\n }\n\n // According to docs, we sort all the pairs in alphabetical order.\n pairs.sort();\n\n const eitherSignature = signData(pairs.join('\\n'), token, options);\n const onLeft = (error: Left) => E.left(error);\n const onRight = (signature: string) => (\n signature === hash ? E.right(undefined) : E.left(new SignatureInvalidError())\n );\n\n return typeof eitherSignature === 'function'\n ? pipe(eitherSignature, TE.matchW(onLeft, onRight))\n : pipe(eitherSignature, E.matchW(onLeft, onRight));\n}\n","import {\n parseInitDataQuery,\n parseInitDataQueryFp,\n type ParseInitDataQueryError,\n} from '@tma.js/transformers';\n\n/**\n * Parses an incoming value as init data.\n */\nexport const parse = parseInitDataQuery;\n\n/**\n * Parses an incoming value as init data.\n */\nexport const parseFp = parseInitDataQueryFp;\n\nexport { ParseInitDataQueryError as ParseError };\n"],"names":["errorClassWithData","errorClass","E","pipe","TE","serializeInitDataQuery","value","BetterPromise","parseInitDataQuery","parseInitDataQueryFp"],"mappings":"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"}
@@ -0,0 +1,241 @@
1
+ import * as E from "fp-ts/Either";
2
+ import * as TE from "fp-ts/TaskEither";
3
+ import { pipe } from "fp-ts/lib/function.js";
4
+ import { serializeInitDataQuery, parseInitDataQuery, parseInitDataQueryFp } from "@tma.js/transformers";
5
+ import { pipe as pipe$1 } from "fp-ts/function";
6
+ import { BetterPromise } from "better-promises";
7
+ import { errorClassWithData, errorClass } from "error-kid";
8
+ class AuthDateInvalidError extends errorClassWithData(
9
+ "AuthDateInvalidError",
10
+ (value) => ({ value }),
11
+ (value) => [`"auth_date" is invalid: ${value || "value is missing"}`]
12
+ ) {
13
+ }
14
+ class SignatureInvalidError extends errorClass("SignatureInvalidError") {
15
+ }
16
+ class HexStringLengthInvalidError extends errorClass(
17
+ "HexStringLengthInvalidError"
18
+ ) {
19
+ }
20
+ class SignatureMissingError extends errorClass(
21
+ "SignatureMissingError",
22
+ (thirdParty) => [`"${thirdParty ? "signature" : "hash"}" parameter is missing`]
23
+ ) {
24
+ }
25
+ class ExpiredError extends errorClassWithData(
26
+ "ExpiredError",
27
+ (issuedAt, expiresAt) => ({ issuedAt, expiresAt }),
28
+ (issuedAt, expiresAt, now) => [
29
+ `Init data expired. Issued at ${issuedAt.toISOString()}, expires at ${expiresAt.toISOString()}, now is ${now.toISOString()}`
30
+ ]
31
+ ) {
32
+ }
33
+ function hexToArrayBuffer(hexString) {
34
+ if (hexString.length % 2 !== 0) {
35
+ return E.left(new HexStringLengthInvalidError());
36
+ }
37
+ const buffer = new ArrayBuffer(hexString.length / 2);
38
+ const uint8Array = new Uint8Array(buffer);
39
+ for (let i = 0; i < hexString.length; i += 2) {
40
+ uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
41
+ }
42
+ return E.right(buffer);
43
+ }
44
+ function arrayBufferToHex(arrBuf) {
45
+ return new Uint8Array(arrBuf).reduce((acc, byte) => {
46
+ return acc + byte.toString(16).padStart(2, "0");
47
+ }, "");
48
+ }
49
+ function bufferToArrayBuffer(buf) {
50
+ const ab = new ArrayBuffer(buf.length);
51
+ buf.copy(new Uint8Array(ab));
52
+ return ab;
53
+ }
54
+ function hashToken(token, createHmac) {
55
+ return createHmac(token, "WebAppData");
56
+ }
57
+ function signDataFp(async, data, key, createHmac, options = {}) {
58
+ const keyHmac = options.tokenHashed ? typeof key === "string" ? hexToArrayBuffer(key) : E.right(key) : pipe(
59
+ E.right(hashToken(key, createHmac)),
60
+ E.match(() => null, (v) => {
61
+ return v instanceof Promise ? TE.tryCatch(() => v, (err) => err) : E.right(v);
62
+ })
63
+ );
64
+ if (async || typeof keyHmac === "function") {
65
+ return pipe(
66
+ typeof keyHmac === "function" ? keyHmac : TE.fromEither(keyHmac),
67
+ TE.chain((v) => TE.tryCatch(
68
+ () => Promise.resolve(createHmac(data, v)).then(arrayBufferToHex),
69
+ (err) => err
70
+ ))
71
+ );
72
+ }
73
+ return pipe(
74
+ keyHmac,
75
+ // In this branch createHmac can't be asynchronous. If it is, keyHmac would be Promise and the
76
+ // result would be returned in the previous "if" statement.
77
+ E.chain((v) => E.right(
78
+ arrayBufferToHex(createHmac(data, v))
79
+ ))
80
+ );
81
+ }
82
+ function signFp(data, key, authDate, signData, options) {
83
+ const query = new URLSearchParams(serializeInitDataQuery({ ...data, auth_date: authDate }));
84
+ const pairs = [...query.entries()].map(([name, value]) => `${name}=${value}`).sort();
85
+ const queryWithHash = (signature) => {
86
+ query.append("hash", signature);
87
+ return query.toString();
88
+ };
89
+ const eitherHash = signData(pairs.join("\n"), key, options);
90
+ return typeof eitherHash === "function" ? pipe(eitherHash, TE.chain((hash) => TE.right(queryWithHash(hash)))) : pipe(eitherHash, E.chain((hash) => E.right(queryWithHash(hash))));
91
+ }
92
+ function validate3rdFp(value, botId, options = {}) {
93
+ let authDate;
94
+ let authDateString;
95
+ let signature;
96
+ const pairs = [];
97
+ (typeof value === "string" ? new URLSearchParams(value) : value).forEach((value2, key) => {
98
+ if (key === "hash") {
99
+ return;
100
+ }
101
+ if (key === "signature") {
102
+ signature = value2;
103
+ return;
104
+ }
105
+ if (key === "auth_date") {
106
+ authDateString = value2;
107
+ const authDateNum = parseInt(value2, 10);
108
+ if (!Number.isNaN(authDateNum)) {
109
+ authDate = new Date(authDateNum * 1e3);
110
+ }
111
+ }
112
+ pairs.push(`${key}=${value2}`);
113
+ });
114
+ if (!signature) {
115
+ return TE.left(new SignatureMissingError(true));
116
+ }
117
+ if (!authDate) {
118
+ return TE.left(new AuthDateInvalidError(authDateString));
119
+ }
120
+ const { expiresIn = 86400 } = options;
121
+ if (expiresIn > 0) {
122
+ const expiresAtTs = authDate.getTime() + expiresIn * 1e3;
123
+ const nowTs = Date.now();
124
+ if (expiresAtTs < nowTs) {
125
+ return TE.left(new ExpiredError(authDate, new Date(expiresAtTs), new Date(nowTs)));
126
+ }
127
+ }
128
+ return pipe$1(
129
+ TE.tryCatch(
130
+ () => {
131
+ return BetterPromise.fn(async () => {
132
+ return crypto.subtle.verify(
133
+ "Ed25519",
134
+ await crypto.subtle.importKey(
135
+ "raw",
136
+ Buffer.from(
137
+ options.test ? "40055058a4ee38156a06562e52eece92a771bcd8346a8c4615cb7376eddf72ec" : "e7bf03a2fa4602af4580703d88dda5bb59f32ed8b02a56c187fe7d34caed242d",
138
+ "hex"
139
+ ),
140
+ "Ed25519",
141
+ false,
142
+ ["verify"]
143
+ ),
144
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
145
+ Buffer.from(signature, "base64"),
146
+ Buffer.from(`${botId}:WebAppData
147
+ ${pairs.sort().join("\n")}`)
148
+ );
149
+ }, options);
150
+ },
151
+ (e) => e
152
+ ),
153
+ TE.chainW((isVerified) => {
154
+ return isVerified ? TE.right(void 0) : TE.left(new SignatureInvalidError());
155
+ })
156
+ );
157
+ }
158
+ function validate3rd(value, botId, options) {
159
+ return BetterPromise.fn(async () => {
160
+ await pipe$1(
161
+ validate3rdFp(value, botId, options),
162
+ TE.mapLeft((error) => {
163
+ throw error;
164
+ })
165
+ )();
166
+ });
167
+ }
168
+ function isValid3rdFp(value, botId, options) {
169
+ return pipe$1(validate3rdFp(value, botId, options), TE.match(
170
+ () => E.right(false),
171
+ () => E.right(true)
172
+ ));
173
+ }
174
+ function isValid3rd(value, botId, options) {
175
+ return BetterPromise.fn(() => pipe$1(
176
+ isValid3rdFp(value, botId, options),
177
+ TE.match(() => false, (v) => v)
178
+ )());
179
+ }
180
+ function validateFp(async, value, token, signData, options = {}) {
181
+ let authDate;
182
+ let authDateString;
183
+ let hash;
184
+ const pairs = [];
185
+ (typeof value === "string" ? new URLSearchParams(value) : value).forEach((value2, key) => {
186
+ if (key === "hash") {
187
+ hash = value2;
188
+ return;
189
+ }
190
+ if (key === "auth_date") {
191
+ authDateString = value2;
192
+ const authDateNum = parseInt(value2, 10);
193
+ if (!Number.isNaN(authDateNum)) {
194
+ authDate = new Date(authDateNum * 1e3);
195
+ }
196
+ }
197
+ pairs.push(`${key}=${value2}`);
198
+ });
199
+ if (!hash) {
200
+ return (async ? TE.left : E.left)(new SignatureMissingError(false));
201
+ }
202
+ if (!authDate) {
203
+ return (async ? TE.left : E.left)(new AuthDateInvalidError(authDateString));
204
+ }
205
+ const { expiresIn = 86400 } = options;
206
+ if (expiresIn > 0) {
207
+ const expiresAtTs = authDate.getTime() + expiresIn * 1e3;
208
+ const nowTs = Date.now();
209
+ if (expiresAtTs < nowTs) {
210
+ return (async ? TE.left : E.left)(
211
+ new ExpiredError(authDate, new Date(expiresAtTs), new Date(nowTs))
212
+ );
213
+ }
214
+ }
215
+ pairs.sort();
216
+ const eitherSignature = signData(pairs.join("\n"), token, options);
217
+ const onLeft = (error) => E.left(error);
218
+ const onRight = (signature) => signature === hash ? E.right(void 0) : E.left(new SignatureInvalidError());
219
+ return typeof eitherSignature === "function" ? pipe$1(eitherSignature, TE.matchW(onLeft, onRight)) : pipe$1(eitherSignature, E.matchW(onLeft, onRight));
220
+ }
221
+ const parse = parseInitDataQuery;
222
+ const parseFp = parseInitDataQueryFp;
223
+ export {
224
+ AuthDateInvalidError as A,
225
+ ExpiredError as E,
226
+ HexStringLengthInvalidError as H,
227
+ SignatureInvalidError as S,
228
+ signDataFp as a,
229
+ bufferToArrayBuffer as b,
230
+ SignatureMissingError as c,
231
+ parseFp as d,
232
+ isValid3rdFp as e,
233
+ validate3rd as f,
234
+ validate3rdFp as g,
235
+ hashToken as h,
236
+ isValid3rd as i,
237
+ parse as p,
238
+ signFp as s,
239
+ validateFp as v
240
+ };
241
+ //# sourceMappingURL=parsing-eczkSd-W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsing-eczkSd-W.js","sources":["../../src/errors.ts","../../src/buf-converters.ts","../../src/hashToken.ts","../../src/signDataFp.ts","../../src/signFp.ts","../../src/validation.ts","../../src/parsing.ts"],"sourcesContent":["import { errorClass, errorClassWithData } from 'error-kid';\n\nexport class AuthDateInvalidError extends errorClassWithData<\n { value: string | undefined },\n [value?: string]\n>(\n 'AuthDateInvalidError',\n value => ({ value }),\n value => [`\"auth_date\" is invalid: ${value || 'value is missing'}`],\n) {\n}\n\nexport class SignatureInvalidError extends errorClass('SignatureInvalidError') {\n}\n\nexport class HexStringLengthInvalidError extends errorClass(\n 'HexStringLengthInvalidError',\n) {\n}\n\nexport class SignatureMissingError extends errorClass<[thirdParty: boolean]>(\n 'SignatureMissingError',\n thirdParty => [`\"${thirdParty ? 'signature' : 'hash'}\" parameter is missing`],\n) {\n}\n\nexport class ExpiredError extends errorClassWithData<\n { issuedAt: Date; expiresAt: Date },\n [issuedAt: Date, expiresAt: Date, now: Date]\n>(\n 'ExpiredError',\n (issuedAt, expiresAt) => ({ issuedAt, expiresAt }),\n (issuedAt, expiresAt, now) => [\n `Init data expired. Issued at ${issuedAt.toISOString()}, expires at ${expiresAt.toISOString()}, now is ${now.toISOString()}`,\n ],\n) {\n}\n","import * as E from 'fp-ts/Either';\n\nimport { HexStringLengthInvalidError } from './errors.js';\n\n/**\n * Converts a hex string to ArrayBuffer.\n * @param hexString - value to convert.\n */\nexport function hexToArrayBuffer(hexString: string): E.Either<\n InstanceType<typeof HexStringLengthInvalidError>,\n ArrayBuffer\n> {\n if (hexString.length % 2 !== 0) {\n return E.left(new HexStringLengthInvalidError());\n }\n const buffer = new ArrayBuffer(hexString.length / 2);\n const uint8Array = new Uint8Array(buffer);\n for (let i = 0; i < hexString.length; i += 2) {\n uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);\n }\n return E.right(buffer);\n}\n\n/**\n * Converts array buffer to hex.\n * @param arrBuf - buffer to convert\n */\nexport function arrayBufferToHex(arrBuf: ArrayBuffer): string {\n return new Uint8Array(arrBuf).reduce((acc, byte) => {\n // Convert byte to hex and pad with zero if needed (e.g., \"0a\" instead of \"a\")\n return acc + byte.toString(16).padStart(2, '0');\n }, '');\n}\n\nexport function bufferToArrayBuffer(buf: Buffer) {\n const ab = new ArrayBuffer(buf.length);\n buf.copy(new Uint8Array(ab));\n return ab;\n}\n","import type { CreateHmacFn, Text } from './types.js';\n\nexport function hashToken<H extends CreateHmacFn<any>>(token: Text, createHmac: H): ReturnType<H> {\n return createHmac(token, 'WebAppData') as ReturnType<H>;\n}\n","import * as E from 'fp-ts/Either';\nimport * as TE from 'fp-ts/TaskEither';\nimport { pipe } from 'fp-ts/lib/function.js';\n\nimport { arrayBufferToHex, hexToArrayBuffer } from './buf-converters.js';\nimport { hashToken } from './hashToken.js';\nimport type { CreateHmacFn, Text } from './types.js';\n\nexport interface SignDataOptions {\n /**\n * True, if token is already hashed and doesn't require hashing using HMAC-SHA-256.\n */\n tokenHashed?: boolean;\n}\n\nexport type SignDataError = ReturnType<typeof hexToArrayBuffer> extends E.Either<infer U, any>\n ? U\n : never;\n\nexport function signDataFp(\n async: false,\n data: Text,\n key: Text,\n createHmac: CreateHmacFn<false>,\n options?: SignDataOptions,\n): E.Either<SignDataError, string>;\n\nexport function signDataFp(\n async: true,\n data: Text,\n key: Text,\n createHmac: CreateHmacFn<true>,\n options?: SignDataOptions,\n): TE.TaskEither<SignDataError, string>;\n\nexport function signDataFp(\n async: boolean,\n data: Text,\n key: Text,\n createHmac: CreateHmacFn<boolean>,\n options: SignDataOptions = {},\n):\n | E.Either<SignDataError, string>\n | TE.TaskEither<SignDataError, string> {\n const keyHmac = options.tokenHashed\n ? typeof key === 'string'\n // If a hashed token was passed, we assume that it is a HEX string. Not to mess with\n // the createHmac function, we should convert this HEX string to ArrayBuffer. Otherwise,\n // incorrect behavior will be met.\n ? hexToArrayBuffer(key)\n : E.right(key)\n // Otherwise we are hashing the token, but we want it to be a monad.\n : pipe(\n E.right(hashToken(key, createHmac)),\n E.match(() => null as never, v => {\n return v instanceof Promise\n ? TE.tryCatch(() => v, err => err as SignDataError)\n : E.right(v);\n }),\n );\n\n if (async || typeof keyHmac === 'function') {\n return pipe(\n typeof keyHmac === 'function' ? keyHmac : TE.fromEither(keyHmac),\n TE.chain(v => TE.tryCatch(\n () => Promise.resolve(createHmac(data, v)).then(arrayBufferToHex),\n err => err as SignDataError,\n )),\n );\n }\n return pipe(\n keyHmac,\n // In this branch createHmac can't be asynchronous. If it is, keyHmac would be Promise and the\n // result would be returned in the previous \"if\" statement.\n E.chain(v => E.right(\n arrayBufferToHex((createHmac as CreateHmacFn<false>)(data, v)),\n )),\n );\n}\n","import { type InitDataLike, serializeInitDataQuery } from '@tma.js/transformers';\nimport * as E from 'fp-ts/Either';\nimport * as TE from 'fp-ts/TaskEither';\nimport { pipe } from 'fp-ts/lib/function.js';\n\nimport type { Text } from './types.js';\n\nexport type SignableData = Omit<InitDataLike, 'auth_date' | 'hash' | 'signature'>;\n\nexport interface SignOptions {\n /**\n * True, if token is already hashed and doesn't require hashing using HMAC-SHA-256.\n */\n tokenHashed?: boolean;\n}\n\ninterface SignDataFpArg<Async extends boolean, Left> {\n (data: Text, key: Text, options?: SignOptions): Async extends true\n ? TE.TaskEither<Left, string>\n : E.Either<Left, string>;\n}\n\n/**\n * Signs specified init data.\n * @param data - init data to sign.\n * @param authDate - date, when this init data should be signed.\n * @param key - private key.\n * @param signData - function signing data.\n * @param options - additional options.\n * @returns Signed init data presented as query parameters.\n */\nexport function signFp<Left>(\n data: SignableData,\n key: Text,\n authDate: Date,\n signData: SignDataFpArg<false, Left>,\n options?: SignOptions,\n): E.Either<Left, string>;\n\n/**\n * Signs specified init data.\n * @param data - init data to sign.\n * @param authDate - date, when this init data should be signed.\n * @param key - private key.\n * @param signData - function signing data.\n * @param options - additional options.\n * @returns Signed init data presented as query parameters.\n */\nexport function signFp<Left>(\n data: SignableData,\n key: Text,\n authDate: Date,\n signData: SignDataFpArg<true, Left>,\n options?: SignOptions,\n): TE.TaskEither<Left, string>;\n\nexport function signFp<Left>(\n data: SignableData,\n key: Text,\n authDate: Date,\n signData: SignDataFpArg<boolean, Left>,\n options?: SignOptions,\n): E.Either<Left, string> | TE.TaskEither<Left, string> {\n const query = new URLSearchParams(serializeInitDataQuery({ ...data, auth_date: authDate }));\n\n // Convert search params to pairs and sort the final array.\n const pairs = [...query.entries()]\n .map(([name, value]) => `${name}=${value}`)\n .sort();\n\n // Compute sign, append it to the params and return.\n const queryWithHash = (signature: string): string => {\n query.append('hash', signature);\n return query.toString();\n };\n\n const eitherHash = signData(pairs.join('\\n'), key, options);\n return typeof eitherHash === 'function'\n ? pipe(eitherHash, TE.chain(hash => TE.right(queryWithHash(hash))))\n : pipe(eitherHash, E.chain(hash => E.right(queryWithHash(hash))));\n}\n","import * as E from 'fp-ts/Either';\nimport * as TE from 'fp-ts/TaskEither';\nimport { pipe } from 'fp-ts/function';\nimport {\n BetterPromise,\n type BetterPromiseOptions,\n type BetterPromiseRejectReason,\n} from 'better-promises';\n\nimport {\n AuthDateInvalidError,\n ExpiredError,\n SignatureInvalidError,\n SignatureMissingError,\n} from './errors.js';\nimport { Text } from './types.js';\n\ntype OmittedPromiseOptions = Omit<BetterPromiseOptions, 'abortOnResolve' | 'abortOnReject'>;\n\nexport type ValidateValue = string | URLSearchParams;\nexport type Validate3rdValue = string | URLSearchParams;\n\nexport type ValidateError =\n | SignatureMissingError\n | SignatureInvalidError\n | AuthDateInvalidError\n | ExpiredError;\nexport type ValidateAsyncError = ValidateError | BetterPromiseRejectReason;\nexport type Validate3rdError =\n | SignatureMissingError\n | SignatureInvalidError\n | AuthDateInvalidError\n | ExpiredError\n | BetterPromiseRejectReason;\n\ninterface ValidateSignDataFpArg<Async extends boolean, Left> {\n (data: Text, key: Text, options?: ValidateOptions): Async extends true\n ? TE.TaskEither<Left, string>\n : E.Either<Left, string>;\n}\n\ninterface SharedValidateOptions {\n /**\n * Time in seconds which states, how long from creation time init data is considered valid.\n *\n * In other words, in case when authDate + expiresIn is before current time, init data is\n * recognized as expired.\n *\n * In case this value is equal to 0, the function does not check init data expiration.\n * @default 86400 (1 day)\n */\n expiresIn?: number;\n}\n\nexport interface ValidateOptions extends SharedValidateOptions {\n /**\n * True, if token is already hashed.\n * @default false\n */\n tokenHashed?: boolean;\n}\n\nexport interface ValidateAsyncOptions extends ValidateOptions, OmittedPromiseOptions {\n}\n\nexport interface Validate3rdOptions extends SharedValidateOptions, OmittedPromiseOptions {\n /**\n * When true, uses the test environment public key to validate init data.\n * @default false\n */\n test?: boolean;\n}\n\n/**\n * Validates passed init data using a publicly known Ee25519 key.\n * @param value - value to check.\n * @param botId - bot identifier\n * @param options - additional validation options.\n */\nexport function validate3rdFp(\n value: Validate3rdValue,\n botId: number,\n options: Validate3rdOptions = {},\n): TE.TaskEither<Validate3rdError, void> {\n // Init data required params.\n let authDate: Date | undefined;\n let authDateString: string | undefined;\n let signature: string | undefined;\n\n // All search params pairs presented as `k=v`.\n const pairs: string[] = [];\n\n // Iterate over all key-value pairs of parsed parameters and find required\n // parameters.\n (typeof value === 'string' ? new URLSearchParams(value) : value).forEach((value, key) => {\n if (key === 'hash') {\n return;\n }\n if (key === 'signature') {\n signature = value;\n return;\n }\n if (key === 'auth_date') {\n authDateString = value;\n const authDateNum = parseInt(value, 10);\n if (!Number.isNaN(authDateNum)) {\n authDate = new Date(authDateNum * 1000);\n }\n }\n\n pairs.push(`${key}=${value}`);\n });\n\n // Signature and auth date always required.\n if (!signature) {\n return TE.left(new SignatureMissingError(true));\n }\n\n if (!authDate) {\n return TE.left(new AuthDateInvalidError(authDateString));\n }\n\n // In case, expiration time passed, we do additional parameters check.\n const { expiresIn = 86400 } = options;\n if (expiresIn > 0) {\n // Check if init data expired.\n const expiresAtTs = authDate.getTime() + (expiresIn * 1000);\n const nowTs = Date.now();\n if (expiresAtTs < nowTs) {\n return TE.left(new ExpiredError(authDate, new Date(expiresAtTs), new Date(nowTs)));\n }\n }\n\n return pipe(\n TE.tryCatch(\n () => {\n return BetterPromise.fn(async () => {\n return crypto.subtle.verify(\n 'Ed25519',\n await crypto.subtle.importKey(\n 'raw',\n Buffer.from(\n options.test\n ? '40055058a4ee38156a06562e52eece92a771bcd8346a8c4615cb7376eddf72ec'\n : 'e7bf03a2fa4602af4580703d88dda5bb59f32ed8b02a56c187fe7d34caed242d',\n 'hex',\n ),\n 'Ed25519',\n false,\n ['verify'],\n ),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n Buffer.from(signature as string, 'base64'),\n Buffer.from(`${botId}:WebAppData\\n${pairs.sort().join('\\n')}`),\n );\n }, options);\n },\n (e: unknown) => e,\n ),\n TE.chainW(isVerified => {\n return isVerified ? TE.right(undefined) : TE.left(new SignatureInvalidError());\n }),\n );\n}\n\n/**\n * @see validate3rdFp\n */\nexport function validate3rd(\n value: Validate3rdValue,\n botId: number,\n options?: Validate3rdOptions,\n): BetterPromise<void> {\n return BetterPromise.fn(async () => {\n await pipe(\n validate3rdFp(value, botId, options),\n TE.mapLeft(error => {\n throw error;\n }),\n )();\n });\n}\n\n/**\n * @param value - value to check.\n * @param botId - bot identifier\n * @param options - additional validation options.\n * @returns True is specified init data is signed by Telegram.\n */\nexport function isValid3rdFp(\n value: Validate3rdValue,\n botId: number,\n options?: Validate3rdOptions,\n): TE.TaskEither<void, boolean> {\n return pipe(validate3rdFp(value, botId, options), TE.match(\n () => E.right(false),\n () => E.right(true),\n ));\n}\n\n/**\n * @see isValid3rdFp\n */\nexport function isValid3rd(\n value: Validate3rdValue,\n botId: number,\n options?: Validate3rdOptions,\n): BetterPromise<boolean> {\n return BetterPromise.fn(() => pipe(\n isValid3rdFp(value, botId, options),\n TE.match(() => false, v => v),\n )());\n}\n\nexport function validateFp<Left>(\n async: false,\n value: ValidateValue,\n token: Text,\n signData: ValidateSignDataFpArg<false, Left>,\n options?: ValidateOptions,\n): E.Either<Left | ValidateError, void>;\n\nexport function validateFp<Left>(\n async: true,\n value: ValidateValue,\n token: Text,\n signData: ValidateSignDataFpArg<true, Left>,\n options?: ValidateAsyncOptions,\n): TE.TaskEither<Left | ValidateAsyncError, void>;\n\nexport function validateFp<Left>(\n async: boolean,\n value: ValidateValue,\n token: Text,\n signData: ValidateSignDataFpArg<boolean, Left>,\n options: ValidateOptions | ValidateAsyncOptions = {},\n):\n | E.Either<Left | ValidateError, void>\n | TE.TaskEither<Left | ValidateAsyncError, void> {\n // Init data required params.\n let authDate: Date | undefined;\n let authDateString: string | undefined;\n let hash: string | undefined;\n\n // All search params pairs presented as `k=v`.\n const pairs: string[] = [];\n\n // Iterate over all key-value pairs of parsed parameters and find required\n // parameters.\n (typeof value === 'string' ? new URLSearchParams(value) : value).forEach((value, key) => {\n if (key === 'hash') {\n hash = value;\n return;\n }\n\n if (key === 'auth_date') {\n authDateString = value;\n const authDateNum = parseInt(value, 10);\n if (!Number.isNaN(authDateNum)) {\n authDate = new Date(authDateNum * 1000);\n }\n }\n\n pairs.push(`${key}=${value}`);\n });\n\n // Hash and auth date always required.\n if (!hash) {\n return (async ? TE.left : E.left)(new SignatureMissingError(false));\n }\n\n if (!authDate) {\n return (async ? TE.left : E.left)(new AuthDateInvalidError(authDateString));\n }\n\n // In case, expiration time passed, we do additional parameters check.\n const { expiresIn = 86400 } = options;\n if (expiresIn > 0) {\n // Check if init data expired.\n const expiresAtTs = authDate.getTime() + (expiresIn * 1000);\n const nowTs = Date.now();\n if (expiresAtTs < nowTs) {\n return (async ? TE.left : E.left)(\n new ExpiredError(authDate, new Date(expiresAtTs), new Date(nowTs)),\n );\n }\n }\n\n // According to docs, we sort all the pairs in alphabetical order.\n pairs.sort();\n\n const eitherSignature = signData(pairs.join('\\n'), token, options);\n const onLeft = (error: Left) => E.left(error);\n const onRight = (signature: string) => (\n signature === hash ? E.right(undefined) : E.left(new SignatureInvalidError())\n );\n\n return typeof eitherSignature === 'function'\n ? pipe(eitherSignature, TE.matchW(onLeft, onRight))\n : pipe(eitherSignature, E.matchW(onLeft, onRight));\n}\n","import {\n parseInitDataQuery,\n parseInitDataQueryFp,\n type ParseInitDataQueryError,\n} from '@tma.js/transformers';\n\n/**\n * Parses an incoming value as init data.\n */\nexport const parse = parseInitDataQuery;\n\n/**\n * Parses an incoming value as init data.\n */\nexport const parseFp = parseInitDataQueryFp;\n\nexport { ParseInitDataQueryError as ParseError };\n"],"names":["value","pipe"],"mappings":"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"}
@@ -1,4 +1,6 @@
1
- export { type Chat, type ChatType, type InitDataParsed, parseInitData as parse, type User, } from '@tma.js/sdk';
2
- export { initDataToSearchParams } from '../initDataToSearchParams.js';
3
- export type { ValidateOptions } from '../validate.js';
4
- export type { SignData, Text, CreateHmacFn } from '../types.js';
1
+ export { AuthDateInvalidError, HexStringLengthInvalidError, SignatureInvalidError, SignatureMissingError, ExpiredError, } from '../errors.js';
2
+ export { parse, parseFp, type ParseError } from '../parsing.js';
3
+ export type { Text, CreateHmacFn } from '../types.js';
4
+ export { isValid3rd, isValid3rdFp, validate3rd, validate3rdFp, type Validate3rdError, type Validate3rdOptions, type Validate3rdValue, type ValidateError, type ValidateOptions, type ValidateValue, type ValidateAsyncOptions, type ValidateAsyncError, } from '../validation.js';
5
+ export type { Chat, ChatType, InitData, User } from '@tma.js/types';
6
+ export { deepSnakeToCamelObjKeys } from '@tma.js/toolkit';
@@ -1,38 +1,134 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const index = require("./index-DQxFLF1J.cjs");
3
+ const betterPromises = require("better-promises");
4
+ const E = require("fp-ts/Either");
5
+ const TE = require("fp-ts/TaskEither");
6
+ const function_js = require("fp-ts/lib/function.js");
7
+ const parsing = require("./parsing-Cj_BXCnv.cjs");
8
+ const toolkit = require("@tma.js/toolkit");
9
+ function _interopNamespaceDefault(e) {
10
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
11
+ if (e) {
12
+ for (const k in e) {
13
+ if (k !== "default") {
14
+ const d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: () => e[k]
18
+ });
19
+ }
20
+ }
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+ const E__namespace = /* @__PURE__ */ _interopNamespaceDefault(E);
26
+ const TE__namespace = /* @__PURE__ */ _interopNamespaceDefault(TE);
4
27
  const createHmac = async (data, key) => {
5
28
  const encoder = new TextEncoder();
6
- return Buffer.from(
7
- await crypto.subtle.sign(
8
- "HMAC",
9
- await crypto.subtle.importKey(
10
- "raw",
11
- typeof key === "string" ? encoder.encode(key) : key,
12
- { name: "HMAC", hash: "SHA-256" },
13
- false,
14
- ["sign", "verify"]
15
- ),
16
- encoder.encode(data.toString())
17
- )
29
+ return crypto.subtle.sign(
30
+ "HMAC",
31
+ await crypto.subtle.importKey(
32
+ "raw",
33
+ typeof key === "string" ? encoder.encode(key) : key,
34
+ { name: "HMAC", hash: "SHA-256" },
35
+ false,
36
+ ["sign", "verify"]
37
+ ),
38
+ typeof data === "string" ? encoder.encode(data) : data
18
39
  );
19
40
  };
20
41
  function hashToken(token) {
21
- return index.hashToken(token, createHmac);
42
+ return parsing.hashToken(token, createHmac);
43
+ }
44
+ function isValidFp(value, token, options) {
45
+ return function_js.pipe(
46
+ validateFp(value, token, options),
47
+ TE__namespace.match(
48
+ (error) => {
49
+ return [
50
+ parsing.AuthDateInvalidError,
51
+ parsing.ExpiredError,
52
+ parsing.SignatureInvalidError,
53
+ parsing.SignatureMissingError,
54
+ parsing.HexStringLengthInvalidError
55
+ ].some((errorClass) => errorClass.is(error)) ? E__namespace.right(false) : E__namespace.left(error);
56
+ },
57
+ () => E__namespace.right(true)
58
+ )
59
+ );
60
+ }
61
+ function isValid(value, token, options) {
62
+ return betterPromises.BetterPromise.fn(() => {
63
+ return function_js.pipe(
64
+ isValidFp(value, token, options),
65
+ TE__namespace.match((error) => {
66
+ throw error;
67
+ }, (isValid2) => isValid2)
68
+ )();
69
+ });
70
+ }
71
+ function signFp(data, key, authDate, options) {
72
+ return parsing.signFp(data, key, authDate, signDataFp, options);
22
73
  }
23
74
  function sign(data, key, authDate, options) {
24
- return index.sign(data, key, authDate, signData, options);
75
+ return betterPromises.BetterPromise.fn(() => {
76
+ return function_js.pipe(
77
+ signFp(data, key, authDate, options),
78
+ TE__namespace.match((e) => {
79
+ throw e;
80
+ }, (v) => v)
81
+ )();
82
+ });
83
+ }
84
+ function signDataFp(data, key, options) {
85
+ return parsing.signDataFp(true, data, key, createHmac, options);
86
+ }
87
+ function signData(data, key, options) {
88
+ return betterPromises.BetterPromise.fn(() => {
89
+ return function_js.pipe(
90
+ signDataFp(data, key, options),
91
+ TE__namespace.match((e) => {
92
+ throw e;
93
+ }, (v) => v)
94
+ )();
95
+ });
25
96
  }
26
- async function signData(data, key, options) {
27
- return index.signData(data, key, createHmac, options);
97
+ function validateFp(value, token, options) {
98
+ return parsing.validateFp(true, value, token, signDataFp, options);
28
99
  }
29
- async function validate(value, token, options) {
30
- return index.validate(value, token, signData, options);
100
+ function validate(value, token, options) {
101
+ return betterPromises.BetterPromise.fn(async () => {
102
+ await function_js.pipe(
103
+ validateFp(value, token, options),
104
+ TE__namespace.mapLeft((error) => {
105
+ throw error;
106
+ })
107
+ )();
108
+ });
31
109
  }
32
- exports.initDataToSearchParams = index.initDataToSearchParams;
33
- exports.parse = index.cs;
110
+ exports.AuthDateInvalidError = parsing.AuthDateInvalidError;
111
+ exports.ExpiredError = parsing.ExpiredError;
112
+ exports.HexStringLengthInvalidError = parsing.HexStringLengthInvalidError;
113
+ exports.SignatureInvalidError = parsing.SignatureInvalidError;
114
+ exports.SignatureMissingError = parsing.SignatureMissingError;
115
+ exports.isValid3rd = parsing.isValid3rd;
116
+ exports.isValid3rdFp = parsing.isValid3rdFp;
117
+ exports.parse = parsing.parse;
118
+ exports.parseFp = parsing.parseFp;
119
+ exports.validate3rd = parsing.validate3rd;
120
+ exports.validate3rdFp = parsing.validate3rdFp;
121
+ Object.defineProperty(exports, "deepSnakeToCamelObjKeys", {
122
+ enumerable: true,
123
+ get: () => toolkit.deepSnakeToCamelObjKeys
124
+ });
34
125
  exports.hashToken = hashToken;
126
+ exports.isValid = isValid;
127
+ exports.isValidFp = isValidFp;
35
128
  exports.sign = sign;
36
129
  exports.signData = signData;
130
+ exports.signDataFp = signDataFp;
131
+ exports.signFp = signFp;
37
132
  exports.validate = validate;
133
+ exports.validateFp = validateFp;
38
134
  //# sourceMappingURL=web.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"web.cjs","sources":["../../src/entries/web.ts"],"sourcesContent":["import { InitData, InitDataParsed } from '@tma.js/sdk';\n\nimport { hashToken as baseHashToken } from '../hashToken.js';\nimport { sign as baseSign, SignOptions } from '../sign.js';\nimport { signData as baseSignData, SignDataOptions } from '../signData.js';\nimport { validate as baseValidate } from '../validate.js';\nimport type{ ValidateOptions } from '../validate.js';\nimport type { CreateHmacFn, SignData, Text } from '../types.js';\n\nconst createHmac: CreateHmacFn<true> = async (data, key) => {\n const encoder = new TextEncoder();\n\n return Buffer.from(\n await crypto.subtle.sign(\n 'HMAC',\n await crypto.subtle.importKey(\n 'raw',\n typeof key === 'string' ? encoder.encode(key) : key,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n ),\n encoder.encode(data.toString()),\n ),\n );\n};\n\n\n/**\n * Hashes specified token using a string, expected during init data sign.\n * @param token - token to hash.\n */\nexport function hashToken(token: Text): Promise<Buffer> {\n return baseHashToken(token, createHmac);\n}\n\n/**\n * Signs specified init data.\n * @param data - init data to sign.\n * @param authDate - date, when this init data should be signed.\n * @param key - private key.\n * @param options - additional options.\n * @returns Signed init data presented as query parameters.\n */\nexport function sign(\n data: SignData,\n key: Text,\n authDate: Date,\n options?: SignOptions\n): Promise<string> {\n return baseSign(data, key, authDate, signData, options);\n}\n\n/**\n * Signs specified data with the passed token.\n * @param data - data to sign.\n * @param key - private key.\n * @param options - additional options.\n * @returns Data sign.\n */\nexport async function signData(data: Text, key: Text, options?: SignDataOptions): Promise<string> {\n return baseSignData(data, key, createHmac, options);\n}\n\n/**\n * Validates passed init data.\n * @param value - value to check.\n * @param token - bot secret token.\n * @param options - additional validation options.\n * @throws {TypeError} \"auth_date\" should present integer\n * @throws {Error} \"hash\" is empty or not found\n * @throws {Error} \"auth_date\" is empty or not found\n * @throws {Error} Init data expired\n */\nexport async function validate(\n value: InitData | InitDataParsed | string | URLSearchParams,\n token: Text,\n options?: ValidateOptions,\n): Promise<void> {\n return baseValidate(value, token, signData, options);\n}\n\nexport * from './shared.js';\n"],"names":["baseHashToken","baseSign","baseSignData","baseValidate"],"mappings":";;;AASA,MAAM,aAAiC,OAAO,MAAM,QAAQ;AACpD,QAAA,UAAU,IAAI;AAEpB,SAAO,OAAO;AAAA,IACZ,MAAM,OAAO,OAAO;AAAA,MAClB;AAAA,MACA,MAAM,OAAO,OAAO;AAAA,QAClB;AAAA,QACA,OAAO,QAAQ,WAAW,QAAQ,OAAO,GAAG,IAAI;AAAA,QAChD,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,QAChC;AAAA,QACA,CAAC,QAAQ,QAAQ;AAAA,MACnB;AAAA,MACA,QAAQ,OAAO,KAAK,UAAU;AAAA,IAChC;AAAA,EAAA;AAEJ;AAOO,SAAS,UAAU,OAA8B;AAC/C,SAAAA,MAAA,UAAc,OAAO,UAAU;AACxC;AAUO,SAAS,KACd,MACA,KACA,UACA,SACiB;AACjB,SAAOC,MAAAA,KAAS,MAAM,KAAK,UAAU,UAAU,OAAO;AACxD;AASsB,eAAA,SAAS,MAAY,KAAW,SAA4C;AAChG,SAAOC,MAAa,SAAA,MAAM,KAAK,YAAY,OAAO;AACpD;AAYsB,eAAA,SACpB,OACA,OACA,SACe;AACf,SAAOC,MAAa,SAAA,OAAO,OAAO,UAAU,OAAO;AACrD;;;;;;;"}
1
+ {"version":3,"file":"web.cjs","sources":["../../src/entries/web.ts"],"sourcesContent":["import { BetterPromise, type BetterPromiseRejectReason } from 'better-promises';\nimport * as E from 'fp-ts/Either';\nimport * as TE from 'fp-ts/TaskEither';\nimport { pipe } from 'fp-ts/lib/function.js';\n\nimport { hashToken as _hashToken } from '../hashToken.js';\nimport { signDataFp as _signDataFp, SignDataError, type SignDataOptions } from '../signDataFp.js';\nimport {\n signFp as _signFp,\n type SignableData,\n type SignOptions,\n} from '../signFp.js';\nimport type { CreateHmacFn, Text } from '../types.js';\nimport {\n validateFp as _validateFp,\n type ValidateValue,\n type ValidateAsyncError,\n type ValidateAsyncOptions,\n} from '../validation.js';\nimport {\n AuthDateInvalidError,\n ExpiredError,\n SignatureInvalidError,\n SignatureMissingError,\n HexStringLengthInvalidError,\n} from '../errors.js';\n\nconst createHmac: CreateHmacFn<true> = async (data, key) => {\n const encoder = new TextEncoder();\n\n return crypto.subtle.sign(\n 'HMAC',\n await crypto.subtle.importKey(\n 'raw',\n typeof key === 'string' ? encoder.encode(key) : key,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n ),\n typeof data === 'string' ? encoder.encode(data) : data,\n );\n};\n\n/**\n * Hashes specified token using a string, expected during init data sign.\n * @param token - token to hash.\n */\nexport function hashToken(token: Text): Promise<ArrayBuffer> {\n return _hashToken(token, createHmac);\n}\n\n/**\n * @param value - value to check.\n * @param token - bot secret token.\n * @param options - additional validation options.\n * @returns True is specified init data is valid.\n */\nexport function isValidFp(\n value: ValidateValue,\n token: Text,\n options?: ValidateAsyncOptions,\n): TE.TaskEither<BetterPromiseRejectReason, boolean> {\n return pipe(\n validateFp(value, token, options),\n TE.match(\n error => {\n return [\n AuthDateInvalidError,\n ExpiredError,\n SignatureInvalidError,\n SignatureMissingError,\n HexStringLengthInvalidError,\n ].some(errorClass => errorClass.is(error))\n ? E.right(false)\n : E.left(error);\n },\n () => E.right(true),\n ),\n );\n}\n\n/**\n * @see isValidFp\n */\nexport function isValid(\n value: ValidateValue,\n token: Text,\n options?: ValidateAsyncOptions,\n): BetterPromise<boolean> {\n return BetterPromise.fn(() => {\n return pipe(\n isValidFp(value, token, options),\n TE.match(error => {\n throw error;\n }, isValid => isValid),\n )();\n });\n}\n\n/**\n * Signs specified init data.\n * @param data - init data to sign.\n * @param authDate - date, when this init data should be signed.\n * @param key - private key.\n * @param options - additional options.\n * @returns Signed init data presented as query parameters.\n */\nexport function signFp(\n data: SignableData,\n key: Text,\n authDate: Date,\n options?: SignOptions,\n): TE.TaskEither<SignDataError, string> {\n return _signFp(data, key, authDate, signDataFp, options);\n}\n\n/**\n * @see signFp\n */\nexport function sign(\n data: SignableData,\n key: Text,\n authDate: Date,\n options?: SignOptions,\n): BetterPromise<string> {\n return BetterPromise.fn(() => {\n return pipe(\n signFp(data, key, authDate, options),\n TE.match(e => {\n throw e;\n }, v => v),\n )();\n });\n}\n\n/**\n * Signs specified data with the passed token.\n * @param data - data to sign.\n * @param key - private key.\n * @param options - additional options.\n * @returns Data sign.\n */\nexport function signDataFp(\n data: Text,\n key: Text,\n options?: SignDataOptions,\n): TE.TaskEither<SignDataError, string> {\n return _signDataFp(true, data, key, createHmac, options);\n}\n\n/**\n * @see signDataFp\n */\nexport function signData(data: Text, key: Text, options?: SignDataOptions): BetterPromise<string> {\n return BetterPromise.fn(() => {\n return pipe(\n signDataFp(data, key, options),\n TE.match(e => {\n throw e;\n }, v => v),\n )();\n });\n}\n\n/**\n * Validates passed init data.\n * @param value - value to check.\n * @param token - bot secret token.\n * @param options - additional validation options.\n */\nexport function validateFp(\n value: ValidateValue,\n token: Text,\n options?: ValidateAsyncOptions,\n): TE.TaskEither<ValidateAsyncError, void> {\n return _validateFp(true, value, token, signDataFp, options);\n}\n\n/**\n * @see validateFp\n */\nexport function validate(\n value: ValidateValue,\n token: Text,\n options?: ValidateAsyncOptions,\n): BetterPromise<void> {\n return BetterPromise.fn(async () => {\n await pipe(\n validateFp(value, token, options),\n TE.mapLeft(error => {\n throw error;\n }),\n )();\n });\n}\n\nexport * from './shared.js';\n"],"names":["_hashToken","pipe","TE","AuthDateInvalidError","ExpiredError","SignatureInvalidError","SignatureMissingError","HexStringLengthInvalidError","E","BetterPromise","isValid","_signFp","_signDataFp","_validateFp"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAM,aAAiC,OAAO,MAAM,QAAQ;AACpD,QAAA,UAAU,IAAI,YAAY;AAEhC,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,MAAM,OAAO,OAAO;AAAA,MAClB;AAAA,MACA,OAAO,QAAQ,WAAW,QAAQ,OAAO,GAAG,IAAI;AAAA,MAChD,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,MAChC;AAAA,MACA,CAAC,QAAQ,QAAQ;AAAA,IACnB;AAAA,IACA,OAAO,SAAS,WAAW,QAAQ,OAAO,IAAI,IAAI;AAAA,EACpD;AACF;AAMO,SAAS,UAAU,OAAmC;AACpD,SAAAA,QAAA,UAAW,OAAO,UAAU;AACrC;AAQgB,SAAA,UACd,OACA,OACA,SACmD;AAC5C,SAAAC,YAAA;AAAA,IACL,WAAW,OAAO,OAAO,OAAO;AAAA,IAChCC,cAAG;AAAA,MACD,CAAS,UAAA;AACA,eAAA;AAAA,UACLC,QAAA;AAAA,UACAC,QAAA;AAAA,UACAC,QAAA;AAAA,UACAC,QAAA;AAAA,UACAC,QAAAA;AAAAA,QACA,EAAA,KAAK,CAAc,eAAA,WAAW,GAAG,KAAK,CAAC,IACrCC,aAAE,MAAM,KAAK,IACbA,aAAE,KAAK,KAAK;AAAA,MAClB;AAAA,MACA,MAAMA,aAAE,MAAM,IAAI;AAAA,IAAA;AAAA,EAEtB;AACF;AAKgB,SAAA,QACd,OACA,OACA,SACwB;AACjB,SAAAC,eAAAA,cAAc,GAAG,MAAM;AACrB,WAAAR,YAAA;AAAA,MACL,UAAU,OAAO,OAAO,OAAO;AAAA,MAC/BC,cAAG,MAAM,CAAS,UAAA;AACV,cAAA;AAAA,MAAA,GACL,CAAAQ,aAAWA,QAAO;AAAA,IAAA,EACrB;AAAA,EAAA,CACH;AACH;AAUO,SAAS,OACd,MACA,KACA,UACA,SACsC;AACtC,SAAOC,QAAAA,OAAQ,MAAM,KAAK,UAAU,YAAY,OAAO;AACzD;AAKO,SAAS,KACd,MACA,KACA,UACA,SACuB;AAChB,SAAAF,eAAAA,cAAc,GAAG,MAAM;AACrB,WAAAR,YAAA;AAAA,MACL,OAAO,MAAM,KAAK,UAAU,OAAO;AAAA,MACnCC,cAAG,MAAM,CAAK,MAAA;AACN,cAAA;AAAA,MAAA,GACL,OAAK,CAAC;AAAA,IAAA,EACT;AAAA,EAAA,CACH;AACH;AASgB,SAAA,WACd,MACA,KACA,SACsC;AACtC,SAAOU,QAAAA,WAAY,MAAM,MAAM,KAAK,YAAY,OAAO;AACzD;AAKgB,SAAA,SAAS,MAAY,KAAW,SAAkD;AACzF,SAAAH,eAAAA,cAAc,GAAG,MAAM;AACrB,WAAAR,YAAA;AAAA,MACL,WAAW,MAAM,KAAK,OAAO;AAAA,MAC7BC,cAAG,MAAM,CAAK,MAAA;AACN,cAAA;AAAA,MAAA,GACL,OAAK,CAAC;AAAA,IAAA,EACT;AAAA,EAAA,CACH;AACH;AAQgB,SAAA,WACd,OACA,OACA,SACyC;AACzC,SAAOW,QAAAA,WAAY,MAAM,OAAO,OAAO,YAAY,OAAO;AAC5D;AAKgB,SAAA,SACd,OACA,OACA,SACqB;AACd,SAAAJ,eAAAA,cAAc,GAAG,YAAY;AAC5B,UAAAR,YAAA;AAAA,MACJ,WAAW,OAAO,OAAO,OAAO;AAAA,MAChCC,cAAG,QAAQ,CAAS,UAAA;AACZ,cAAA;AAAA,MACP,CAAA;AAAA,IAAA,EACD;AAAA,EAAA,CACH;AACH;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,14 +1,25 @@
1
- import { InitData, InitDataParsed } from '@tma.js/sdk';
2
- import { SignOptions } from '../sign.js';
3
- import { SignDataOptions } from '../signData.js';
4
- import { ValidateOptions } from '../validate.js';
5
- import { SignData, Text } from '../types.js';
6
-
1
+ import { BetterPromise, BetterPromiseRejectReason } from 'better-promises';
2
+ import { SignDataError, SignDataOptions } from '../signDataFp.js';
3
+ import { SignableData, SignOptions } from '../signFp.js';
4
+ import { Text } from '../types.js';
5
+ import { ValidateValue, ValidateAsyncError, ValidateAsyncOptions } from '../validation.js';
6
+ import * as TE from 'fp-ts/TaskEither';
7
7
  /**
8
8
  * Hashes specified token using a string, expected during init data sign.
9
9
  * @param token - token to hash.
10
10
  */
11
- export declare function hashToken(token: Text): Promise<Buffer>;
11
+ export declare function hashToken(token: Text): Promise<ArrayBuffer>;
12
+ /**
13
+ * @param value - value to check.
14
+ * @param token - bot secret token.
15
+ * @param options - additional validation options.
16
+ * @returns True is specified init data is valid.
17
+ */
18
+ export declare function isValidFp(value: ValidateValue, token: Text, options?: ValidateAsyncOptions): TE.TaskEither<BetterPromiseRejectReason, boolean>;
19
+ /**
20
+ * @see isValidFp
21
+ */
22
+ export declare function isValid(value: ValidateValue, token: Text, options?: ValidateAsyncOptions): BetterPromise<boolean>;
12
23
  /**
13
24
  * Signs specified init data.
14
25
  * @param data - init data to sign.
@@ -17,7 +28,11 @@ export declare function hashToken(token: Text): Promise<Buffer>;
17
28
  * @param options - additional options.
18
29
  * @returns Signed init data presented as query parameters.
19
30
  */
20
- export declare function sign(data: SignData, key: Text, authDate: Date, options?: SignOptions): Promise<string>;
31
+ export declare function signFp(data: SignableData, key: Text, authDate: Date, options?: SignOptions): TE.TaskEither<SignDataError, string>;
32
+ /**
33
+ * @see signFp
34
+ */
35
+ export declare function sign(data: SignableData, key: Text, authDate: Date, options?: SignOptions): BetterPromise<string>;
21
36
  /**
22
37
  * Signs specified data with the passed token.
23
38
  * @param data - data to sign.
@@ -25,16 +40,20 @@ export declare function sign(data: SignData, key: Text, authDate: Date, options?
25
40
  * @param options - additional options.
26
41
  * @returns Data sign.
27
42
  */
28
- export declare function signData(data: Text, key: Text, options?: SignDataOptions): Promise<string>;
43
+ export declare function signDataFp(data: Text, key: Text, options?: SignDataOptions): TE.TaskEither<SignDataError, string>;
44
+ /**
45
+ * @see signDataFp
46
+ */
47
+ export declare function signData(data: Text, key: Text, options?: SignDataOptions): BetterPromise<string>;
29
48
  /**
30
49
  * Validates passed init data.
31
50
  * @param value - value to check.
32
51
  * @param token - bot secret token.
33
52
  * @param options - additional validation options.
34
- * @throws {TypeError} "auth_date" should present integer
35
- * @throws {Error} "hash" is empty or not found
36
- * @throws {Error} "auth_date" is empty or not found
37
- * @throws {Error} Init data expired
38
53
  */
39
- export declare function validate(value: InitData | InitDataParsed | string | URLSearchParams, token: Text, options?: ValidateOptions): Promise<void>;
54
+ export declare function validateFp(value: ValidateValue, token: Text, options?: ValidateAsyncOptions): TE.TaskEither<ValidateAsyncError, void>;
55
+ /**
56
+ * @see validateFp
57
+ */
58
+ export declare function validate(value: ValidateValue, token: Text, options?: ValidateAsyncOptions): BetterPromise<void>;
40
59
  export * from './shared.js';