diff options
| author | Darius Jahandarie <djahandarie@gmail.com> | 2023-12-06 03:53:16 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-06 03:53:16 +0000 | 
| commit | bd5bc1a5db29903bc098995cd9262c4576bf76af (patch) | |
| tree | c9214189e0214480fcf6539ad1c6327aef6cbd1c /ext/js/comm/api.js | |
| parent | fd6bba8a2a869eaf2b2c1fa49001f933fce3c618 (diff) | |
| parent | 23e6fb76319c9ed7c9bcdc3efba39bc5dd38f288 (diff) | |
Merge pull request #339 from toasted-nutbread/type-annotations
Type annotations
Diffstat (limited to 'ext/js/comm/api.js')
| -rw-r--r-- | ext/js/comm/api.js | 356 | 
1 files changed, 309 insertions, 47 deletions
| diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index 05f95464..26218595 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -16,184 +16,428 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -import {deferPromise, deserializeError, isObject} from '../core.js'; +import {deferPromise} from '../core.js'; +import {ExtensionError} from '../core/extension-error.js';  export class API { +    /** +     * @param {import('../yomitan.js').Yomitan} yomitan +     */      constructor(yomitan) { +        /** @type {import('../yomitan.js').Yomitan} */          this._yomitan = yomitan;      } +    /** +     * @param {import('api').OptionsGetDetails['optionsContext']} optionsContext +     * @returns {Promise<import('api').OptionsGetResult>} +     */      optionsGet(optionsContext) { -        return this._invoke('optionsGet', {optionsContext}); +        /** @type {import('api').OptionsGetDetails} */ +        const details = {optionsContext}; +        return this._invoke('optionsGet', details);      } +    /** +     * @returns {Promise<import('api').OptionsGetFullResult>} +     */      optionsGetFull() {          return this._invoke('optionsGetFull');      } +    /** +     * @param {import('api').TermsFindDetails['text']} text +     * @param {import('api').TermsFindDetails['details']} details +     * @param {import('api').TermsFindDetails['optionsContext']} optionsContext +     * @returns {Promise<import('api').TermsFindResult>} +     */      termsFind(text, details, optionsContext) { -        return this._invoke('termsFind', {text, details, optionsContext}); -    } - +        /** @type {import('api').TermsFindDetails} */ +        const details2 = {text, details, optionsContext}; +        return this._invoke('termsFind', details2); +    } + +    /** +     * @param {import('api').ParseTextDetails['text']} text +     * @param {import('api').ParseTextDetails['optionsContext']} optionsContext +     * @param {import('api').ParseTextDetails['scanLength']} scanLength +     * @param {import('api').ParseTextDetails['useInternalParser']} useInternalParser +     * @param {import('api').ParseTextDetails['useMecabParser']} useMecabParser +     * @returns {Promise<import('api').ParseTextResult>} +     */      parseText(text, optionsContext, scanLength, useInternalParser, useMecabParser) { -        return this._invoke('parseText', {text, optionsContext, scanLength, useInternalParser, useMecabParser}); +        /** @type {import('api').ParseTextDetails} */ +        const details = {text, optionsContext, scanLength, useInternalParser, useMecabParser}; +        return this._invoke('parseText', details);      } +    /** +     * @param {import('api').KanjiFindDetails['text']} text +     * @param {import('api').KanjiFindDetails['optionsContext']} optionsContext +     * @returns {Promise<import('api').KanjiFindResult>} +     */      kanjiFind(text, optionsContext) { -        return this._invoke('kanjiFind', {text, optionsContext}); +        /** @type {import('api').KanjiFindDetails} */ +        const details = {text, optionsContext}; +        return this._invoke('kanjiFind', details);      } +    /** +     * @returns {Promise<import('api').IsAnkiConnectedResult>} +     */      isAnkiConnected() {          return this._invoke('isAnkiConnected');      } +    /** +     * @returns {Promise<import('api').GetAnkiConnectVersionResult>} +     */      getAnkiConnectVersion() {          return this._invoke('getAnkiConnectVersion');      } +    /** +     * @param {import('api').AddAnkiNoteDetails['note']} note +     * @returns {Promise<import('api').AddAnkiNoteResult>} +     */      addAnkiNote(note) { -        return this._invoke('addAnkiNote', {note}); +        /** @type {import('api').AddAnkiNoteDetails} */ +        const details = {note}; +        return this._invoke('addAnkiNote', details);      } +    /** +     * @param {import('api').GetAnkiNoteInfoDetails['notes']} notes +     * @param {import('api').GetAnkiNoteInfoDetails['fetchAdditionalInfo']} fetchAdditionalInfo +     * @returns {Promise<import('api').GetAnkiNoteInfoResult>} +     */      getAnkiNoteInfo(notes, fetchAdditionalInfo) { -        return this._invoke('getAnkiNoteInfo', {notes, fetchAdditionalInfo}); -    } - +        /** @type {import('api').GetAnkiNoteInfoDetails} */ +        const details = {notes, fetchAdditionalInfo}; +        return this._invoke('getAnkiNoteInfo', details); +    } + +    /** +     * @param {import('api').InjectAnkiNoteMediaDetails['timestamp']} timestamp +     * @param {import('api').InjectAnkiNoteMediaDetails['definitionDetails']} definitionDetails +     * @param {import('api').InjectAnkiNoteMediaDetails['audioDetails']} audioDetails +     * @param {import('api').InjectAnkiNoteMediaDetails['screenshotDetails']} screenshotDetails +     * @param {import('api').InjectAnkiNoteMediaDetails['clipboardDetails']} clipboardDetails +     * @param {import('api').InjectAnkiNoteMediaDetails['dictionaryMediaDetails']} dictionaryMediaDetails +     * @returns {Promise<import('api').InjectAnkiNoteMediaResult>} +     */      injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails) { -        return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails}); +        /** @type {import('api').InjectAnkiNoteMediaDetails} */ +        const details = {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails}; +        return this._invoke('injectAnkiNoteMedia', details);      } +    /** +     * @param {import('api').NoteViewDetails['noteId']} noteId +     * @param {import('api').NoteViewDetails['mode']} mode +     * @param {import('api').NoteViewDetails['allowFallback']} allowFallback +     * @returns {Promise<import('api').NoteViewResult>} +     */      noteView(noteId, mode, allowFallback) { -        return this._invoke('noteView', {noteId, mode, allowFallback}); +        /** @type {import('api').NoteViewDetails} */ +        const details = {noteId, mode, allowFallback}; +        return this._invoke('noteView', details);      } +    /** +     * @param {import('api').SuspendAnkiCardsForNoteDetails['noteId']} noteId +     * @returns {Promise<import('api').SuspendAnkiCardsForNoteResult>} +     */      suspendAnkiCardsForNote(noteId) { -        return this._invoke('suspendAnkiCardsForNote', {noteId}); +        /** @type {import('api').SuspendAnkiCardsForNoteDetails} */ +        const details = {noteId}; +        return this._invoke('suspendAnkiCardsForNote', details);      } +    /** +     * @param {import('api').GetTermAudioInfoListDetails['source']} source +     * @param {import('api').GetTermAudioInfoListDetails['term']} term +     * @param {import('api').GetTermAudioInfoListDetails['reading']} reading +     * @returns {Promise<import('api').GetTermAudioInfoListResult>} +     */      getTermAudioInfoList(source, term, reading) { -        return this._invoke('getTermAudioInfoList', {source, term, reading}); +        /** @type {import('api').GetTermAudioInfoListDetails} */ +        const details = {source, term, reading}; +        return this._invoke('getTermAudioInfoList', details);      } +    /** +     * @param {import('api').CommandExecDetails['command']} command +     * @param {import('api').CommandExecDetails['params']} [params] +     * @returns {Promise<import('api').CommandExecResult>} +     */      commandExec(command, params) { -        return this._invoke('commandExec', {command, params}); +        /** @type {import('api').CommandExecDetails} */ +        const details = {command, params}; +        return this._invoke('commandExec', details);      } +    /** +     * @param {import('api').SendMessageToFrameDetails['frameId']} frameId +     * @param {import('api').SendMessageToFrameDetails['action']} action +     * @param {import('api').SendMessageToFrameDetails['params']} [params] +     * @returns {Promise<import('api').SendMessageToFrameResult>} +     */      sendMessageToFrame(frameId, action, params) { -        return this._invoke('sendMessageToFrame', {frameId, action, params}); +        /** @type {import('api').SendMessageToFrameDetails} */ +        const details = {frameId, action, params}; +        return this._invoke('sendMessageToFrame', details);      } +    /** +     * @param {import('api').BroadcastTabDetails['action']} action +     * @param {import('api').BroadcastTabDetails['params']} params +     * @returns {Promise<import('api').BroadcastTabResult>} +     */      broadcastTab(action, params) { -        return this._invoke('broadcastTab', {action, params}); +        /** @type {import('api').BroadcastTabDetails} */ +        const details = {action, params}; +        return this._invoke('broadcastTab', details);      } +    /** +     * @returns {Promise<import('api').FrameInformationGetResult>} +     */      frameInformationGet() {          return this._invoke('frameInformationGet');      } +    /** +     * @param {import('api').InjectStylesheetDetails['type']} type +     * @param {import('api').InjectStylesheetDetails['value']} value +     * @returns {Promise<import('api').InjectStylesheetResult>} +     */      injectStylesheet(type, value) { -        return this._invoke('injectStylesheet', {type, value}); +        /** @type {import('api').InjectStylesheetDetails} */ +        const details = {type, value}; +        return this._invoke('injectStylesheet', details);      } +    /** +     * @param {import('api').GetStylesheetContentDetails['url']} url +     * @returns {Promise<import('api').GetStylesheetContentResult>} +     */      getStylesheetContent(url) { -        return this._invoke('getStylesheetContent', {url}); +        /** @type {import('api').GetStylesheetContentDetails} */ +        const details = {url}; +        return this._invoke('getStylesheetContent', details);      } +    /** +     * @returns {Promise<import('api').GetEnvironmentInfoResult>} +     */      getEnvironmentInfo() {          return this._invoke('getEnvironmentInfo');      } +    /** +     * @returns {Promise<import('api').ClipboardGetResult>} +     */      clipboardGet() {          return this._invoke('clipboardGet');      } +    /** +     * @returns {Promise<import('api').GetDisplayTemplatesHtmlResult>} +     */      getDisplayTemplatesHtml() {          return this._invoke('getDisplayTemplatesHtml');      } +    /** +     * @returns {Promise<import('api').GetZoomResult>} +     */      getZoom() {          return this._invoke('getZoom');      } +    /** +     * @returns {Promise<import('api').GetDefaultAnkiFieldTemplatesResult>} +     */      getDefaultAnkiFieldTemplates() {          return this._invoke('getDefaultAnkiFieldTemplates');      } +    /** +     * @returns {Promise<import('api').GetDictionaryInfoResult>} +     */      getDictionaryInfo() {          return this._invoke('getDictionaryInfo');      } +    /** +     * @returns {Promise<import('api').PurgeDatabaseResult>} +     */      purgeDatabase() {          return this._invoke('purgeDatabase');      } +    /** +     * @param {import('api').GetMediaDetails['targets']} targets +     * @returns {Promise<import('api').GetMediaResult>} +     */      getMedia(targets) { -        return this._invoke('getMedia', {targets}); +        /** @type {import('api').GetMediaDetails} */ +        const details = {targets}; +        return this._invoke('getMedia', details);      } +    /** +     * @param {import('api').LogDetails['error']} error +     * @param {import('api').LogDetails['level']} level +     * @param {import('api').LogDetails['context']} context +     * @returns {Promise<import('api').LogResult>} +     */      log(error, level, context) { -        return this._invoke('log', {error, level, context}); +        /** @type {import('api').LogDetails} */ +        const details = {error, level, context}; +        return this._invoke('log', details);      } +    /** +     * @returns {Promise<import('api').LogIndicatorClearResult>} +     */      logIndicatorClear() {          return this._invoke('logIndicatorClear');      } +    /** +     * @param {import('api').ModifySettingsDetails['targets']} targets +     * @param {import('api').ModifySettingsDetails['source']} source +     * @returns {Promise<import('api').ModifySettingsResult>} +     */      modifySettings(targets, source) { -        return this._invoke('modifySettings', {targets, source}); +        const details = {targets, source}; +        return this._invoke('modifySettings', details);      } +    /** +     * @param {import('api').GetSettingsDetails['targets']} targets +     * @returns {Promise<import('api').GetSettingsResult>} +     */      getSettings(targets) { -        return this._invoke('getSettings', {targets}); +        /** @type {import('api').GetSettingsDetails} */ +        const details = {targets}; +        return this._invoke('getSettings', details);      } +    /** +     * @param {import('api').SetAllSettingsDetails['value']} value +     * @param {import('api').SetAllSettingsDetails['source']} source +     * @returns {Promise<import('api').SetAllSettingsResult>} +     */      setAllSettings(value, source) { -        return this._invoke('setAllSettings', {value, source}); +        /** @type {import('api').SetAllSettingsDetails} */ +        const details = {value, source}; +        return this._invoke('setAllSettings', details);      } +    /** +     * @param {import('api').GetOrCreateSearchPopupDetails} details +     * @returns {Promise<import('api').GetOrCreateSearchPopupResult>} +     */      getOrCreateSearchPopup(details) { -        return this._invoke('getOrCreateSearchPopup', isObject(details) ? details : {}); +        return this._invoke('getOrCreateSearchPopup', details);      } +    /** +     * @param {import('api').IsTabSearchPopupDetails['tabId']} tabId +     * @returns {Promise<import('api').IsTabSearchPopupResult>} +     */      isTabSearchPopup(tabId) { -        return this._invoke('isTabSearchPopup', {tabId}); +        /** @type {import('api').IsTabSearchPopupDetails} */ +        const details = {tabId}; +        return this._invoke('isTabSearchPopup', details);      } +    /** +     * @param {import('api').TriggerDatabaseUpdatedDetails['type']} type +     * @param {import('api').TriggerDatabaseUpdatedDetails['cause']} cause +     * @returns {Promise<import('api').TriggerDatabaseUpdatedResult>} +     */      triggerDatabaseUpdated(type, cause) { -        return this._invoke('triggerDatabaseUpdated', {type, cause}); +        /** @type {import('api').TriggerDatabaseUpdatedDetails} */ +        const details = {type, cause}; +        return this._invoke('triggerDatabaseUpdated', details);      } +    /** +     * @returns {Promise<import('api').TestMecabResult>} +     */      testMecab() { -        return this._invoke('testMecab', {}); +        return this._invoke('testMecab');      } +    /** +     * @param {import('api').TextHasJapaneseCharactersDetails['text']} text +     * @returns {Promise<import('api').TextHasJapaneseCharactersResult>} +     */      textHasJapaneseCharacters(text) { -        return this._invoke('textHasJapaneseCharacters', {text}); +        /** @type {import('api').TextHasJapaneseCharactersDetails} */ +        const details = {text}; +        return this._invoke('textHasJapaneseCharacters', details);      } +    /** +     * @param {import('api').GetTermFrequenciesDetails['termReadingList']} termReadingList +     * @param {import('api').GetTermFrequenciesDetails['dictionaries']} dictionaries +     * @returns {Promise<import('api').GetTermFrequenciesResult>} +     */      getTermFrequencies(termReadingList, dictionaries) { -        return this._invoke('getTermFrequencies', {termReadingList, dictionaries}); +        /** @type {import('api').GetTermFrequenciesDetails} */ +        const details = {termReadingList, dictionaries}; +        return this._invoke('getTermFrequencies', details);      } +    /** +     * @param {import('api').FindAnkiNotesDetails['query']} query +     * @returns {Promise<import('api').FindAnkiNotesResult>} +     */      findAnkiNotes(query) { -        return this._invoke('findAnkiNotes', {query}); +        /** @type {import('api').FindAnkiNotesDetails} */ +        const details = {query}; +        return this._invoke('findAnkiNotes', details);      } +    /** +     * @param {import('api').LoadExtensionScriptsDetails['files']} files +     * @returns {Promise<import('api').LoadExtensionScriptsResult>} +     */      loadExtensionScripts(files) { -        return this._invoke('loadExtensionScripts', {files}); +        /** @type {import('api').LoadExtensionScriptsDetails} */ +        const details = {files}; +        return this._invoke('loadExtensionScripts', details);      } +    /** +     * @param {import('api').OpenCrossFramePortDetails['targetTabId']} targetTabId +     * @param {import('api').OpenCrossFramePortDetails['targetFrameId']} targetFrameId +     * @returns {Promise<import('api').OpenCrossFramePortResult>} +     */      openCrossFramePort(targetTabId, targetFrameId) {          return this._invoke('openCrossFramePort', {targetTabId, targetFrameId});      }      // Utilities -    _createActionPort(timeout=5000) { +    /** +     * @param {number} timeout +     * @returns {Promise<chrome.runtime.Port>} +     */ +    _createActionPort(timeout) {          return new Promise((resolve, reject) => { +            /** @type {?import('core').Timeout} */              let timer = null;              const portDetails = deferPromise(); +            /** +             * @param {chrome.runtime.Port} port +             */              const onConnect = async (port) => {                  try {                      const {name: expectedName, id: expectedId} = await portDetails.promise; @@ -210,6 +454,9 @@ export class API {                  resolve(port);              }; +            /** +             * @param {Error} e +             */              const onError = (e) => {                  if (timer !== null) {                      clearTimeout(timer); @@ -227,14 +474,24 @@ export class API {          });      } -    _invokeWithProgress(action, params, onProgress, timeout=5000) { +    /** +     * @template [TReturn=unknown] +     * @param {string} action +     * @param {import('core').SerializableObject} params +     * @param {?(...args: unknown[]) => void} onProgress0 +     * @param {number} [timeout] +     * @returns {Promise<TReturn>} +     */ +    _invokeWithProgress(action, params, onProgress0, timeout=5000) {          return new Promise((resolve, reject) => { +            /** @type {?chrome.runtime.Port} */              let port = null; -            if (typeof onProgress !== 'function') { -                onProgress = () => {}; -            } +            const onProgress = typeof onProgress0 === 'function' ? onProgress0 : () => {}; +            /** +             * @param {import('backend').InvokeWithProgressResponseMessage<TReturn>} message +             */              const onMessage = (message) => {                  switch (message.type) {                      case 'progress': @@ -250,7 +507,7 @@ export class API {                          break;                      case 'error':                          cleanup(); -                        reject(deserializeError(message.data)); +                        reject(ExtensionError.deserialize(message.data));                          break;                  }              }; @@ -267,7 +524,6 @@ export class API {                      port.disconnect();                      port = null;                  } -                onProgress = null;              };              (async () => { @@ -281,20 +537,23 @@ export class API {                      const fragmentSize = 1e7; // 10 MB                      for (let i = 0, ii = messageString.length; i < ii; i += fragmentSize) {                          const data = messageString.substring(i, i + fragmentSize); -                        port.postMessage({action: 'fragment', data}); +                        port.postMessage(/** @type {import('backend').InvokeWithProgressRequestFragmentMessage} */ ({action: 'fragment', data}));                      } -                    port.postMessage({action: 'invoke'}); +                    port.postMessage(/** @type {import('backend').InvokeWithProgressRequestInvokeMessage} */ ({action: 'invoke'}));                  } catch (e) {                      cleanup();                      reject(e); -                } finally { -                    action = null; -                    params = null;                  }              })();          });      } +    /** +     * @template [TReturn=unknown] +     * @param {string} action +     * @param {import('core').SerializableObject} [params] +     * @returns {Promise<TReturn>} +     */      _invoke(action, params={}) {          const data = {action, params};          return new Promise((resolve, reject) => { @@ -303,7 +562,7 @@ export class API {                      this._checkLastError(chrome.runtime.lastError);                      if (response !== null && typeof response === 'object') {                          if (typeof response.error !== 'undefined') { -                            reject(deserializeError(response.error)); +                            reject(ExtensionError.deserialize(response.error));                          } else {                              resolve(response.result);                          } @@ -318,7 +577,10 @@ export class API {          });      } -    _checkLastError() { +    /** +     * @param {chrome.runtime.LastError|undefined} _ignore +     */ +    _checkLastError(_ignore) {          // NOP      }  } |