import { extend, hasOwn, isString, isFunction, isPlainObject, } from '@vue/shared' import { validateProtocols } from '../protocol' import { invokeCallback, createAsyncApiCallback, onKeepAliveApiCallback, offKeepAliveApiCallback, findInvokeCallbackByName, createKeepAliveApiCallback, removeKeepAliveApiCallback, } from './callback' import type { CALLBACK_TYPES } from './callback' import { promisify } from './promise' function formatApiArgs( args: any[], options?: ApiOptions ) { const params = args[0] if ( !options || (!isPlainObject(options.formatArgs) && isPlainObject(params)) ) { return } const formatArgs = options.formatArgs! const keys = Object.keys(formatArgs) for (let i = 0; i < keys.length; i++) { const name = keys[i] const formatterOrDefaultValue = formatArgs[name]! if (isFunction(formatterOrDefaultValue)) { const errMsg = formatterOrDefaultValue(args[0][name], params) if (isString(errMsg)) { return errMsg } } else { // defaultValue if (!hasOwn(params, name)) { params[name] = formatterOrDefaultValue } } } } function invokeSuccess(id: number, name: string, res: unknown) { return invokeCallback(id, extend(res || {}, { errMsg: name + ':ok' })) } function invokeFail(id: number, name: string, errMsg?: string, errRes?: any) { return invokeCallback( id, extend({ errMsg: name + ':fail' + (errMsg ? ' ' + errMsg : '') }, errRes) ) } function beforeInvokeApi( name: string, args: any[], protocol?: ApiProtocols, options?: ApiOptions ) { if (__DEV__) { validateProtocols(name!, args, protocol) } if (options && options.beforeInvoke) { const errMsg = options.beforeInvoke(args) if (isString(errMsg)) { return errMsg } } const errMsg = formatApiArgs(args, options) if (errMsg) { return errMsg } } function checkCallback(callback: Function) { if (!isFunction(callback)) { throw new Error( 'Invalid args: type check failed for args "callback". Expected Function' ) } } function wrapperOnApi( name: string, fn: Function, options?: ApiOptions ) { return (callback: Function) => { checkCallback(callback) const errMsg = beforeInvokeApi(name, [callback], undefined, options) if (errMsg) { throw new Error(errMsg) } // 是否是首次调用on,如果是首次,需要初始化onMethod监听 const isFirstInvokeOnApi = !findInvokeCallbackByName(name) createKeepAliveApiCallback(name, callback) if (isFirstInvokeOnApi) { onKeepAliveApiCallback(name) fn() } } } function wrapperOffApi( name: string, fn: Function, options?: ApiOptions ) { return (callback: Function) => { checkCallback(callback) const errMsg = beforeInvokeApi(name, [callback], undefined, options) if (errMsg) { throw new Error(errMsg) } name = name.replace('off', 'on') removeKeepAliveApiCallback(name, callback) // 是否还存在监听,若已不存在,则移除onMethod监听 const hasInvokeOnApi = findInvokeCallbackByName(name) if (!hasInvokeOnApi) { offKeepAliveApiCallback(name) fn() } } } function normalizeErrMsg(errMsg: string | Error) { if (!errMsg || isString(errMsg)) { return errMsg } if (errMsg.stack) { console.error(errMsg.message + '\n' + errMsg.stack) return errMsg.message } return errMsg as unknown as string } function wrapperTaskApi( name: string, fn: Function, protocol?: ApiProtocols, options?: ApiOptions ) { return (args: Record) => { const id = createAsyncApiCallback(name, args, options) const errMsg = beforeInvokeApi(name, [args], protocol, options) if (errMsg) { return invokeFail(id, name, errMsg) } return fn(args, { resolve: (res: unknown) => invokeSuccess(id, name, res), reject: (errMsg: string | Error, errRes?: any) => invokeFail(id, name, normalizeErrMsg(errMsg), errRes), }) } } function wrapperSyncApi( name: string, fn: Function, protocol?: ApiProtocols, options?: ApiOptions ) { return (...args: any[]) => { const errMsg = beforeInvokeApi(name, args, protocol, options) if (errMsg) { throw new Error(errMsg) } return fn.apply(null, args) } } function wrapperAsyncApi( name: string, fn: Function, protocol?: ApiProtocols, options?: ApiOptions ) { return wrapperTaskApi(name, fn, protocol, options) } export function defineOnApi( name: string, fn: () => void, options?: ApiOptions ) { return wrapperOnApi(name, fn, options) as unknown as T } export function defineOffApi( name: string, fn: () => void, options?: ApiOptions ) { return wrapperOffApi(name, fn, options) as unknown as T } export function defineTaskApi>( name: string, fn: ( args: Omit, res: { resolve: (res?: AsyncApiRes

| void) => void reject: (err?: string) => void } ) => ReturnType, protocol?: ApiProtocols, options?: ApiOptions ) { return promisify( name, wrapperTaskApi(name, fn, __DEV__ ? protocol : undefined, options) ) as unknown as T } export function defineSyncApi( name: string, fn: T, protocol?: ApiProtocols, options?: ApiOptions ) { return wrapperSyncApi( name, fn, __DEV__ ? protocol : undefined, options ) as unknown as T } export type DefineAsyncApiFn> = ( args: P extends undefined ? undefined : Omit, res: { resolve: (res: AsyncApiRes

| void) => void reject: (errMsg?: string, errRes?: any) => void } ) => void export function defineAsyncApi>( name: string, fn: DefineAsyncApiFn, protocol?: ApiProtocols, options?: ApiOptions ) { return promisify( name, wrapperAsyncApi(name, fn as any, __DEV__ ? protocol : undefined, options) ) as AsyncApi

} function createUnsupportedMsg(name: string) { return `method 'uni.${name}' not supported` } export function createUnsupportedSyncApi(name: string) { return (): any => { console.error(createUnsupportedMsg(name)) } } export const createUnsupportedOnApi = createUnsupportedSyncApi export const createUnsupportedOffApi = createUnsupportedSyncApi export const createUnsupportedTaskApi = createUnsupportedSyncApi export function createUnsupportedAsyncApi(name: string) { return (_args: unknown, { reject }: { reject: (err?: string) => void }) => { return reject(createUnsupportedMsg(name)) } }