fix: handle vectors of primitives in rpc_result
This commit is contained in:
parent
99e83b40aa
commit
6221a8716f
21 changed files with 241 additions and 80 deletions
|
@ -101,7 +101,7 @@ export function normalizeToInputChannel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MtInvalidPeerTypeError(input ?? res, 'user')
|
throw new MtInvalidPeerTypeError(input ?? res, 'channel')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isInputPeerUser(
|
export function isInputPeerUser(
|
||||||
|
|
|
@ -394,6 +394,13 @@ export class SessionConnection extends PersistentConnection {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.peekUint() === 0xf35c6d01) {
|
||||||
|
// rpc_result
|
||||||
|
message.uint()
|
||||||
|
|
||||||
|
return this._onRpcResult(message)
|
||||||
|
}
|
||||||
|
|
||||||
// we are safe.. i guess
|
// we are safe.. i guess
|
||||||
this._handleMessage(messageId, message.object())
|
this._handleMessage(messageId, message.object())
|
||||||
}
|
}
|
||||||
|
@ -433,9 +440,6 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (message._) {
|
switch (message._) {
|
||||||
case 'mt_rpc_result':
|
|
||||||
this._onRpcResult(message)
|
|
||||||
break
|
|
||||||
case 'mt_pong':
|
case 'mt_pong':
|
||||||
this._onPong(message)
|
this._onPong(message)
|
||||||
break
|
break
|
||||||
|
@ -482,7 +486,9 @@ export class SessionConnection extends PersistentConnection {
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
if (tl.isAnyUpdates(message)) {
|
if (tl.isAnyUpdates(message)) {
|
||||||
if (this._usable && this.params.inactivityTimeout) { this._rescheduleInactivity() }
|
if (this._usable && this.params.inactivityTimeout) {
|
||||||
|
this._rescheduleInactivity()
|
||||||
|
}
|
||||||
|
|
||||||
this.emit('update', message)
|
this.emit('update', message)
|
||||||
|
|
||||||
|
@ -493,13 +499,24 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onRpcResult({ result, reqMsgId }: mtp.RawMt_rpc_result): void {
|
private _onRpcResult(message: TlBinaryReader): void {
|
||||||
if (this._usable && this.params.inactivityTimeout) { this._rescheduleInactivity() }
|
if (this._usable && this.params.inactivityTimeout) {
|
||||||
|
this._rescheduleInactivity()
|
||||||
|
}
|
||||||
|
|
||||||
|
const reqMsgId = message.long()
|
||||||
|
|
||||||
if (reqMsgId.isZero()) {
|
if (reqMsgId.isZero()) {
|
||||||
|
let resultType
|
||||||
|
|
||||||
|
try {
|
||||||
|
resultType = (message.object() as any)._
|
||||||
|
} catch (err) {
|
||||||
|
resultType = message.peekUint()
|
||||||
|
}
|
||||||
this.log.warn(
|
this.log.warn(
|
||||||
'received rpc_result with %s with req_msg_id = 0',
|
'received rpc_result with %s with req_msg_id = 0',
|
||||||
result._,
|
resultType,
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -508,12 +525,24 @@ export class SessionConnection extends PersistentConnection {
|
||||||
const msg = this._pendingMessages.get(reqMsgId)
|
const msg = this._pendingMessages.get(reqMsgId)
|
||||||
|
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
|
let result
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = message.object() as any
|
||||||
|
} catch (err) {
|
||||||
|
result = '[failed to parse]'
|
||||||
|
}
|
||||||
|
this.log.warn(
|
||||||
|
'received rpc_result with %s with req_msg_id = 0',
|
||||||
|
result,
|
||||||
|
)
|
||||||
|
|
||||||
// check if the msg is one of the recent ones
|
// check if the msg is one of the recent ones
|
||||||
if (this._recentOutgoingMsgIds.has(reqMsgId)) {
|
if (this._recentOutgoingMsgIds.has(reqMsgId)) {
|
||||||
this.log.debug(
|
this.log.debug(
|
||||||
'received rpc_result again for %l (contains %s)',
|
'received rpc_result again for %l (contains %s)',
|
||||||
reqMsgId,
|
reqMsgId,
|
||||||
result._,
|
result,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
this.log.warn(
|
this.log.warn(
|
||||||
|
@ -537,6 +566,11 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
const rpc = msg.rpc
|
const rpc = msg.rpc
|
||||||
|
|
||||||
|
const customReader = this._readerMap._results![rpc.method]
|
||||||
|
const result: any = customReader ?
|
||||||
|
customReader(message) :
|
||||||
|
message.object()
|
||||||
|
|
||||||
// initConnection call was definitely received and
|
// initConnection call was definitely received and
|
||||||
// processed by the server, so we no longer need to use it
|
// processed by the server, so we no longer need to use it
|
||||||
if (rpc.initConn) this._initConnectionCalled = true
|
if (rpc.initConn) this._initConnectionCalled = true
|
||||||
|
@ -1056,7 +1090,9 @@ export class SessionConnection extends PersistentConnection {
|
||||||
stack?: string,
|
stack?: string,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
): Promise<tl.RpcCallReturn[T['_']]> {
|
): Promise<tl.RpcCallReturn[T['_']]> {
|
||||||
if (this._usable && this.params.inactivityTimeout) { this._rescheduleInactivity() }
|
if (this._usable && this.params.inactivityTimeout) {
|
||||||
|
this._rescheduleInactivity()
|
||||||
|
}
|
||||||
|
|
||||||
if (!stack && this.params.niceStacks !== false) {
|
if (!stack && this.params.niceStacks !== false) {
|
||||||
stack = new Error().stack
|
stack = new Error().stack
|
||||||
|
@ -1162,7 +1198,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
timeout: undefined,
|
timeout: undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
const promise = createCancellablePromise(
|
const promise = createCancellablePromise<any>(
|
||||||
this._cancelRpc.bind(this, pending),
|
this._cancelRpc.bind(this, pending),
|
||||||
)
|
)
|
||||||
pending.promise = promise
|
pending.promise = promise
|
||||||
|
|
|
@ -18,7 +18,10 @@ const TWO_PWR_32_DBL = (1 << 16) * (1 << 16)
|
||||||
*/
|
*/
|
||||||
// avoid unnecessary type complexity
|
// avoid unnecessary type complexity
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type TlReaderMap = Record<number, (r: any) => unknown>
|
export type TlReaderMap = Record<number, (r: any) => unknown> & {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
_results?: Record<string, (r: any) => unknown>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reader for TL objects.
|
* Reader for TL objects.
|
||||||
|
@ -157,7 +160,8 @@ export class TlBinaryReader {
|
||||||
|
|
||||||
bytes(): Buffer {
|
bytes(): Buffer {
|
||||||
const firstByte = this.data[this.pos++]
|
const firstByte = this.data[this.pos++]
|
||||||
let length; let padding
|
let length
|
||||||
|
let padding
|
||||||
|
|
||||||
if (firstByte === 254) {
|
if (firstByte === 254) {
|
||||||
length =
|
length =
|
||||||
|
@ -183,7 +187,9 @@ export class TlBinaryReader {
|
||||||
object(): unknown {
|
object(): unknown {
|
||||||
const id = this.uint()
|
const id = this.uint()
|
||||||
|
|
||||||
if (id === 0x1cb5c415 /* vector */) { return this.vector(this.object, true) }
|
if (id === 0x1cb5c415 /* vector */) {
|
||||||
|
return this.vector(this.object, true)
|
||||||
|
}
|
||||||
if (id === 0x3072cfa1 /* gzip_packed */) return this.gzip()
|
if (id === 0x3072cfa1 /* gzip_packed */) return this.gzip()
|
||||||
if (id === 0xbc799737 /* boolFalse */) return false
|
if (id === 0xbc799737 /* boolFalse */) return false
|
||||||
if (id === 0x997275b5 /* boolTrue */) return true
|
if (id === 0x997275b5 /* boolTrue */) return true
|
||||||
|
@ -251,7 +257,9 @@ export class TlBinaryReader {
|
||||||
* @param pos Position to seek to
|
* @param pos Position to seek to
|
||||||
*/
|
*/
|
||||||
seekTo(pos: number): void {
|
seekTo(pos: number): void {
|
||||||
if (pos >= this.data.length || pos < 0) { throw new RangeError('New position is out of range') }
|
if (pos >= this.data.length || pos < 0) {
|
||||||
|
throw new RangeError('New position is out of range')
|
||||||
|
}
|
||||||
this.pos = pos
|
this.pos = pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,10 @@ const TWO_PWR_32_DBL = (1 << 16) * (1 << 16)
|
||||||
*/
|
*/
|
||||||
// avoid unnecessary type complexity
|
// avoid unnecessary type complexity
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type TlWriterMap = Record<string, (w: any, val: any) => void>
|
export type TlWriterMap = Record<string, (w: any, val: any) => void> & {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
_bare?: Record<number, (w: any, val: any) => void>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counter of the required number of bytes to encode a given object.
|
* Counter of the required number of bytes to encode a given object.
|
||||||
|
@ -198,7 +201,9 @@ export class TlBinaryWriter {
|
||||||
obj: { _: string },
|
obj: { _: string },
|
||||||
knownSize = -1,
|
knownSize = -1,
|
||||||
): Buffer {
|
): Buffer {
|
||||||
if (knownSize === -1) { knownSize = TlSerializationCounter.countNeededBytes(objectMap, obj) }
|
if (knownSize === -1) {
|
||||||
|
knownSize = TlSerializationCounter.countNeededBytes(objectMap, obj)
|
||||||
|
}
|
||||||
|
|
||||||
const writer = TlBinaryWriter.alloc(objectMap, knownSize)
|
const writer = TlBinaryWriter.alloc(objectMap, knownSize)
|
||||||
|
|
||||||
|
@ -315,7 +320,11 @@ export class TlBinaryWriter {
|
||||||
fn(this, obj)
|
fn(this, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
vector(fn: (item: unknown, bare?: boolean) => void, val: unknown[], bare?: boolean): void {
|
vector(
|
||||||
|
fn: (item: unknown, bare?: boolean) => void,
|
||||||
|
val: unknown[],
|
||||||
|
bare?: boolean,
|
||||||
|
): void {
|
||||||
if (!bare) this.uint(0x1cb5c415)
|
if (!bare) this.uint(0x1cb5c415)
|
||||||
this.uint(val.length)
|
this.uint(val.length)
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,23 @@ export interface ReaderCodegenOptions {
|
||||||
* Whether to include methods in the readers map
|
* Whether to include methods in the readers map
|
||||||
*/
|
*/
|
||||||
includeMethods?: boolean
|
includeMethods?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to include `._results` field in the result object,
|
||||||
|
* containing a map of methods names to their result readers.
|
||||||
|
*
|
||||||
|
* Requires `parseMethodTypes` to be `true` when parsing the TL schema.
|
||||||
|
*
|
||||||
|
* **Note**: will only work for primitives and vectors of primitives
|
||||||
|
*/
|
||||||
|
includeMethodResults?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_OPTIONS: ReaderCodegenOptions = {
|
const DEFAULT_OPTIONS: ReaderCodegenOptions = {
|
||||||
includeFlags: false,
|
includeFlags: false,
|
||||||
variableName: 'm',
|
variableName: 'm',
|
||||||
includeMethods: false,
|
includeMethods: false,
|
||||||
|
includeMethodResults: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -177,5 +188,34 @@ export function generateReaderCodeForTlEntries(
|
||||||
ret += generateReaderCodeForTlEntry(entry, params) + '\n'
|
ret += generateReaderCodeForTlEntry(entry, params) + '\n'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (params.includeMethodResults) {
|
||||||
|
ret += '_results:{\n'
|
||||||
|
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
if (entry.kind !== 'method') return
|
||||||
|
|
||||||
|
const pre = `'${entry.name}':function(r){return `
|
||||||
|
|
||||||
|
const isVector =
|
||||||
|
entry.typeModifiers?.isVector ||
|
||||||
|
entry.typeModifiers?.isBareVector
|
||||||
|
const post = entry.typeModifiers?.isBareVector ? ',1' : ''
|
||||||
|
|
||||||
|
if (entry.type in TL_PRIMITIVES) {
|
||||||
|
const type = entry.type
|
||||||
|
// booleans can be properly parsed as they have own constructor ids
|
||||||
|
if (type === 'Bool' || type === 'bool') return
|
||||||
|
|
||||||
|
if (isVector) {
|
||||||
|
ret += `${pre}r.vector(r.${type}${post})},\n`
|
||||||
|
} else {
|
||||||
|
ret += `${pre}r.${type}()},\n`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ret += '},\n'
|
||||||
|
}
|
||||||
|
|
||||||
return ret + '}'
|
return ret + '}'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { TlEntry, TlErrors, TlFullSchema } from '../types'
|
import { TlEntry, TlErrors, TlFullSchema, TlTypeModifiers } from '../types'
|
||||||
import { groupTlEntriesByNamespace, splitNameToNamespace } from '../utils'
|
import { groupTlEntriesByNamespace, splitNameToNamespace } from '../utils'
|
||||||
import { errorCodeToClassName, generateCodeForErrors } from './errors'
|
import { errorCodeToClassName, generateCodeForErrors } from './errors'
|
||||||
import { camelToPascal, indent, jsComment, snakeToCamel } from './utils'
|
import { camelToPascal, indent, jsComment, snakeToCamel } from './utils'
|
||||||
|
@ -26,10 +26,31 @@ export const PRIMITIVE_TO_TS: Record<string, string> = {
|
||||||
function fullTypeName(
|
function fullTypeName(
|
||||||
type: string,
|
type: string,
|
||||||
baseNamespace: string,
|
baseNamespace: string,
|
||||||
|
{
|
||||||
namespace = true,
|
namespace = true,
|
||||||
method = false,
|
method = false,
|
||||||
link = false,
|
link = false,
|
||||||
|
typeModifiers,
|
||||||
|
}: {
|
||||||
|
namespace?: boolean
|
||||||
|
method?: boolean
|
||||||
|
link?: boolean
|
||||||
|
typeModifiers?: TlTypeModifiers
|
||||||
|
} = {},
|
||||||
): string {
|
): string {
|
||||||
|
if (typeModifiers) {
|
||||||
|
const inner = fullTypeName(type, baseNamespace, {
|
||||||
|
namespace,
|
||||||
|
method,
|
||||||
|
link,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (typeModifiers.isVector || typeModifiers.isBareVector) {
|
||||||
|
if (link) return `${inner} array`
|
||||||
|
|
||||||
|
return `${inner}[]`
|
||||||
|
}
|
||||||
|
}
|
||||||
if (type in PRIMITIVE_TO_TS) return PRIMITIVE_TO_TS[type]
|
if (type in PRIMITIVE_TO_TS) return PRIMITIVE_TO_TS[type]
|
||||||
|
|
||||||
const [ns, name] = splitNameToNamespace(type)
|
const [ns, name] = splitNameToNamespace(type)
|
||||||
|
@ -52,7 +73,10 @@ function fullTypeName(
|
||||||
}
|
}
|
||||||
|
|
||||||
function entryFullTypeName(entry: TlEntry): string {
|
function entryFullTypeName(entry: TlEntry): string {
|
||||||
return fullTypeName(entry.name, '', false, entry.kind === 'method')
|
return fullTypeName(entry.name, '', {
|
||||||
|
namespace: false,
|
||||||
|
method: entry.kind === 'method',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,9 +106,7 @@ export function generateTypescriptDefinitionsForTlEntry(
|
||||||
comment += `RPC method returns ${fullTypeName(
|
comment += `RPC method returns ${fullTypeName(
|
||||||
entry.type,
|
entry.type,
|
||||||
baseNamespace,
|
baseNamespace,
|
||||||
true,
|
{ link: true, typeModifiers: entry.typeModifiers },
|
||||||
false,
|
|
||||||
true,
|
|
||||||
)}`
|
)}`
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
|
@ -151,10 +173,10 @@ export function generateTypescriptDefinitionsForTlEntry(
|
||||||
typeFinal = true
|
typeFinal = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!typeFinal) type = fullTypeName(arg.type, baseNamespace)
|
if (!typeFinal) {
|
||||||
|
type = fullTypeName(arg.type, baseNamespace, {
|
||||||
if (arg.typeModifiers?.isVector || arg.typeModifiers?.isBareVector) {
|
typeModifiers: arg.typeModifiers,
|
||||||
type += '[]'
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ret += `: ${type};\n`
|
ret += `: ${type};\n`
|
||||||
|
@ -306,7 +328,9 @@ export function generateTypescriptDefinitionsForTlSchema(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
type = fullTypeName(entry.type, namespace + '.')
|
type = fullTypeName(entry.type, namespace + '.', {
|
||||||
|
typeModifiers: entry.typeModifiers,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ts += indent(indentSize + 4, `'${entry.name}': ${type}`) + '\n'
|
ts += indent(indentSize + 4, `'${entry.name}': ${type}`) + '\n'
|
||||||
|
@ -324,7 +348,7 @@ export function generateTypescriptDefinitionsForTlSchema(
|
||||||
if (union.comment) {
|
if (union.comment) {
|
||||||
ts += indent(indentSize, jsComment(union.comment)) + '\n'
|
ts += indent(indentSize, jsComment(union.comment)) + '\n'
|
||||||
}
|
}
|
||||||
const typeName = fullTypeName(name, '', false)
|
const typeName = fullTypeName(name, '', { namespace: false })
|
||||||
const typeWithoutNs = typeName.substring(4)
|
const typeWithoutNs = typeName.substring(4)
|
||||||
ts += indent(indentSize, `type ${typeName} = `)
|
ts += indent(indentSize, `type ${typeName} = `)
|
||||||
|
|
||||||
|
@ -364,7 +388,8 @@ export function generateTypescriptDefinitionsForTlSchema(
|
||||||
ts +=
|
ts +=
|
||||||
indent(
|
indent(
|
||||||
8,
|
8,
|
||||||
'| ' + fullTypeName(entry.name, namespace + '.', true, true),
|
'| ' +
|
||||||
|
fullTypeName(entry.name, namespace + '.', { method: true }),
|
||||||
) + '\n'
|
) + '\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,12 +406,9 @@ export function generateTypescriptDefinitionsForTlSchema(
|
||||||
indent(
|
indent(
|
||||||
8,
|
8,
|
||||||
'| ' +
|
'| ' +
|
||||||
fullTypeName(
|
fullTypeName(entry.name, namespace + '.', {
|
||||||
entry.name,
|
method: entry.kind === 'method',
|
||||||
namespace + '.',
|
}),
|
||||||
true,
|
|
||||||
entry.kind === 'method',
|
|
||||||
),
|
|
||||||
) + '\n'
|
) + '\n'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,16 @@ export function mergeTlEntries(entries: TlEntry[]): TlEntry | string {
|
||||||
kind: first.kind,
|
kind: first.kind,
|
||||||
name: first.name,
|
name: first.name,
|
||||||
type: first.type,
|
type: first.type,
|
||||||
|
typeModifiers: first.typeModifiers,
|
||||||
id: first.id,
|
id: first.id,
|
||||||
comment: first.comment,
|
comment: first.comment,
|
||||||
generics: first.generics,
|
generics: first.generics,
|
||||||
arguments: first.arguments,
|
arguments: first.arguments,
|
||||||
}
|
}
|
||||||
|
|
||||||
// even if the entry contains id, let's re-calculate it just to be sure
|
if (result.id === 0) {
|
||||||
result.id = computeConstructorIdFromEntry(result)
|
result.id = computeConstructorIdFromEntry(result)
|
||||||
|
}
|
||||||
|
|
||||||
const argsIndex: Record<string, true> = {}
|
const argsIndex: Record<string, true> = {}
|
||||||
const flagsLastIndex: Record<string, number> = {}
|
const flagsLastIndex: Record<string, number> = {}
|
||||||
|
|
|
@ -13,7 +13,7 @@ const SINGLE_REGEX =
|
||||||
*/
|
*/
|
||||||
export function parseTlToEntries(
|
export function parseTlToEntries(
|
||||||
tl: string,
|
tl: string,
|
||||||
params?: {
|
params: {
|
||||||
/**
|
/**
|
||||||
* Whether to throw an error if a line failed to parse
|
* Whether to throw an error if a line failed to parse
|
||||||
*/
|
*/
|
||||||
|
@ -45,7 +45,12 @@ export function parseTlToEntries(
|
||||||
* If true, the `id` field will be set to 0 for all entries.
|
* If true, the `id` field will be set to 0 for all entries.
|
||||||
*/
|
*/
|
||||||
forIdComputation?: boolean
|
forIdComputation?: boolean
|
||||||
},
|
|
||||||
|
/**
|
||||||
|
* Whether to parse typeModifiers for method return types
|
||||||
|
*/
|
||||||
|
parseMethodTypes?: boolean
|
||||||
|
} = {},
|
||||||
): TlEntry[] {
|
): TlEntry[] {
|
||||||
const ret: TlEntry[] = []
|
const ret: TlEntry[] = []
|
||||||
|
|
||||||
|
@ -56,13 +61,13 @@ export function parseTlToEntries(
|
||||||
|
|
||||||
let currentKind: TlEntry['kind'] = 'class'
|
let currentKind: TlEntry['kind'] = 'class'
|
||||||
let currentComment = ''
|
let currentComment = ''
|
||||||
const prefix = params?.prefix ?? ''
|
const prefix = params.prefix ?? ''
|
||||||
|
|
||||||
lines.forEach((line, idx) => {
|
lines.forEach((line, idx) => {
|
||||||
line = line.trim()
|
line = line.trim()
|
||||||
|
|
||||||
if (line === '') {
|
if (line === '') {
|
||||||
if (params?.onOrphanComment) {
|
if (params.onOrphanComment) {
|
||||||
params.onOrphanComment(currentComment)
|
params.onOrphanComment(currentComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,9 +107,9 @@ export function parseTlToEntries(
|
||||||
if (!match) {
|
if (!match) {
|
||||||
const err = new Error(`Failed to parse line ${idx + 1}: ${line}`)
|
const err = new Error(`Failed to parse line ${idx + 1}: ${line}`)
|
||||||
|
|
||||||
if (params?.panicOnError) {
|
if (params.panicOnError) {
|
||||||
throw err
|
throw err
|
||||||
} else if (params?.onError) {
|
} else if (params.onError) {
|
||||||
params.onError(err, line, idx + 1)
|
params.onError(err, line, idx + 1)
|
||||||
} else {
|
} else {
|
||||||
console.warn(err)
|
console.warn(err)
|
||||||
|
@ -121,7 +126,7 @@ export function parseTlToEntries(
|
||||||
|
|
||||||
let typeIdNum = typeId ? parseInt(typeId, 16) : 0
|
let typeIdNum = typeId ? parseInt(typeId, 16) : 0
|
||||||
|
|
||||||
if (typeIdNum === 0 && !params?.forIdComputation) {
|
if (typeIdNum === 0 && !params.forIdComputation) {
|
||||||
typeIdNum = computeConstructorIdFromString(line)
|
typeIdNum = computeConstructorIdFromString(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +146,15 @@ export function parseTlToEntries(
|
||||||
arguments: [],
|
arguments: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry.kind === 'method' && params.parseMethodTypes) {
|
||||||
|
const [type, modifiers] = parseArgumentType(entry.type)
|
||||||
|
entry.type = type
|
||||||
|
|
||||||
|
if (Object.keys(modifiers).length) {
|
||||||
|
entry.typeModifiers = modifiers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (generics) {
|
if (generics) {
|
||||||
entry.generics = generics.split(',').map((it) => {
|
entry.generics = generics.split(',').map((it) => {
|
||||||
const [name, type] = it.split(':')
|
const [name, type] = it.split(':')
|
||||||
|
@ -193,14 +207,29 @@ export function parseTlToEntries(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (currentComment && params?.onOrphanComment) {
|
if (currentComment && params.onOrphanComment) {
|
||||||
params.onOrphanComment(currentComment)
|
params.onOrphanComment(currentComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
// post-process:
|
// post-process:
|
||||||
|
// - add return type ctor id for methods
|
||||||
// - find arguments where type is not a union and put corresponding modifiers
|
// - find arguments where type is not a union and put corresponding modifiers
|
||||||
// - apply prefix
|
// - apply prefix
|
||||||
ret.forEach((entry, entryIdx) => {
|
ret.forEach((entry, entryIdx) => {
|
||||||
|
if (params.parseMethodTypes && entry.kind === 'method') {
|
||||||
|
const type = entry.type
|
||||||
|
|
||||||
|
if (type in unions && unions[type].length === 1) {
|
||||||
|
if (!entry.typeModifiers) entry.typeModifiers = {}
|
||||||
|
|
||||||
|
entry.typeModifiers.constructorId = unions[type][0].id
|
||||||
|
} else if (type in entries) {
|
||||||
|
if (!entry.typeModifiers) entry.typeModifiers = {}
|
||||||
|
entry.typeModifiers.isBareType = true
|
||||||
|
entry.typeModifiers.constructorId = entries[type].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
entry.arguments.forEach((arg) => {
|
entry.arguments.forEach((arg) => {
|
||||||
const type = arg.type
|
const type = arg.type
|
||||||
|
|
||||||
|
@ -214,9 +243,9 @@ export function parseTlToEntries(
|
||||||
`Union ${type} has more than one entry, cannot use it like %${type} (found in ${entry.name}#${arg.name})`,
|
`Union ${type} has more than one entry, cannot use it like %${type} (found in ${entry.name}#${arg.name})`,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (params?.panicOnError) {
|
if (params.panicOnError) {
|
||||||
throw err
|
throw err
|
||||||
} else if (params?.onError) {
|
} else if (params.onError) {
|
||||||
params.onError(err, '', entryIdx)
|
params.onError(err, '', entryIdx)
|
||||||
} else {
|
} else {
|
||||||
console.warn(err)
|
console.warn(err)
|
||||||
|
|
|
@ -27,11 +27,12 @@ export function patchRuntimeTlSchema(
|
||||||
readerMap: TlReaderMap
|
readerMap: TlReaderMap
|
||||||
writerMap: TlWriterMap
|
writerMap: TlWriterMap
|
||||||
} {
|
} {
|
||||||
const entries = parseTlToEntries(schema)
|
const entries = parseTlToEntries(schema, { parseMethodTypes: true })
|
||||||
|
|
||||||
const readersCode = generateReaderCodeForTlEntries(entries, {
|
const readersCode = generateReaderCodeForTlEntries(entries, {
|
||||||
variableName: '_',
|
variableName: '_',
|
||||||
includeMethods: false,
|
includeMethods: false,
|
||||||
|
includeMethodResults: true,
|
||||||
})
|
})
|
||||||
const writersCode = generateWriterCodeForTlEntries(entries, {
|
const writersCode = generateWriterCodeForTlEntries(entries, {
|
||||||
variableName: '_',
|
variableName: '_',
|
||||||
|
@ -49,10 +50,21 @@ export function patchRuntimeTlSchema(
|
||||||
readerMap: {
|
readerMap: {
|
||||||
...readers,
|
...readers,
|
||||||
...newReaders,
|
...newReaders,
|
||||||
|
_results: {
|
||||||
|
...readers._results,
|
||||||
|
...newReaders._results,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
// ts is not smart enough
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
writerMap: {
|
writerMap: {
|
||||||
...writers,
|
...writers,
|
||||||
...newWriters,
|
...newWriters,
|
||||||
|
_bare: {
|
||||||
|
...writers._bare,
|
||||||
|
...newWriters._bare,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,10 @@ export function writeTlEntryToString(
|
||||||
if (forIdComputation) {
|
if (forIdComputation) {
|
||||||
str += '= ' + normalizeType(entry.type)
|
str += '= ' + normalizeType(entry.type)
|
||||||
} else {
|
} else {
|
||||||
str += '= ' + entry.type + ';'
|
const type = entry.typeModifiers ?
|
||||||
|
stringifyArgumentType(entry.type, entry.typeModifiers) :
|
||||||
|
entry.type
|
||||||
|
str += '= ' + type + ';'
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
return str
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Modifiers for {@link TlArgument.type}
|
* Modifiers for {@link TlArgument.type}
|
||||||
*/
|
*/
|
||||||
export interface TlArgumentModifiers {
|
export interface TlTypeModifiers {
|
||||||
/**
|
/**
|
||||||
* Predicate of the argument
|
* Predicate of the argument
|
||||||
* @example `flags.3`
|
* @example `flags.3`
|
||||||
|
@ -74,7 +74,7 @@ export interface TlArgument {
|
||||||
/**
|
/**
|
||||||
* Modifiers for {@link type}
|
* Modifiers for {@link type}
|
||||||
*/
|
*/
|
||||||
typeModifiers?: TlArgumentModifiers
|
typeModifiers?: TlTypeModifiers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comment of the argument
|
* Comment of the argument
|
||||||
|
@ -121,6 +121,11 @@ export interface TlEntry {
|
||||||
*/
|
*/
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For methods (where {@link type} is the return type), modifiers for {@link type}
|
||||||
|
*/
|
||||||
|
typeModifiers?: TlTypeModifiers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comment of the entry
|
* Comment of the entry
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { TlArgumentModifiers, TlEntry } from './types'
|
import { TlEntry, TlTypeModifiers } from './types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split qualified TL entry name into namespace and name
|
* Split qualified TL entry name into namespace and name
|
||||||
|
@ -63,7 +63,7 @@ export function groupTlEntriesByNamespace(
|
||||||
|
|
||||||
export function stringifyArgumentType(
|
export function stringifyArgumentType(
|
||||||
type: string,
|
type: string,
|
||||||
modifiers?: TlArgumentModifiers,
|
modifiers?: TlTypeModifiers,
|
||||||
) {
|
) {
|
||||||
if (!modifiers) return type
|
if (!modifiers) return type
|
||||||
let ret = type
|
let ret = type
|
||||||
|
@ -76,8 +76,8 @@ export function stringifyArgumentType(
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseArgumentType(type: string): [string, TlArgumentModifiers] {
|
export function parseArgumentType(type: string): [string, TlTypeModifiers] {
|
||||||
const modifiers: TlArgumentModifiers = {}
|
const modifiers: TlTypeModifiers = {}
|
||||||
const [predicate, type_] = type.split('?')
|
const [predicate, type_] = type.split('?')
|
||||||
|
|
||||||
if (type_) {
|
if (type_) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
4
packages/tl/binary/reader.d.ts
vendored
4
packages/tl/binary/reader.d.ts
vendored
|
@ -1,3 +1,5 @@
|
||||||
declare const __tlReaderMap: Record<number, (r: unknown) => unknown>
|
declare const __tlReaderMap: Record<number, (r: unknown) => unknown> & {
|
||||||
|
_results: Record<string, (r: unknown) => unknown>
|
||||||
|
}
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default __tlReaderMap
|
export default __tlReaderMap
|
||||||
|
|
4
packages/tl/binary/writer.d.ts
vendored
4
packages/tl/binary/writer.d.ts
vendored
|
@ -1,3 +1,5 @@
|
||||||
declare const __tlWriterMap: Record<string, (w: unknown, val: unknown) => void>
|
declare const __tlWriterMap: Record<string, (w: unknown, val: unknown) => void> & {
|
||||||
|
_bare: Record<number, (r: unknown) => unknown>
|
||||||
|
}
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default __tlWriterMap
|
export default __tlWriterMap
|
||||||
|
|
|
@ -80,7 +80,6 @@
|
||||||
"phoneCallAccepted": ["admin_id", "participant_id"],
|
"phoneCallAccepted": ["admin_id", "participant_id"],
|
||||||
"phoneCallRequested": ["admin_id", "participant_id"],
|
"phoneCallRequested": ["admin_id", "participant_id"],
|
||||||
"phoneCallWaiting": ["admin_id", "participant_id"],
|
"phoneCallWaiting": ["admin_id", "participant_id"],
|
||||||
"pollResults": ["recent_voters"],
|
|
||||||
"privacyValueAllowChatParticipants": ["chats"],
|
"privacyValueAllowChatParticipants": ["chats"],
|
||||||
"privacyValueAllowUsers": ["users"],
|
"privacyValueAllowUsers": ["users"],
|
||||||
"privacyValueDisallowChatParticipants": ["chats"],
|
"privacyValueDisallowChatParticipants": ["chats"],
|
||||||
|
@ -123,7 +122,6 @@
|
||||||
"updateDeleteChannelMessages": ["channel_id"],
|
"updateDeleteChannelMessages": ["channel_id"],
|
||||||
"updateGroupCall": ["chat_id"],
|
"updateGroupCall": ["chat_id"],
|
||||||
"updateInlineBotCallbackQuery": ["user_id"],
|
"updateInlineBotCallbackQuery": ["user_id"],
|
||||||
"updateMessagePollVote": ["user_id"],
|
|
||||||
"updatePinnedChannelMessages": ["channel_id"],
|
"updatePinnedChannelMessages": ["channel_id"],
|
||||||
"updateReadChannelDiscussionInbox": ["channel_id", "broadcast_id"],
|
"updateReadChannelDiscussionInbox": ["channel_id", "broadcast_id"],
|
||||||
"updateReadChannelDiscussionOutbox": ["channel_id"],
|
"updateReadChannelDiscussionOutbox": ["channel_id"],
|
||||||
|
@ -139,7 +137,6 @@
|
||||||
"updateUserPhoto": ["user_id"],
|
"updateUserPhoto": ["user_id"],
|
||||||
"updateUserStatus": ["user_id"],
|
"updateUserStatus": ["user_id"],
|
||||||
"updateUserTyping": ["user_id"],
|
"updateUserTyping": ["user_id"],
|
||||||
"updateWebViewResultSent": ["bot_id"],
|
|
||||||
"user": ["id"],
|
"user": ["id"],
|
||||||
"userEmpty": ["id"],
|
"userEmpty": ["id"],
|
||||||
"userFull": ["id"],
|
"userFull": ["id"],
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -45,7 +45,11 @@ const README_MD_FILE = join(__dirname, '../README.md')
|
||||||
const PACKAGE_JSON_FILE = join(__dirname, '../package.json')
|
const PACKAGE_JSON_FILE = join(__dirname, '../package.json')
|
||||||
|
|
||||||
function tlToFullSchema(tl: string): TlFullSchema {
|
function tlToFullSchema(tl: string): TlFullSchema {
|
||||||
return parseFullTlSchema(parseTlToEntries(tl))
|
return parseFullTlSchema(
|
||||||
|
parseTlToEntries(tl, {
|
||||||
|
parseMethodTypes: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Schema {
|
interface Schema {
|
||||||
|
|
|
@ -34,21 +34,10 @@ async function main() {
|
||||||
'mt_message',
|
'mt_message',
|
||||||
'mt_msg_copy',
|
'mt_msg_copy',
|
||||||
'mt_gzip_packed',
|
'mt_gzip_packed',
|
||||||
|
'mt_rpc_result',
|
||||||
].indexOf(it.name) === -1,
|
].indexOf(it.name) === -1,
|
||||||
)
|
)
|
||||||
|
|
||||||
const rpcResult = entries.find((it) => it.name === 'mt_rpc_result')
|
|
||||||
|
|
||||||
if (!rpcResult) {
|
|
||||||
throw new Error('mt_rpc_result not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcResult.arguments.forEach((arg) => {
|
|
||||||
if (arg.name === 'result') {
|
|
||||||
arg.type = 'any'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// mtproto is handled internally, for simplicity we make them all classes
|
// mtproto is handled internally, for simplicity we make them all classes
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
entry.kind = 'class'
|
entry.kind = 'class'
|
||||||
|
|
|
@ -59,6 +59,7 @@ async function generateReaders(
|
||||||
let code = generateReaderCodeForTlEntries(apiSchema.entries, {
|
let code = generateReaderCodeForTlEntries(apiSchema.entries, {
|
||||||
variableName: 'm',
|
variableName: 'm',
|
||||||
includeMethods: false,
|
includeMethods: false,
|
||||||
|
includeMethodResults: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const mtpCode = generateReaderCodeForTlEntries(mtpSchema.entries, {
|
const mtpCode = generateReaderCodeForTlEntries(mtpSchema.entries, {
|
||||||
|
|
Loading…
Reference in a new issue