Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)
1/*
2 * Copyright (C) 2023-2025 Yomitan Authors
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18import {ExtensionError} from './extension-error.js';
19
20/**
21 * @template {import('api-map').ApiSurface} [TApiSurface=never]
22 * @template {unknown[]} [TExtraParams=[]]
23 * @param {import('api-map').ApiMapInit<TApiSurface, TExtraParams>} init
24 * @returns {import('api-map').ApiMap<TApiSurface, TExtraParams>}
25 */
26export function createApiMap(init) {
27 return new Map(init);
28}
29
30/**
31 * @template {import('api-map').ApiSurface} [TApiSurface=never]
32 * @template {unknown[]} [TExtraParams=[]]
33 * @param {import('api-map').ApiMap<TApiSurface, TExtraParams>} map
34 * @param {import('api-map').ApiMapInit<TApiSurface, TExtraParams>} init
35 * @throws {Error}
36 */
37export function extendApiMap(map, init) {
38 for (const [key, value] of init) {
39 if (map.has(key)) { throw new Error(`The handler for ${String(key)} has already been registered`); }
40 map.set(key, value);
41 }
42}
43
44/**
45 * @template {import('api-map').ApiSurface} [TApiSurface=never]
46 * @template {unknown[]} [TExtraParams=[]]
47 * @param {import('api-map').ApiMap<TApiSurface, TExtraParams>} map
48 * @param {string} name
49 * @returns {import('api-map').ApiHandlerAny<TApiSurface, TExtraParams>|undefined}
50 */
51export function getApiMapHandler(map, name) {
52 return map.get(/** @type {import('api-map').ApiNames<TApiSurface>} */ (name));
53}
54
55/**
56 * @template {import('api-map').ApiSurface} [TApiSurface=never]
57 * @template {unknown[]} [TExtraParams=[]]
58 * @param {import('api-map').ApiMap<TApiSurface, TExtraParams>} map
59 * @param {string} name
60 * @param {import('api-map').ApiParamsAny<TApiSurface>} params
61 * @param {TExtraParams} extraParams
62 * @param {(response: import('core').Response<import('api-map').ApiReturnAny<TApiSurface>>) => void} callback
63 * @param {() => void} [handlerNotFoundCallback]
64 * @returns {boolean} `true` if async, `false` otherwise.
65 */
66export function invokeApiMapHandler(map, name, params, extraParams, callback, handlerNotFoundCallback) {
67 const handler = getApiMapHandler(map, name);
68 if (typeof handler === 'undefined') {
69 if (typeof handlerNotFoundCallback === 'function') {
70 try {
71 handlerNotFoundCallback();
72 } catch (error) {
73 // NOP
74 }
75 }
76 return false;
77 }
78 try {
79 const promiseOrResult = handler(/** @type {import('core').SafeAny} */ (params), ...extraParams);
80 if (promiseOrResult instanceof Promise) {
81 /** @type {Promise<unknown>} */ (promiseOrResult).then(
82 (result) => { callback({result}); },
83 (error) => { callback({error: ExtensionError.serialize(error)}); },
84 );
85 return true;
86 } else {
87 callback({result: promiseOrResult});
88 return false;
89 }
90 } catch (error) {
91 callback({error: ExtensionError.serialize(error)});
92 return false;
93 }
94}