Skip to content
This repository has been archived by the owner on Sep 4, 2024. It is now read-only.

Add types #285

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions envs/browser/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import MTProto from '../../src';

export default MTProto;
export * from '../../src';
4 changes: 4 additions & 0 deletions envs/node/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import MTProto from '../../src';

export default MTProto;
export * from '../../src';
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"/src"
],
"main": "./envs/node/index.js",
"types": "src/index.d.ts",
"engines": {
"node": ">=18"
},
Expand All @@ -37,7 +38,8 @@
"test": "jest",
"generate-builder": "node scripts/generate-builder.js",
"generate-parser": "node scripts/generate-parser.js",
"generate-tl": "run-p generate-builder generate-parser",
"generate-types": "node scripts/generate-types.js",
"generate-tl": "run-p generate-builder generate-parser generate-types",
"prepublishOnly": "run-s generate-tl"
},
"license": "GPL-3.0",
Expand Down
115 changes: 115 additions & 0 deletions scripts/generate-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
const fs = require('fs');

const schema = require('../scheme/api.json');

const lines = [];

const excludedTypes = ['Bool', 'True', 'Null', 'Vector t'];

function sanatizeName(name) {
return name.replace(/\./g, '_');
}

function generateType(type) {
let array = false;
let optional = false;

if (type.includes('flags') && type.includes('?')) {
optional = true;
type = type.split('?')[1];
}

if (type.includes('Vector')) {
array = true;
const regex = /<(.*?)>/;
const matches = regex.exec(type);
if (matches && matches.length >= 2) {
type = matches[1];
}
}

if (type === 'int' || type === 'long' || type === 'double' || type === '#') {
optional = true;
type = 'number';
} else if (type === 'string') {
optional = true;
type = 'string';
} else if (type === 'Bool' || type === 'true') {
optional = true;
type = 'boolean';
} else if (type === 'bytes') {
optional = true;
type = 'Uint8Array';
} else if (type === '!X' || type === 'X') {
type = 'unknown';
} else {
type = sanatizeName(type);
}

return { type: `${array ? `Array<${type}>` : type}`, optional };
}

function generateTypeLine(input) {
let { name } = input;
let { type, optional } = generateType(input.type);

return `${name}${optional ? '?' : ''}: ${type};`;
}

lines.push('export interface Methods {');

schema.methods.forEach((method) => {
const methodName = method.method.indexOf('.') > -1 ? `'${method.method}'` : method.method;
lines.push(` ${methodName}: {`);

if (method.params.length === 0) {
lines.push(` params?: {};`);
} else {
lines.push(` params: {`);
method.params.forEach((param) => {
if (param.name === 'flags') return;
lines.push(` ${generateTypeLine(param)}`);
});
lines.push(` };`);
}
lines.push(` return: ${generateType(method.type).type};`);
lines.push(` };`);
});

lines.push('};\n');

let finishedTypes = [];

schema.constructors.forEach((constructor) => {
if (
excludedTypes.includes(constructor.type) ||
finishedTypes.findIndex((t) => t === constructor.type) >= 0
) {
return;
}

let name = sanatizeName(constructor.type);

const variants = [];
const constructors = schema.constructors.filter((c) => c.type === constructor.type);

constructors.forEach((constructorVariant) => {
let variantName = sanatizeName(constructorVariant.predicate);

lines.push(`export type ${variantName} = {`);
lines.push(` _: '${constructorVariant.predicate}';`);
constructorVariant.params.forEach((param) => {
lines.push(` ${generateTypeLine(param)}`);
});
lines.push('};');
variants.push(variantName);
});

lines.push(`export type ${name} = ${variants.join(' | ')};\n`);

finishedTypes.push(constructor.type);
});

const fileContent = lines.join('\n');

fs.writeFileSync('src/tl/types/schema.d.ts', fileContent);
55 changes: 55 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import RPC from './rpc';
import { Methods } from './tl/types/schema';

type MethodReturnMap<T extends Methods> = {
[K in keyof T]: T[K] extends { return: infer R } ? R : never;
};

type MethodOptions = {
syncAuth?: boolean;
dcId?: number;
};

type Error = {
_: string;
error_code: number;
error_message: string;
};

declare class CustomStorage {
set(key: string, value: string): Promise<void>;
get(key: string): Promise<string | null>;
}

declare class MTProto {
constructor(options: {
api_id: number;
api_hash: string;
test?: boolean;
storageOptions?: {
instance: CustomStorage;
};
});

/**
* @throws {Error}
*/
call<T extends keyof Methods>(
method: T,
params?: Methods[T]['params'],
options?: MethodOptions,
): Promise<MethodReturnMap<Methods>[T]>;

syncAuth(
dcId: number,
): Promise<(MethodReturnMap<Methods>['auth.importAuthorization'] | undefined)[]>;

setDefaultDc(dcId: number): ReturnType<InstanceType<typeof CustomStorage>['set']>;

getRPC(dcId: number): InstanceType<typeof RPC>;

updateInitConnectionParams(params: Record<string, string>): void;
}

export { CustomStorage, Methods, MethodReturnMap, MethodOptions, Error };
export default MTProto;
Loading