diff options
| -rw-r--r-- | ext/js/accessibility/google-docs.js | 11 | ||||
| -rw-r--r-- | ext/js/background/backend.js | 103 | ||||
| -rw-r--r-- | ext/js/comm/api.js | 337 | ||||
| -rw-r--r-- | ext/js/display/query-parser.js | 4 | ||||
| -rw-r--r-- | types/ext/api.d.ts | 672 | 
5 files changed, 514 insertions, 613 deletions
| diff --git a/ext/js/accessibility/google-docs.js b/ext/js/accessibility/google-docs.js index 27841b6d..4bc398ff 100644 --- a/ext/js/accessibility/google-docs.js +++ b/ext/js/accessibility/google-docs.js @@ -24,10 +24,11 @@      self.googleDocsAccessibilitySetup = true;      /** -     * @template [TReturn=unknown] -     * @param {string} action -     * @param {import('core').SerializableObject} params -     * @returns {Promise<TReturn>} +     * @template {import('api').ApiNames} TAction +     * @template {import('api').ApiParams<TAction>} TParams +     * @param {TAction} action +     * @param {TParams} params +     * @returns {Promise<import('api').ApiReturn<TAction>>}       */      const invokeApi = (action, params) => {          return new Promise((resolve, reject) => { @@ -45,7 +46,7 @@      };      const optionsContext = {depth: 0, url: location.href}; -    /** @type {import('api').OptionsGetResult} */ +    /** @type {import('api').ApiReturn<'optionsGet'>} */      let options;      try {          options = await invokeApi('optionsGet', {optionsContext}); diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index c62685b2..f7cad1e6 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -22,7 +22,8 @@ import {AnkiConnect} from '../comm/anki-connect.js';  import {ClipboardMonitor} from '../comm/clipboard-monitor.js';  import {ClipboardReader} from '../comm/clipboard-reader.js';  import {Mecab} from '../comm/mecab.js'; -import {clone, deferPromise, invokeMessageHandler, isObject, log, promiseTimeout} from '../core.js'; +import {clone, deferPromise, isObject, log, promiseTimeout} from '../core.js'; +import {createApiMap, invokeApiMapHandler} from '../core/api-map.js';  import {ExtensionError} from '../core/extension-error.js';  import {readResponseJson} from '../core/json.js';  import {AnkiUtil} from '../data/anki-util.js'; @@ -146,8 +147,8 @@ export class Backend {          this._permissionsUtil = new PermissionsUtil();          /* eslint-disable no-multi-spaces */ -        /** @type {import('core').MessageHandlerMap} */ -        this._messageHandlers = new Map(/** @type {import('core').MessageHandlerMapInit} */ ([ +        /** @type {import('api').ApiMap} */ +        this._apiMap = createApiMap([              ['requestBackendReadySignal',    this._onApiRequestBackendReadySignal.bind(this)],              ['optionsGet',                   this._onApiOptionsGet.bind(this)],              ['optionsGetFull',               this._onApiOptionsGetFull.bind(this)], @@ -190,7 +191,7 @@ export class Backend {              ['findAnkiNotes',                this._onApiFindAnkiNotes.bind(this)],              ['loadExtensionScripts',         this._onApiLoadExtensionScripts.bind(this)],              ['openCrossFramePort',           this._onApiOpenCrossFramePort.bind(this)] -        ])); +        ]);          /* eslint-enable no-multi-spaces */          /** @type {Map<string, (params?: import('core').SerializableObject) => void>} */ @@ -369,7 +370,7 @@ export class Backend {          });      } -    /** @type {import('extension').ChromeRuntimeOnMessageCallback} */ +    /** @type {import('extension').ChromeRuntimeOnMessageCallback<import('api').MessageAny>} */      _onMessageWrapper(message, sender, sendResponse) {          if (this._isPrepared) {              return this._onMessage(message, sender, sendResponse); @@ -392,15 +393,13 @@ export class Backend {      }      /** -     * @param {{action: string, params?: import('core').SerializableObject}} message +     * @param {import('api').MessageAny} message       * @param {chrome.runtime.MessageSender} sender       * @param {(response?: unknown) => void} callback       * @returns {boolean}       */      _onMessage({action, params}, sender, callback) { -        const messageHandler = this._messageHandlers.get(action); -        if (typeof messageHandler === 'undefined') { return false; } -        return invokeMessageHandler(messageHandler, params, callback, sender); +        return invokeApiMapHandler(this._apiMap, action, params, [sender], callback);      }      /** @@ -427,7 +426,7 @@ export class Backend {      // Message handlers -    /** @type {import('api').Handler<import('api').RequestBackendReadySignalDetails, import('api').RequestBackendReadySignalResult, true>} */ +    /** @type {import('api').ApiHandler<'requestBackendReadySignal'>} */      _onApiRequestBackendReadySignal(_params, sender) {          // tab ID isn't set in background (e.g. browser_action)          const data = {action: 'Yomitan.backendReady', params: {}}; @@ -443,17 +442,17 @@ export class Backend {          }      } -    /** @type {import('api').Handler<import('api').OptionsGetDetails, import('api').OptionsGetResult>} */ +    /** @type {import('api').ApiHandler<'optionsGet'>} */      _onApiOptionsGet({optionsContext}) {          return this._getProfileOptions(optionsContext, false);      } -    /** @type {import('api').Handler<import('api').OptionsGetFullDetails, import('api').OptionsGetFullResult>} */ +    /** @type {import('api').ApiHandler<'optionsGetFull'>} */      _onApiOptionsGetFull() {          return this._getOptionsFull(false);      } -    /** @type {import('api').Handler<import('api').KanjiFindDetails, import('api').KanjiFindResult>} */ +    /** @type {import('api').ApiHandler<'kanjiFind'>} */      async _onApiKanjiFind({text, optionsContext}) {          const options = this._getProfileOptions(optionsContext, false);          const {general: {maxResults}} = options; @@ -463,7 +462,7 @@ export class Backend {          return dictionaryEntries;      } -    /** @type {import('api').Handler<import('api').TermsFindDetails, import('api').TermsFindResult>} */ +    /** @type {import('api').ApiHandler<'termsFind'>} */      async _onApiTermsFind({text, details, optionsContext}) {          const options = this._getProfileOptions(optionsContext, false);          const {general: {resultOutputMode: mode, maxResults}} = options; @@ -473,7 +472,7 @@ export class Backend {          return {dictionaryEntries, originalTextLength};      } -    /** @type {import('api').Handler<import('api').ParseTextDetails, import('api').ParseTextResult>} */ +    /** @type {import('api').ApiHandler<'parseText'>} */      async _onApiParseText({text, optionsContext, scanLength, useInternalParser, useMecabParser}) {          const [internalResults, mecabResults] = await Promise.all([              (useInternalParser ? this._textParseScanning(text, scanLength, optionsContext) : null), @@ -506,22 +505,22 @@ export class Backend {          return results;      } -    /** @type {import('api').Handler<import('api').GetAnkiConnectVersionDetails, import('api').GetAnkiConnectVersionResult>} */ +    /** @type {import('api').ApiHandler<'getAnkiConnectVersion'>} */      async _onApiGetAnkiConnectVersion() {          return await this._anki.getVersion();      } -    /** @type {import('api').Handler<import('api').IsAnkiConnectedDetails, import('api').IsAnkiConnectedResult>} */ +    /** @type {import('api').ApiHandler<'isAnkiConnected'>} */      async _onApiIsAnkiConnected() {          return await this._anki.isConnected();      } -    /** @type {import('api').Handler<import('api').AddAnkiNoteDetails, import('api').AddAnkiNoteResult>} */ +    /** @type {import('api').ApiHandler<'addAnkiNote'>} */      async _onApiAddAnkiNote({note}) {          return await this._anki.addNote(note);      } -    /** @type {import('api').Handler<import('api').GetAnkiNoteInfoDetails, import('api').GetAnkiNoteInfoResult>} */ +    /** @type {import('api').ApiHandler<'getAnkiNoteInfo'>} */      async _onApiGetAnkiNoteInfo({notes, fetchAdditionalInfo}) {          /** @type {import('anki').NoteInfoWrapper[]} */          const results = []; @@ -558,7 +557,7 @@ export class Backend {          return results;      } -    /** @type {import('api').Handler<import('api').InjectAnkiNoteMediaDetails, import('api').InjectAnkiNoteMediaResult>} */ +    /** @type {import('api').ApiHandler<'injectAnkiNoteMedia'>} */      async _onApiInjectAnkiNoteMedia({timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails}) {          return await this._injectAnkNoteMedia(              this._anki, @@ -571,7 +570,7 @@ export class Backend {          );      } -    /** @type {import('api').Handler<import('api').NoteViewDetails, import('api').NoteViewResult>} */ +    /** @type {import('api').ApiHandler<'noteView'>} */      async _onApiNoteView({noteId, mode, allowFallback}) {          if (mode === 'edit') {              try { @@ -590,7 +589,7 @@ export class Backend {          return 'browse';      } -    /** @type {import('api').Handler<import('api').SuspendAnkiCardsForNoteDetails, import('api').SuspendAnkiCardsForNoteResult>} */ +    /** @type {import('api').ApiHandler<'suspendAnkiCardsForNote'>} */      async _onApiSuspendAnkiCardsForNote({noteId}) {          const cardIds = await this._anki.findCardsForNote(noteId);          const count = cardIds.length; @@ -601,17 +600,17 @@ export class Backend {          return count;      } -    /** @type {import('api').Handler<import('api').CommandExecDetails, import('api').CommandExecResult>} */ +    /** @type {import('api').ApiHandler<'commandExec'>} */      _onApiCommandExec({command, params}) {          return this._runCommand(command, params);      } -    /** @type {import('api').Handler<import('api').GetTermAudioInfoListDetails, import('api').GetTermAudioInfoListResult>} */ +    /** @type {import('api').ApiHandler<'getTermAudioInfoList'>} */      async _onApiGetTermAudioInfoList({source, term, reading}) {          return await this._audioDownloader.getTermAudioInfoList(source, term, reading);      } -    /** @type {import('api').Handler<import('api').SendMessageToFrameDetails, import('api').SendMessageToFrameResult, true>} */ +    /** @type {import('api').ApiHandler<'sendMessageToFrame'>} */      _onApiSendMessageToFrame({frameId: targetFrameId, action, params}, sender) {          if (!sender) { return false; }          const {tab} = sender; @@ -625,7 +624,7 @@ export class Backend {          return true;      } -    /** @type {import('api').Handler<import('api').BroadcastTabDetails, import('api').BroadcastTabResult, true>} */ +    /** @type {import('api').ApiHandler<'broadcastTab'>} */      _onApiBroadcastTab({action, params}, sender) {          if (!sender) { return false; }          const {tab} = sender; @@ -639,7 +638,7 @@ export class Backend {          return true;      } -    /** @type {import('api').Handler<import('api').FrameInformationGetDetails, import('api').FrameInformationGetResult, true>} */ +    /** @type {import('api').ApiHandler<'frameInformationGet'>} */      _onApiFrameInformationGet(_params, sender) {          const tab = sender.tab;          const tabId = tab ? tab.id : void 0; @@ -647,14 +646,14 @@ export class Backend {          return Promise.resolve({tabId, frameId});      } -    /** @type {import('api').Handler<import('api').InjectStylesheetDetails, import('api').InjectStylesheetResult, true>} */ +    /** @type {import('api').ApiHandler<'injectStylesheet'>} */      async _onApiInjectStylesheet({type, value}, sender) {          const {frameId, tab} = sender;          if (typeof tab !== 'object' || tab === null || typeof tab.id !== 'number') { throw new Error('Invalid tab'); }          return await this._scriptManager.injectStylesheet(type, value, tab.id, frameId, false);      } -    /** @type {import('api').Handler<import('api').GetStylesheetContentDetails, import('api').GetStylesheetContentResult>} */ +    /** @type {import('api').ApiHandler<'getStylesheetContent'>} */      async _onApiGetStylesheetContent({url}) {          if (!url.startsWith('/') || url.startsWith('//') || !url.endsWith('.css')) {              throw new Error('Invalid URL'); @@ -662,22 +661,22 @@ export class Backend {          return await this._fetchText(url);      } -    /** @type {import('api').Handler<import('api').GetEnvironmentInfoDetails, import('api').GetEnvironmentInfoResult>} */ +    /** @type {import('api').ApiHandler<'getEnvironmentInfo'>} */      _onApiGetEnvironmentInfo() {          return this._environment.getInfo();      } -    /** @type {import('api').Handler<import('api').ClipboardGetDetails, import('api').ClipboardGetResult>} */ +    /** @type {import('api').ApiHandler<'clipboardGet'>} */      async _onApiClipboardGet() {          return this._clipboardReader.getText(false);      } -    /** @type {import('api').Handler<import('api').GetDisplayTemplatesHtmlDetails, import('api').GetDisplayTemplatesHtmlResult>} */ +    /** @type {import('api').ApiHandler<'getDisplayTemplatesHtml'>} */      async _onApiGetDisplayTemplatesHtml() {          return await this._fetchText('/display-templates.html');      } -    /** @type {import('api').Handler<import('api').GetZoomDetails, import('api').GetZoomResult, true>} */ +    /** @type {import('api').ApiHandler<'getZoom'>} */      _onApiGetZoom(_params, sender) {          return new Promise((resolve, reject) => {              if (!sender || !sender.tab) { @@ -707,45 +706,45 @@ export class Backend {          });      } -    /** @type {import('api').Handler<import('api').GetDefaultAnkiFieldTemplatesDetails, import('api').GetDefaultAnkiFieldTemplatesResult>} */ +    /** @type {import('api').ApiHandler<'getDefaultAnkiFieldTemplates'>} */      _onApiGetDefaultAnkiFieldTemplates() {          return /** @type {string} */ (this._defaultAnkiFieldTemplates);      } -    /** @type {import('api').Handler<import('api').GetDictionaryInfoDetails, import('api').GetDictionaryInfoResult>} */ +    /** @type {import('api').ApiHandler<'getDictionaryInfo'>} */      async _onApiGetDictionaryInfo() {          return await this._dictionaryDatabase.getDictionaryInfo();      } -    /** @type {import('api').Handler<import('api').PurgeDatabaseDetails, import('api').PurgeDatabaseResult>} */ +    /** @type {import('api').ApiHandler<'purgeDatabase'>} */      async _onApiPurgeDatabase() {          await this._dictionaryDatabase.purge();          this._triggerDatabaseUpdated('dictionary', 'purge');      } -    /** @type {import('api').Handler<import('api').GetMediaDetails, import('api').GetMediaResult>} */ +    /** @type {import('api').ApiHandler<'getMedia'>} */      async _onApiGetMedia({targets}) {          return await this._getNormalizedDictionaryDatabaseMedia(targets);      } -    /** @type {import('api').Handler<import('api').LogDetails, import('api').LogResult>} */ +    /** @type {import('api').ApiHandler<'log'>} */      _onApiLog({error, level, context}) {          log.log(ExtensionError.deserialize(error), level, context);      } -    /** @type {import('api').Handler<import('api').LogIndicatorClearDetails, import('api').LogIndicatorClearResult>} */ +    /** @type {import('api').ApiHandler<'logIndicatorClear'>} */      _onApiLogIndicatorClear() {          if (this._logErrorLevel === null) { return; }          this._logErrorLevel = null;          this._updateBadge();      } -    /** @type {import('api').Handler<import('api').ModifySettingsDetails, import('api').ModifySettingsResult>} */ +    /** @type {import('api').ApiHandler<'modifySettings'>} */      _onApiModifySettings({targets, source}) {          return this._modifySettings(targets, source);      } -    /** @type {import('api').Handler<import('api').GetSettingsDetails, import('api').GetSettingsResult>} */ +    /** @type {import('api').ApiHandler<'getSettings'>} */      _onApiGetSettings({targets}) {          const results = [];          for (const target of targets) { @@ -759,14 +758,14 @@ export class Backend {          return results;      } -    /** @type {import('api').Handler<import('api').SetAllSettingsDetails, import('api').SetAllSettingsResult>} */ +    /** @type {import('api').ApiHandler<'setAllSettings'>} */      async _onApiSetAllSettings({value, source}) {          this._optionsUtil.validate(value);          this._options = clone(value);          await this._saveOptions(source);      } -    /** @type {import('api').Handler<import('api').GetOrCreateSearchPopupDetails, import('api').GetOrCreateSearchPopupResult>} */ +    /** @type {import('api').ApiHandlerNoExtraArgs<'getOrCreateSearchPopup'>} */      async _onApiGetOrCreateSearchPopup({focus = false, text}) {          const {tab, created} = await this._getOrCreateSearchPopupWrapper();          if (focus === true || (focus === 'ifCreated' && created)) { @@ -782,19 +781,19 @@ export class Backend {          return {tabId: typeof id === 'number' ? id : null, windowId: tab.windowId};      } -    /** @type {import('api').Handler<import('api').IsTabSearchPopupDetails, import('api').IsTabSearchPopupResult>} */ +    /** @type {import('api').ApiHandler<'isTabSearchPopup'>} */      async _onApiIsTabSearchPopup({tabId}) {          const baseUrl = chrome.runtime.getURL('/search.html');          const tab = typeof tabId === 'number' ? await this._checkTabUrl(tabId, (url) => url !== null && url.startsWith(baseUrl)) : null;          return (tab !== null);      } -    /** @type {import('api').Handler<import('api').TriggerDatabaseUpdatedDetails, import('api').TriggerDatabaseUpdatedResult>} */ +    /** @type {import('api').ApiHandler<'triggerDatabaseUpdated'>} */      _onApiTriggerDatabaseUpdated({type, cause}) {          this._triggerDatabaseUpdated(type, cause);      } -    /** @type {import('api').Handler<import('api').TestMecabDetails, import('api').TestMecabResult>} */ +    /** @type {import('api').ApiHandler<'testMecab'>} */      async _onApiTestMecab() {          if (!this._mecab.isEnabled()) {              throw new Error('MeCab not enabled'); @@ -831,22 +830,22 @@ export class Backend {          return true;      } -    /** @type {import('api').Handler<import('api').TextHasJapaneseCharactersDetails, import('api').TextHasJapaneseCharactersResult>} */ +    /** @type {import('api').ApiHandler<'textHasJapaneseCharacters'>} */      _onApiTextHasJapaneseCharacters({text}) {          return this._japaneseUtil.isStringPartiallyJapanese(text);      } -    /** @type {import('api').Handler<import('api').GetTermFrequenciesDetails, import('api').GetTermFrequenciesResult>} */ +    /** @type {import('api').ApiHandler<'getTermFrequencies'>} */      async _onApiGetTermFrequencies({termReadingList, dictionaries}) {          return await this._translator.getTermFrequencies(termReadingList, dictionaries);      } -    /** @type {import('api').Handler<import('api').FindAnkiNotesDetails, import('api').FindAnkiNotesResult>} */ +    /** @type {import('api').ApiHandler<'findAnkiNotes'>} */      async _onApiFindAnkiNotes({query}) {          return await this._anki.findNotes(query);      } -    /** @type {import('api').Handler<import('api').LoadExtensionScriptsDetails, import('api').LoadExtensionScriptsResult, true>} */ +    /** @type {import('api').ApiHandler<'loadExtensionScripts'>} */      async _onApiLoadExtensionScripts({files}, sender) {          if (!sender || !sender.tab) { throw new Error('Invalid sender'); }          const tabId = sender.tab.id; @@ -857,7 +856,7 @@ export class Backend {          }      } -    /** @type {import('api').Handler<import('api').OpenCrossFramePortDetails, import('api').OpenCrossFramePortResult, true>} */ +    /** @type {import('api').ApiHandler<'openCrossFramePort'>} */      _onApiOpenCrossFramePort({targetTabId, targetFrameId}, sender) {          const sourceTabId = (sender && sender.tab ? sender.tab.id : null);          if (typeof sourceTabId !== 'number') { @@ -2062,7 +2061,7 @@ export class Backend {       * @param {?import('api').InjectAnkiNoteMediaScreenshotDetails} screenshotDetails       * @param {?import('api').InjectAnkiNoteMediaClipboardDetails} clipboardDetails       * @param {import('api').InjectAnkiNoteMediaDictionaryMediaDetails[]} dictionaryMediaDetails -     * @returns {Promise<import('api').InjectAnkiNoteMediaResult>} +     * @returns {Promise<import('api').ApiReturn<'injectAnkiNoteMedia'>>}       */      async _injectAnkNoteMedia(ankiConnect, timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails) {          let screenshotFileName = null; diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index de19650d..f2d4d545 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -28,395 +28,344 @@ export class API {      }      /** -     * @param {import('api').OptionsGetDetails['optionsContext']} optionsContext -     * @returns {Promise<import('api').OptionsGetResult>} +     * @param {import('api').ApiParam<'optionsGet', 'optionsContext'>} optionsContext +     * @returns {Promise<import('api').ApiReturn<'optionsGet'>>}       */      optionsGet(optionsContext) { -        /** @type {import('api').OptionsGetDetails} */ -        const details = {optionsContext}; -        return this._invoke('optionsGet', details); +        return this._invoke('optionsGet', {optionsContext});      }      /** -     * @returns {Promise<import('api').OptionsGetFullResult>} +     * @returns {Promise<import('api').ApiReturn<'optionsGetFull'>>}       */      optionsGetFull() { -        return this._invoke('optionsGetFull'); +        return this._invoke('optionsGetFull', void 0);      }      /** -     * @param {import('api').TermsFindDetails['text']} text -     * @param {import('api').TermsFindDetails['details']} details -     * @param {import('api').TermsFindDetails['optionsContext']} optionsContext -     * @returns {Promise<import('api').TermsFindResult>} +     * @param {import('api').ApiParam<'termsFind', 'text'>} text +     * @param {import('api').ApiParam<'termsFind', 'details'>} details +     * @param {import('api').ApiParam<'termsFind', 'optionsContext'>} optionsContext +     * @returns {Promise<import('api').ApiReturn<'termsFind'>>}       */      termsFind(text, details, optionsContext) { -        /** @type {import('api').TermsFindDetails} */ -        const details2 = {text, details, optionsContext}; -        return this._invoke('termsFind', details2); +        return this._invoke('termsFind', {text, details, optionsContext});      }      /** -     * @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>} +     * @param {import('api').ApiParam<'parseText', 'text'>} text +     * @param {import('api').ApiParam<'parseText', 'optionsContext'>} optionsContext +     * @param {import('api').ApiParam<'parseText', 'scanLength'>} scanLength +     * @param {import('api').ApiParam<'parseText', 'useInternalParser'>} useInternalParser +     * @param {import('api').ApiParam<'parseText', 'useMecabParser'>} useMecabParser +     * @returns {Promise<import('api').ApiReturn<'parseText'>>}       */      parseText(text, optionsContext, scanLength, useInternalParser, useMecabParser) { -        /** @type {import('api').ParseTextDetails} */ -        const details = {text, optionsContext, scanLength, useInternalParser, useMecabParser}; -        return this._invoke('parseText', details); +        return this._invoke('parseText', {text, optionsContext, scanLength, useInternalParser, useMecabParser});      }      /** -     * @param {import('api').KanjiFindDetails['text']} text -     * @param {import('api').KanjiFindDetails['optionsContext']} optionsContext -     * @returns {Promise<import('api').KanjiFindResult>} +     * @param {import('api').ApiParam<'kanjiFind', 'text'>} text +     * @param {import('api').ApiParam<'kanjiFind', 'optionsContext'>} optionsContext +     * @returns {Promise<import('api').ApiReturn<'kanjiFind'>>}       */      kanjiFind(text, optionsContext) { -        /** @type {import('api').KanjiFindDetails} */ -        const details = {text, optionsContext}; -        return this._invoke('kanjiFind', details); +        return this._invoke('kanjiFind', {text, optionsContext});      }      /** -     * @returns {Promise<import('api').IsAnkiConnectedResult>} +     * @returns {Promise<import('api').ApiReturn<'isAnkiConnected'>>}       */      isAnkiConnected() { -        return this._invoke('isAnkiConnected'); +        return this._invoke('isAnkiConnected', void 0);      }      /** -     * @returns {Promise<import('api').GetAnkiConnectVersionResult>} +     * @returns {Promise<import('api').ApiReturn<'getAnkiConnectVersion'>>}       */      getAnkiConnectVersion() { -        return this._invoke('getAnkiConnectVersion'); +        return this._invoke('getAnkiConnectVersion', void 0);      }      /** -     * @param {import('api').AddAnkiNoteDetails['note']} note -     * @returns {Promise<import('api').AddAnkiNoteResult>} +     * @param {import('api').ApiParam<'addAnkiNote', 'note'>} note +     * @returns {Promise<import('api').ApiReturn<'addAnkiNote'>>}       */      addAnkiNote(note) { -        /** @type {import('api').AddAnkiNoteDetails} */ -        const details = {note}; -        return this._invoke('addAnkiNote', details); +        return this._invoke('addAnkiNote', {note});      }      /** -     * @param {import('api').GetAnkiNoteInfoDetails['notes']} notes -     * @param {import('api').GetAnkiNoteInfoDetails['fetchAdditionalInfo']} fetchAdditionalInfo -     * @returns {Promise<import('api').GetAnkiNoteInfoResult>} +     * @param {import('api').ApiParam<'getAnkiNoteInfo', 'notes'>} notes +     * @param {import('api').ApiParam<'getAnkiNoteInfo', 'fetchAdditionalInfo'>} fetchAdditionalInfo +     * @returns {Promise<import('api').ApiReturn<'getAnkiNoteInfo'>>}       */      getAnkiNoteInfo(notes, fetchAdditionalInfo) { -        /** @type {import('api').GetAnkiNoteInfoDetails} */ -        const details = {notes, fetchAdditionalInfo}; -        return this._invoke('getAnkiNoteInfo', details); +        return this._invoke('getAnkiNoteInfo', {notes, fetchAdditionalInfo});      }      /** -     * @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>} +     * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'timestamp'>} timestamp +     * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'definitionDetails'>} definitionDetails +     * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'audioDetails'>} audioDetails +     * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'screenshotDetails'>} screenshotDetails +     * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'clipboardDetails'>} clipboardDetails +     * @param {import('api').ApiParam<'injectAnkiNoteMedia', 'dictionaryMediaDetails'>} dictionaryMediaDetails +     * @returns {Promise<import('api').ApiReturn<'injectAnkiNoteMedia'>>}       */      injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails) { -        /** @type {import('api').InjectAnkiNoteMediaDetails} */ -        const details = {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails}; -        return this._invoke('injectAnkiNoteMedia', details); +        return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails});      }      /** -     * @param {import('api').NoteViewDetails['noteId']} noteId -     * @param {import('api').NoteViewDetails['mode']} mode -     * @param {import('api').NoteViewDetails['allowFallback']} allowFallback -     * @returns {Promise<import('api').NoteViewResult>} +     * @param {import('api').ApiParam<'noteView', 'noteId'>} noteId +     * @param {import('api').ApiParam<'noteView', 'mode'>} mode +     * @param {import('api').ApiParam<'noteView', 'allowFallback'>} allowFallback +     * @returns {Promise<import('api').ApiReturn<'noteView'>>}       */      noteView(noteId, mode, allowFallback) { -        /** @type {import('api').NoteViewDetails} */ -        const details = {noteId, mode, allowFallback}; -        return this._invoke('noteView', details); +        return this._invoke('noteView', {noteId, mode, allowFallback});      }      /** -     * @param {import('api').SuspendAnkiCardsForNoteDetails['noteId']} noteId -     * @returns {Promise<import('api').SuspendAnkiCardsForNoteResult>} +     * @param {import('api').ApiParam<'suspendAnkiCardsForNote', 'noteId'>} noteId +     * @returns {Promise<import('api').ApiReturn<'suspendAnkiCardsForNote'>>}       */      suspendAnkiCardsForNote(noteId) { -        /** @type {import('api').SuspendAnkiCardsForNoteDetails} */ -        const details = {noteId}; -        return this._invoke('suspendAnkiCardsForNote', details); +        return this._invoke('suspendAnkiCardsForNote', {noteId});      }      /** -     * @param {import('api').GetTermAudioInfoListDetails['source']} source -     * @param {import('api').GetTermAudioInfoListDetails['term']} term -     * @param {import('api').GetTermAudioInfoListDetails['reading']} reading -     * @returns {Promise<import('api').GetTermAudioInfoListResult>} +     * @param {import('api').ApiParam<'getTermAudioInfoList', 'source'>} source +     * @param {import('api').ApiParam<'getTermAudioInfoList', 'term'>} term +     * @param {import('api').ApiParam<'getTermAudioInfoList', 'reading'>} reading +     * @returns {Promise<import('api').ApiReturn<'getTermAudioInfoList'>>}       */      getTermAudioInfoList(source, term, reading) { -        /** @type {import('api').GetTermAudioInfoListDetails} */ -        const details = {source, term, reading}; -        return this._invoke('getTermAudioInfoList', details); +        return this._invoke('getTermAudioInfoList', {source, term, reading});      }      /** -     * @param {import('api').CommandExecDetails['command']} command -     * @param {import('api').CommandExecDetails['params']} [params] -     * @returns {Promise<import('api').CommandExecResult>} +     * @param {import('api').ApiParam<'commandExec', 'command'>} command +     * @param {import('api').ApiParam<'commandExec', 'params'>} [params] +     * @returns {Promise<import('api').ApiReturn<'commandExec'>>}       */      commandExec(command, params) { -        /** @type {import('api').CommandExecDetails} */ -        const details = {command, params}; -        return this._invoke('commandExec', details); +        return this._invoke('commandExec', {command, params});      }      /** -     * @param {import('api').SendMessageToFrameDetails['frameId']} frameId -     * @param {import('api').SendMessageToFrameDetails['action']} action -     * @param {import('api').SendMessageToFrameDetails['params']} [params] -     * @returns {Promise<import('api').SendMessageToFrameResult>} +     * @param {import('api').ApiParam<'sendMessageToFrame', 'frameId'>} frameId +     * @param {import('api').ApiParam<'sendMessageToFrame', 'action'>} action +     * @param {import('api').ApiParam<'sendMessageToFrame', 'params'>} [params] +     * @returns {Promise<import('api').ApiReturn<'sendMessageToFrame'>>}       */      sendMessageToFrame(frameId, action, params) { -        /** @type {import('api').SendMessageToFrameDetails} */ -        const details = {frameId, action, params}; -        return this._invoke('sendMessageToFrame', details); +        return this._invoke('sendMessageToFrame', {frameId, action, params});      }      /** -     * @param {import('api').BroadcastTabDetails['action']} action -     * @param {import('api').BroadcastTabDetails['params']} params -     * @returns {Promise<import('api').BroadcastTabResult>} +     * @param {import('api').ApiParam<'broadcastTab', 'action'>} action +     * @param {import('api').ApiParam<'broadcastTab', 'params'>} params +     * @returns {Promise<import('api').ApiReturn<'broadcastTab'>>}       */      broadcastTab(action, params) { -        /** @type {import('api').BroadcastTabDetails} */ -        const details = {action, params}; -        return this._invoke('broadcastTab', details); +        return this._invoke('broadcastTab', {action, params});      }      /** -     * @returns {Promise<import('api').FrameInformationGetResult>} +     * @returns {Promise<import('api').ApiReturn<'frameInformationGet'>>}       */      frameInformationGet() { -        return this._invoke('frameInformationGet'); +        return this._invoke('frameInformationGet', void 0);      }      /** -     * @param {import('api').InjectStylesheetDetails['type']} type -     * @param {import('api').InjectStylesheetDetails['value']} value -     * @returns {Promise<import('api').InjectStylesheetResult>} +     * @param {import('api').ApiParam<'injectStylesheet', 'type'>} type +     * @param {import('api').ApiParam<'injectStylesheet', 'value'>} value +     * @returns {Promise<import('api').ApiReturn<'injectStylesheet'>>}       */      injectStylesheet(type, value) { -        /** @type {import('api').InjectStylesheetDetails} */ -        const details = {type, value}; -        return this._invoke('injectStylesheet', details); +        return this._invoke('injectStylesheet', {type, value});      }      /** -     * @param {import('api').GetStylesheetContentDetails['url']} url -     * @returns {Promise<import('api').GetStylesheetContentResult>} +     * @param {import('api').ApiParam<'getStylesheetContent', 'url'>} url +     * @returns {Promise<import('api').ApiReturn<'getStylesheetContent'>>}       */      getStylesheetContent(url) { -        /** @type {import('api').GetStylesheetContentDetails} */ -        const details = {url}; -        return this._invoke('getStylesheetContent', details); +        return this._invoke('getStylesheetContent', {url});      }      /** -     * @returns {Promise<import('api').GetEnvironmentInfoResult>} +     * @returns {Promise<import('api').ApiReturn<'getEnvironmentInfo'>>}       */      getEnvironmentInfo() { -        return this._invoke('getEnvironmentInfo'); +        return this._invoke('getEnvironmentInfo', void 0);      }      /** -     * @returns {Promise<import('api').ClipboardGetResult>} +     * @returns {Promise<import('api').ApiReturn<'clipboardGet'>>}       */      clipboardGet() { -        return this._invoke('clipboardGet'); +        return this._invoke('clipboardGet', void 0);      }      /** -     * @returns {Promise<import('api').GetDisplayTemplatesHtmlResult>} +     * @returns {Promise<import('api').ApiReturn<'getDisplayTemplatesHtml'>>}       */      getDisplayTemplatesHtml() { -        return this._invoke('getDisplayTemplatesHtml'); +        return this._invoke('getDisplayTemplatesHtml', void 0);      }      /** -     * @returns {Promise<import('api').GetZoomResult>} +     * @returns {Promise<import('api').ApiReturn<'getZoom'>>}       */      getZoom() { -        return this._invoke('getZoom'); +        return this._invoke('getZoom', void 0);      }      /** -     * @returns {Promise<import('api').GetDefaultAnkiFieldTemplatesResult>} +     * @returns {Promise<import('api').ApiReturn<'getDefaultAnkiFieldTemplates'>>}       */      getDefaultAnkiFieldTemplates() { -        return this._invoke('getDefaultAnkiFieldTemplates'); +        return this._invoke('getDefaultAnkiFieldTemplates', void 0);      }      /** -     * @returns {Promise<import('api').GetDictionaryInfoResult>} +     * @returns {Promise<import('api').ApiReturn<'getDictionaryInfo'>>}       */      getDictionaryInfo() { -        return this._invoke('getDictionaryInfo'); +        return this._invoke('getDictionaryInfo', void 0);      }      /** -     * @returns {Promise<import('api').PurgeDatabaseResult>} +     * @returns {Promise<import('api').ApiReturn<'purgeDatabase'>>}       */      purgeDatabase() { -        return this._invoke('purgeDatabase'); +        return this._invoke('purgeDatabase', void 0);      }      /** -     * @param {import('api').GetMediaDetails['targets']} targets -     * @returns {Promise<import('api').GetMediaResult>} +     * @param {import('api').ApiParam<'getMedia', 'targets'>} targets +     * @returns {Promise<import('api').ApiReturn<'getMedia'>>}       */      getMedia(targets) { -        /** @type {import('api').GetMediaDetails} */ -        const details = {targets}; -        return this._invoke('getMedia', details); +        return this._invoke('getMedia', {targets});      }      /** -     * @param {import('api').LogDetails['error']} error -     * @param {import('api').LogDetails['level']} level -     * @param {import('api').LogDetails['context']} context -     * @returns {Promise<import('api').LogResult>} +     * @param {import('api').ApiParam<'log', 'error'>} error +     * @param {import('api').ApiParam<'log', 'level'>} level +     * @param {import('api').ApiParam<'log', 'context'>} context +     * @returns {Promise<import('api').ApiReturn<'log'>>}       */      log(error, level, context) { -        /** @type {import('api').LogDetails} */ -        const details = {error, level, context}; -        return this._invoke('log', details); +        return this._invoke('log', {error, level, context});      }      /** -     * @returns {Promise<import('api').LogIndicatorClearResult>} +     * @returns {Promise<import('api').ApiReturn<'logIndicatorClear'>>}       */      logIndicatorClear() { -        return this._invoke('logIndicatorClear'); +        return this._invoke('logIndicatorClear', void 0);      }      /** -     * @param {import('api').ModifySettingsDetails['targets']} targets -     * @param {import('api').ModifySettingsDetails['source']} source -     * @returns {Promise<import('api').ModifySettingsResult>} +     * @param {import('api').ApiParam<'modifySettings', 'targets'>} targets +     * @param {import('api').ApiParam<'modifySettings', 'source'>} source +     * @returns {Promise<import('api').ApiReturn<'modifySettings'>>}       */      modifySettings(targets, source) { -        const details = {targets, source}; -        return this._invoke('modifySettings', details); +        return this._invoke('modifySettings', {targets, source});      }      /** -     * @param {import('api').GetSettingsDetails['targets']} targets -     * @returns {Promise<import('api').GetSettingsResult>} +     * @param {import('api').ApiParam<'getSettings', 'targets'>} targets +     * @returns {Promise<import('api').ApiReturn<'getSettings'>>}       */      getSettings(targets) { -        /** @type {import('api').GetSettingsDetails} */ -        const details = {targets}; -        return this._invoke('getSettings', details); +        return this._invoke('getSettings', {targets});      }      /** -     * @param {import('api').SetAllSettingsDetails['value']} value -     * @param {import('api').SetAllSettingsDetails['source']} source -     * @returns {Promise<import('api').SetAllSettingsResult>} +     * @param {import('api').ApiParam<'setAllSettings', 'value'>} value +     * @param {import('api').ApiParam<'setAllSettings', 'source'>} source +     * @returns {Promise<import('api').ApiReturn<'setAllSettings'>>}       */      setAllSettings(value, source) { -        /** @type {import('api').SetAllSettingsDetails} */ -        const details = {value, source}; -        return this._invoke('setAllSettings', details); +        return this._invoke('setAllSettings', {value, source});      }      /** -     * @param {import('api').GetOrCreateSearchPopupDetails} details -     * @returns {Promise<import('api').GetOrCreateSearchPopupResult>} +     * @param {import('api').ApiParams<'getOrCreateSearchPopup'>} details +     * @returns {Promise<import('api').ApiReturn<'getOrCreateSearchPopup'>>}       */      getOrCreateSearchPopup(details) {          return this._invoke('getOrCreateSearchPopup', details);      }      /** -     * @param {import('api').IsTabSearchPopupDetails['tabId']} tabId -     * @returns {Promise<import('api').IsTabSearchPopupResult>} +     * @param {import('api').ApiParam<'isTabSearchPopup', 'tabId'>} tabId +     * @returns {Promise<import('api').ApiReturn<'isTabSearchPopup'>>}       */      isTabSearchPopup(tabId) { -        /** @type {import('api').IsTabSearchPopupDetails} */ -        const details = {tabId}; -        return this._invoke('isTabSearchPopup', details); +        return this._invoke('isTabSearchPopup', {tabId});      }      /** -     * @param {import('api').TriggerDatabaseUpdatedDetails['type']} type -     * @param {import('api').TriggerDatabaseUpdatedDetails['cause']} cause -     * @returns {Promise<import('api').TriggerDatabaseUpdatedResult>} +     * @param {import('api').ApiParam<'triggerDatabaseUpdated', 'type'>} type +     * @param {import('api').ApiParam<'triggerDatabaseUpdated', 'cause'>} cause +     * @returns {Promise<import('api').ApiReturn<'triggerDatabaseUpdated'>>}       */      triggerDatabaseUpdated(type, cause) { -        /** @type {import('api').TriggerDatabaseUpdatedDetails} */ -        const details = {type, cause}; -        return this._invoke('triggerDatabaseUpdated', details); +        return this._invoke('triggerDatabaseUpdated', {type, cause});      }      /** -     * @returns {Promise<import('api').TestMecabResult>} +     * @returns {Promise<import('api').ApiReturn<'testMecab'>>}       */      testMecab() { -        return this._invoke('testMecab'); +        return this._invoke('testMecab', void 0);      }      /** -     * @param {import('api').TextHasJapaneseCharactersDetails['text']} text -     * @returns {Promise<import('api').TextHasJapaneseCharactersResult>} +     * @param {import('api').ApiParam<'textHasJapaneseCharacters', 'text'>} text +     * @returns {Promise<import('api').ApiReturn<'textHasJapaneseCharacters'>>}       */      textHasJapaneseCharacters(text) { -        /** @type {import('api').TextHasJapaneseCharactersDetails} */ -        const details = {text}; -        return this._invoke('textHasJapaneseCharacters', details); +        return this._invoke('textHasJapaneseCharacters', {text});      }      /** -     * @param {import('api').GetTermFrequenciesDetails['termReadingList']} termReadingList -     * @param {import('api').GetTermFrequenciesDetails['dictionaries']} dictionaries -     * @returns {Promise<import('api').GetTermFrequenciesResult>} +     * @param {import('api').ApiParam<'getTermFrequencies', 'termReadingList'>} termReadingList +     * @param {import('api').ApiParam<'getTermFrequencies', 'dictionaries'>} dictionaries +     * @returns {Promise<import('api').ApiReturn<'getTermFrequencies'>>}       */      getTermFrequencies(termReadingList, dictionaries) { -        /** @type {import('api').GetTermFrequenciesDetails} */ -        const details = {termReadingList, dictionaries}; -        return this._invoke('getTermFrequencies', details); +        return this._invoke('getTermFrequencies', {termReadingList, dictionaries});      }      /** -     * @param {import('api').FindAnkiNotesDetails['query']} query -     * @returns {Promise<import('api').FindAnkiNotesResult>} +     * @param {import('api').ApiParam<'findAnkiNotes', 'query'>} query +     * @returns {Promise<import('api').ApiReturn<'findAnkiNotes'>>}       */      findAnkiNotes(query) { -        /** @type {import('api').FindAnkiNotesDetails} */ -        const details = {query}; -        return this._invoke('findAnkiNotes', details); +        return this._invoke('findAnkiNotes', {query});      }      /** -     * @param {import('api').LoadExtensionScriptsDetails['files']} files -     * @returns {Promise<import('api').LoadExtensionScriptsResult>} +     * @param {import('api').ApiParam<'loadExtensionScripts', 'files'>} files +     * @returns {Promise<import('api').ApiReturn<'loadExtensionScripts'>>}       */      loadExtensionScripts(files) { -        /** @type {import('api').LoadExtensionScriptsDetails} */ -        const details = {files}; -        return this._invoke('loadExtensionScripts', details); +        return this._invoke('loadExtensionScripts', {files});      }      /** -     * @param {import('api').OpenCrossFramePortDetails['targetTabId']} targetTabId -     * @param {import('api').OpenCrossFramePortDetails['targetFrameId']} targetFrameId -     * @returns {Promise<import('api').OpenCrossFramePortResult>} +     * @param {import('api').ApiParam<'openCrossFramePort', 'targetTabId'>} targetTabId +     * @param {import('api').ApiParam<'openCrossFramePort', 'targetFrameId'>} targetFrameId +     * @returns {Promise<import('api').ApiReturn<'openCrossFramePort'>>}       */      openCrossFramePort(targetTabId, targetFrameId) {          return this._invoke('openCrossFramePort', {targetTabId, targetFrameId}); @@ -425,12 +374,14 @@ export class API {      // Utilities      /** -     * @template [TReturn=unknown] -     * @param {string} action -     * @param {import('core').SerializableObject} [params] -     * @returns {Promise<TReturn>} +     * @template {import('api').ApiNames} TAction +     * @template {import('api').ApiParams<TAction>} TParams +     * @param {TAction} action +     * @param {TParams} params +     * @returns {Promise<import('api').ApiReturn<TAction>>}       */ -    _invoke(action, params = {}) { +    _invoke(action, params) { +        /** @type {import('api').MessageAny} */          const data = {action, params};          return new Promise((resolve, reject) => {              try { diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js index 3e59098f..0e7e1e1a 100644 --- a/ext/js/display/query-parser.js +++ b/ext/js/display/query-parser.js @@ -48,7 +48,7 @@ export class QueryParser extends EventDispatcher {          this._useInternalParser = true;          /** @type {boolean} */          this._useMecabParser = false; -        /** @type {import('api').ParseTextResult} */ +        /** @type {import('api').ParseTextResultItem[]} */          this._parseResults = [];          /** @type {HTMLElement} */          this._queryParser = querySelectorNotNull(document, '#query-parser-content'); @@ -252,7 +252,7 @@ export class QueryParser extends EventDispatcher {      /**       * @param {HTMLSelectElement} select -     * @param {import('api').ParseTextResult} parseResults +     * @param {import('api').ParseTextResultItem[]} parseResults       * @param {?string} selectedParser       */      _updateParserModeSelect(select, parseResults, selectedParser) { diff --git a/types/ext/api.d.ts b/types/ext/api.d.ts index 93004447..4e01de02 100644 --- a/types/ext/api.d.ts +++ b/types/ext/api.d.ts @@ -31,58 +31,23 @@ import type * as Settings from './settings';  import type * as SettingsModifications from './settings-modifications';  import type * as Translation from './translation';  import type * as Translator from './translator'; - -// Generic - -export type Handler<TDetails = unknown, TResult = unknown, THasSender extends boolean = false> = ( -    details: TDetails, -    sender: (THasSender extends true ? chrome.runtime.MessageSender : void) -) => (TResult | Promise<TResult>); - -// optionsGet - -export type OptionsGetDetails = { -    optionsContext: Settings.OptionsContext; -}; - -export type OptionsGetResult = Settings.ProfileOptions; - -// optionsGetFull - -export type OptionsGetFullDetails = Record<string, never>; - -export type OptionsGetFullResult = Settings.Options; - -// termsFind +import type { +    ApiMap as BaseApiMap, +    ApiMapInit as BaseApiMapInit, +    ApiHandler as BaseApiHandler, +    ApiParams as BaseApiParams, +    ApiReturn as BaseApiReturn, +    ApiNames as BaseApiNames, +    ApiParam as BaseApiParam, +    ApiParamNames as BaseApiParamNames, +    ApiParamsAny as BaseApiParamsAny, +} from './api-map';  export type FindTermsDetails = {      matchType?: Translation.FindTermsMatchType;      deinflect?: boolean;  }; -export type TermsFindDetails = { -    text: string; -    details: FindTermsDetails; -    optionsContext: Settings.OptionsContext; -}; - -export type TermsFindResult = { -    dictionaryEntries: Dictionary.TermDictionaryEntry[]; -    originalTextLength: number; -}; - -// parseText - -export type ParseTextDetails = { -    text: string; -    optionsContext: Settings.OptionsContext; -    scanLength: number; -    useInternalParser: boolean; -    useMecabParser: boolean; -}; - -export type ParseTextResult = ParseTextResultItem[]; -  export type ParseTextResultItem = {      id: string;      source: 'scanning-parser' | 'mecab'; @@ -97,55 +62,6 @@ export type ParseTextSegment = {  export type ParseTextLine = ParseTextSegment[]; -// kanjiFind - -export type KanjiFindDetails = { -    text: string; -    optionsContext: Settings.OptionsContext; -}; - -export type KanjiFindResult = Dictionary.KanjiDictionaryEntry[]; - -// isAnkiConnected - -export type IsAnkiConnectedDetails = Record<string, never>; - -export type IsAnkiConnectedResult = boolean; - -// getAnkiConnectVersion - -export type GetAnkiConnectVersionDetails = Record<string, never>; - -export type GetAnkiConnectVersionResult = number | null; - -// addAnkiNote - -export type AddAnkiNoteDetails = { -    note: Anki.Note; -}; - -export type AddAnkiNoteResult = Anki.NoteId | null; - -// getAnkiNoteInfo - -export type GetAnkiNoteInfoDetails = { -    notes: Anki.Note[]; -    fetchAdditionalInfo: boolean; -}; - -export type GetAnkiNoteInfoResult = Anki.NoteInfoWrapper[]; - -// injectAnkiNoteMedia - -export type InjectAnkiNoteMediaDetails = { -    timestamp: number; -    definitionDetails: InjectAnkiNoteMediaDefinitionDetails; -    audioDetails: InjectAnkiNoteMediaAudioDetails | null; -    screenshotDetails: InjectAnkiNoteMediaScreenshotDetails | null; -    clipboardDetails: InjectAnkiNoteMediaClipboardDetails | null; -    dictionaryMediaDetails: InjectAnkiNoteMediaDictionaryMediaDetails[]; -}; -  export type InjectAnkiNoteMediaTermDefinitionDetails = {      type: 'term';      term: string; @@ -178,286 +94,320 @@ export type InjectAnkiNoteMediaDictionaryMediaDetails = {      path: string;  }; -export type InjectAnkiNoteMediaResult = { -    screenshotFileName: string | null; -    clipboardImageFileName: string | null; -    clipboardText: string | null; -    audioFileName: string | null; -    dictionaryMedia: InjectAnkiNoteDictionaryMediaResult[]; -    errors: Core.SerializedError[]; -}; -  export type InjectAnkiNoteDictionaryMediaResult = {      dictionary: string;      path: string;      fileName: string | null;  }; -// noteView - -export type NoteViewDetails = { -    noteId: Anki.NoteId; -    mode: Settings.AnkiNoteGuiMode; -    allowFallback: boolean; -}; - -export type NoteViewResult = Settings.AnkiNoteGuiMode; - -// suspendAnkiCardsForNote - -export type SuspendAnkiCardsForNoteDetails = { -    noteId: Anki.NoteId; -}; - -export type SuspendAnkiCardsForNoteResult = number; - -// getTermAudioInfoList - -export type GetTermAudioInfoListDetails = { -    source: Audio.AudioSourceInfo; -    term: string; -    reading: string; -}; - -export type GetTermAudioInfoListResult = AudioDownloader.Info[]; - -// commandExec - -export type CommandExecDetails = { -    command: string; -    params?: Core.SerializableObject; -}; - -export type CommandExecResult = boolean; - -// sendMessageToFrame - -export type SendMessageToFrameDetails = { -    frameId: number; -    action: string; -    params?: Core.SerializableObject; -}; - -export type SendMessageToFrameResult = boolean; - -// broadcastTab - -export type BroadcastTabDetails = { -    action: string; -    params?: Core.SerializableObject; -}; - -export type BroadcastTabResult = boolean; - -// frameInformationGet - -export type FrameInformationGetDetails = Record<string, never>; - -export type FrameInformationGetResult = Extension.ContentOrigin; - -// injectStylesheet - -export type InjectStylesheetDetails = { -    type: 'file' | 'code'; -    value: string; -}; - -export type InjectStylesheetResult = void; - -// getStylesheetContent - -export type GetStylesheetContentDetails = { -    url: string; -}; - -export type GetStylesheetContentResult = string; - -// getEnvironmentInfo - -export type GetEnvironmentInfoDetails = Record<string, never>; - -export type GetEnvironmentInfoResult = Environment.Info; - -// clipboardGet - -export type ClipboardGetDetails = Record<string, never>; - -export type ClipboardGetResult = string; - -// getDisplayTemplatesHtml - -export type GetDisplayTemplatesHtmlDetails = Record<string, never>; - -export type GetDisplayTemplatesHtmlResult = string; - -// getZoom - -export type GetZoomDetails = Record<string, never>; - -export type GetZoomResult = { -    zoomFactor: number; -}; - -// getDefaultAnkiFieldTemplates - -export type GetDefaultAnkiFieldTemplatesDetails = Record<string, never>; - -export type GetDefaultAnkiFieldTemplatesResult = string; - -// getDictionaryInfo - -export type GetDictionaryInfoDetails = Record<string, never>; - -export type GetDictionaryInfoResult = DictionaryImporter.Summary[]; - -// purgeDatabase - -export type PurgeDatabaseDetails = Record<string, never>; - -export type PurgeDatabaseResult = void; - -// getMedia - -export type GetMediaDetails = { -    targets: GetMediaDetailsTarget[]; -}; -  export type GetMediaDetailsTarget = {      path: string;      dictionary: string;  }; -export type GetMediaResult = DictionaryDatabase.MediaDataStringContent[]; - -// log - -export type LogDetails = { -    error: Core.SerializedError; -    level: Log.LogLevel; -    context: Log.LogContext | undefined; -}; - -export type LogResult = void; - -// logIndicatorClear - -export type LogIndicatorClearDetails = Record<string, never>; - -export type LogIndicatorClearResult = void; - -// modifySettings - -export type ModifySettingsDetails = { -    targets: SettingsModifications.ScopedModification[]; -    source: string; -}; - -export type ModifySettingsResult = Core.Response<SettingsModifications.ModificationResult>[]; - -// getSettings - -export type GetSettingsDetails = { -    targets: SettingsModifications.ScopedRead[]; -}; - -export type GetSettingsResult = Core.Response<SettingsModifications.ModificationResult>[]; - -// setAllSettings - -export type SetAllSettingsDetails = { -    value: Settings.Options; -    source: string; -}; - -export type SetAllSettingsResult = void; - -// getOrCreateSearchPopup - -export type GetOrCreateSearchPopupDetails = { -    focus?: boolean | 'ifCreated'; -    text?: string; -}; - -export type GetOrCreateSearchPopupResult = { -    tabId: number | null; -    windowId: number; -}; - -// isTabSearchPopup - -export type IsTabSearchPopupDetails = { -    tabId: number; -}; - -export type IsTabSearchPopupResult = boolean; - -// triggerDatabaseUpdated - -export type TriggerDatabaseUpdatedDetails = { -    type: Backend.DatabaseUpdateType; -    cause: Backend.DatabaseUpdateCause; -}; - -export type TriggerDatabaseUpdatedResult = void; - -// testMecab - -export type TestMecabDetails = Record<string, never>; - -export type TestMecabResult = true; - -// textHasJapaneseCharacters - -export type TextHasJapaneseCharactersDetails = { -    text: string; -}; - -export type TextHasJapaneseCharactersResult = boolean; - -// getTermFrequencies - -export type GetTermFrequenciesDetails = { -    termReadingList: GetTermFrequenciesDetailsTermReadingListItem[]; -    dictionaries: string[]; -}; -  export type GetTermFrequenciesDetailsTermReadingListItem = {      term: string;      reading: string | null;  }; -export type GetTermFrequenciesResult = Translator.TermFrequencySimple[]; - -// findAnkiNotes - -export type FindAnkiNotesDetails = { -    query: string; -}; - -export type FindAnkiNotesResult = Anki.NoteId[]; - -// loadExtensionScripts - -export type LoadExtensionScriptsDetails = { -    files: string[]; -}; - -export type LoadExtensionScriptsResult = void; - -// openCrossFramePort - -export type OpenCrossFramePortDetails = { -    targetTabId: number; -    targetFrameId: number; -}; - -export type OpenCrossFramePortResult = { -    targetTabId: number; -    targetFrameId: number; +type ApiSurface = { +    optionsGet: { +        params: { +            optionsContext: Settings.OptionsContext; +        }; +        return: Settings.ProfileOptions; +    }; +    optionsGetFull: { +        params: void; +        return: Settings.Options; +    }; +    termsFind: { +        params: { +            text: string; +            details: FindTermsDetails; +            optionsContext: Settings.OptionsContext; +        }; +        return: { +            dictionaryEntries: Dictionary.TermDictionaryEntry[]; +            originalTextLength: number; +        }; +    }; +    parseText: { +        params: { +            text: string; +            optionsContext: Settings.OptionsContext; +            scanLength: number; +            useInternalParser: boolean; +            useMecabParser: boolean; +        }; +        return: ParseTextResultItem[]; +    }; +    kanjiFind: { +        params: { +            text: string; +            optionsContext: Settings.OptionsContext; +        }; +        return: Dictionary.KanjiDictionaryEntry[]; +    }; +    isAnkiConnected: { +        params: void; +        return: boolean; +    }; +    getAnkiConnectVersion: { +        params: void; +        return: number | null; +    }; +    addAnkiNote: { +        params: { +            note: Anki.Note; +        }; +        return: Anki.NoteId | null; +    }; +    getAnkiNoteInfo: { +        params: { +            notes: Anki.Note[]; +            fetchAdditionalInfo: boolean; +        }; +        return: Anki.NoteInfoWrapper[]; +    }; +    injectAnkiNoteMedia: { +        params: { +            timestamp: number; +            definitionDetails: InjectAnkiNoteMediaDefinitionDetails; +            audioDetails: InjectAnkiNoteMediaAudioDetails | null; +            screenshotDetails: InjectAnkiNoteMediaScreenshotDetails | null; +            clipboardDetails: InjectAnkiNoteMediaClipboardDetails | null; +            dictionaryMediaDetails: InjectAnkiNoteMediaDictionaryMediaDetails[]; +        }; +        return: { +            screenshotFileName: string | null; +            clipboardImageFileName: string | null; +            clipboardText: string | null; +            audioFileName: string | null; +            dictionaryMedia: InjectAnkiNoteDictionaryMediaResult[]; +            errors: Core.SerializedError[]; +        }; +    }; +    noteView: { +        params: { +            noteId: Anki.NoteId; +            mode: Settings.AnkiNoteGuiMode; +            allowFallback: boolean; +        }; +        return: Settings.AnkiNoteGuiMode; +    }; +    suspendAnkiCardsForNote: { +        params: { +            noteId: Anki.NoteId; +        }; +        return: number; +    }; +    getTermAudioInfoList: { +        params: { +            source: Audio.AudioSourceInfo; +            term: string; +            reading: string; +        }; +        return: AudioDownloader.Info[]; +    }; +    commandExec: { +        params: { +            command: string; +            params?: Core.SerializableObject; +        }; +        return: boolean; +    }; +    sendMessageToFrame: { +        params: { +            frameId: number; +            action: string; +            params?: Core.SerializableObject; +        }; +        return: boolean; +    }; +    broadcastTab: { +        params: { +            action: string; +            params?: Core.SerializableObject; +        }; +        return: boolean; +    }; +    frameInformationGet: { +        params: void; +        return: Extension.ContentOrigin; +    }; +    injectStylesheet: { +        params: { +            type: 'file' | 'code'; +            value: string; +        }; +        return: void; +    }; +    getStylesheetContent: { +        params: { +            url: string; +        }; +        return: string; +    }; +    getEnvironmentInfo: { +        params: void; +        return: Environment.Info; +    }; +    clipboardGet: { +        params: void; +        return: string; +    }; +    getDisplayTemplatesHtml: { +        params: void; +        return: string; +    }; +    getZoom: { +        params: void; +        return: { +            zoomFactor: number; +        }; +    }; +    getDefaultAnkiFieldTemplates: { +        params: void; +        return: string; +    }; +    getDictionaryInfo: { +        params: void; +        return: DictionaryImporter.Summary[]; +    }; +    purgeDatabase: { +        params: void; +        return: void; +    }; +    getMedia: { +        params: { +            targets: GetMediaDetailsTarget[]; +        }; +        return: DictionaryDatabase.MediaDataStringContent[]; +    }; +    log: { +        params: { +            error: Core.SerializedError; +            level: Log.LogLevel; +            context: Log.LogContext | undefined; +        }; +        return: void; +    }; +    logIndicatorClear: { +        params: void; +        return: void; +    }; +    modifySettings: { +        params: { +            targets: SettingsModifications.ScopedModification[]; +            source: string; +        }; +        return: Core.Response<SettingsModifications.ModificationResult>[]; +    }; +    getSettings: { +        params: { +            targets: SettingsModifications.ScopedRead[]; +        }; +        return: Core.Response<SettingsModifications.ModificationResult>[]; +    }; +    setAllSettings: { +        params: { +            value: Settings.Options; +            source: string; +        }; +        return: void; +    }; +    getOrCreateSearchPopup: { +        params: { +            focus?: boolean | 'ifCreated'; +            text?: string; +        }; +        return: { +            tabId: number | null; +            windowId: number; +        }; +    }; +    isTabSearchPopup: { +        params: { +            tabId: number; +        }; +        return: boolean; +    }; +    triggerDatabaseUpdated: { +        params: { +            type: Backend.DatabaseUpdateType; +            cause: Backend.DatabaseUpdateCause; +        }; +        return: void; +    }; +    testMecab: { +        params: void; +        return: true; +    }; +    textHasJapaneseCharacters: { +        params: { +            text: string; +        }; +        return: boolean; +    }; +    getTermFrequencies: { +        params: { +            termReadingList: GetTermFrequenciesDetailsTermReadingListItem[]; +            dictionaries: string[]; +        }; +        return: Translator.TermFrequencySimple[]; +    }; +    findAnkiNotes: { +        params: { +            query: string; +        }; +        return: Anki.NoteId[]; +    }; +    loadExtensionScripts: { +        params: { +            files: string[]; +        }; +        return: void; +    }; +    openCrossFramePort: { +        params: { +            targetTabId: number; +            targetFrameId: number; +        }; +        return: { +            targetTabId: number; +            targetFrameId: number; +        }; +    }; +    requestBackendReadySignal: { +        params: void; +        return: boolean; +    }; +}; + +type ApiExtraArgs = [sender: chrome.runtime.MessageSender]; + +export type ApiNames = BaseApiNames<ApiSurface>; + +export type ApiMap = BaseApiMap<ApiSurface, ApiExtraArgs>; + +export type ApiMapInit = BaseApiMapInit<ApiSurface, ApiExtraArgs>; + +export type ApiHandler<TName extends ApiNames> = BaseApiHandler<ApiSurface[TName], ApiExtraArgs>; + +export type ApiHandlerNoExtraArgs<TName extends ApiNames> = BaseApiHandler<ApiSurface[TName], []>; + +export type ApiParams<TName extends ApiNames> = BaseApiParams<ApiSurface[TName]>; + +export type ApiParam<TName extends ApiNames, TParamName extends BaseApiParamNames<ApiSurface[TName]>> = BaseApiParam<ApiSurface[TName], TParamName>; + +export type ApiReturn<TName extends ApiNames> = BaseApiReturn<ApiSurface[TName]>; + +export type ApiParamsAny = BaseApiParamsAny<ApiSurface>; + +export type MessageAny = Message<ApiNames>; + +type Message<TName extends ApiNames> = { +    action: TName; +    params: ApiParams<TName>;  }; - -// requestBackendReadySignal - -export type RequestBackendReadySignalDetails = Record<string, never>; - -export type RequestBackendReadySignalResult = boolean; |