summaryrefslogtreecommitdiff
path: root/ext/js/comm/api.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js/comm/api.js')
-rw-r--r--ext/js/comm/api.js356
1 files changed, 309 insertions, 47 deletions
diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js
index 05f95464..62dc98b1 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 targetTabId
+ * @param targetFrameId
+ */
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 {?number} */
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
}
}