This commit is contained in:
Sebastian 2023-03-02 13:51:03 -03:00
parent f446a59214
commit 2d5fbb22cd
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
38 changed files with 288 additions and 13623 deletions

View File

@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@ -13,7 +13,15 @@
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/*eslint quote-props: ["error", "consistent"]*/
export const strings: {[s: string]: any} = {};
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
/*
* Linaria need pre-process typscript files into javascript before running.
* We choose to use the default preact-cli config.
* This file should be used from @linaria/rollup plugin only
*/
{
"plugins": ["./trim-extension.cjs"],
}

View File

@ -0,0 +1,151 @@
#!/usr/bin/env node
/*
This file is part of GNU Taler
(C) 2021-2023 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import esbuild from "esbuild";
import path from "path";
import fs from "fs";
import linaria from '@linaria/esbuild'
// eslint-disable-next-line no-undef
const BASE = process.cwd();
const preact = path.join(
BASE,
"node_modules",
"preact",
"compat",
"dist",
"compat.module.js",
);
const preactCompatPlugin = {
name: "preact-compat",
setup(build) {
build.onResolve({ filter: /^(react-dom|react)$/ }, (args) => {
//console.log("onresolve", JSON.stringify(args, undefined, 2));
return {
path: preact,
};
});
},
};
const pages = ["OfferTip","OfferRefund","DepletedTip","RequestPayment","ShowOrderDetails"]
const entryPoints = pages.map(p => `src/pages/${p}.tsx`);
let GIT_ROOT = BASE;
while (!fs.existsSync(path.join(GIT_ROOT, ".git")) && GIT_ROOT !== "/") {
GIT_ROOT = path.join(GIT_ROOT, "../");
}
if (GIT_ROOT === "/") {
// eslint-disable-next-line no-undef
console.log("not found");
// eslint-disable-next-line no-undef
process.exit(1);
}
const GIT_HASH = GIT_ROOT === "/" ? undefined : git_hash();
let _package = JSON.parse(fs.readFileSync(path.join(BASE, "package.json")));
function git_hash() {
const rev = fs
.readFileSync(path.join(GIT_ROOT, ".git", "HEAD"))
.toString()
.trim()
.split(/.*[: ]/)
.slice(-1)[0];
if (rev.indexOf("/") === -1) {
return rev;
} else {
return fs.readFileSync(path.join(GIT_ROOT, ".git", rev)).toString().trim();
}
}
function templatePlugin(options) {
return {
name: "template-backend",
setup(build) {
build.onEnd(() => {
for (const page of options.pages) {
const css = fs.readFileSync(path.join(build.initialOptions.outdir, `${page}.css`),"utf8").toString()
const js = fs.readFileSync(path.join(build.initialOptions.outdir, `${page}.js`),"utf8").toString()
const scripts = `<script>${js}</script>`
const style = `<style>${css}</style>`
const render = new Function(`${js}; return page.buildTimeRendering();`)()
const html = `
<!doctype html>
<html>
<head>
${render.head}
${style}
</head>
<script id="built_time_data">
</script>
<body>
${render.body}
${scripts}
<script>page.mount()</script>
</body>
</html>`
fs.writeFileSync(path.join(build.initialOptions.outdir, `${page}.html`), html);
}
});
},
};
}
export const buildConfig = {
entryPoints: [...entryPoints],
bundle: true,
outdir: "dist/pages",
minify: true,
loader: {
".svg": "file",
".png": "dataurl",
".jpeg": "dataurl",
'.ttf': 'file',
'.woff': 'file',
'.woff2': 'file',
'.eot': 'file',
},
target: ["es6"],
format: "iife",
platform: "browser",
sourcemap: false,
globalName: "page",
jsxFactory: "h",
jsxFragment: "Fragment",
define: {
__VERSION__: `"${_package.version}"`,
__GIT_HASH__: `"${GIT_HASH}"`,
},
plugins: [
linaria.default({
babelOptions: {
babelrc: false,
configFile: './babel.config-linaria.json',
},
sourceMap: true,
}),
preactCompatPlugin,
templatePlugin({pages})
],
};
await esbuild.build(buildConfig)

View File

@ -38,36 +38,18 @@
]
},
"dependencies": {
"@gnu-taler/taler-util": "workspace:*",
"axios": "^0.21.1",
"date-fns": "^2.21.1",
"history": "4.10.1",
"jed": "^1.1.1",
"preact": "10.11.3",
"preact-router": "3.2.1",
"qrcode-generator": "^1.4.4",
"swr": "^0.5.5",
"yup": "^0.32.9"
"qrcode-generator": "^1.4.4"
},
"devDependencies": {
"@babel/core": "7.18.9",
"@creativebulma/bulma-tooltip": "^1.2.0",
"@gnu-taler/pogen": "^0.0.5",
"@linaria/babel-preset": "3.0.0-beta.22",
"@linaria/core": "3.0.0-beta.22",
"@linaria/react": "3.0.0-beta.22",
"@linaria/rollup": "3.0.0-beta.22",
"@linaria/shaker": "3.0.0-beta.22",
"@linaria/webpack-loader": "3.0.0-beta.22",
"@rollup/plugin-alias": "^3.1.5",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-html": "^0.2.3",
"@rollup/plugin-image": "^2.1.1",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-typescript": "11",
"@types/history": "^4.7.8",
"@types/mocha": "^8.2.2",
"@types/mustache": "^4.1.2",
"@typescript-eslint/eslint-plugin": "^4.22.0",
@ -81,11 +63,8 @@
"po2json": "^0.4.5",
"preact-render-to-string": "^5.1.19",
"rimraf": "^3.0.2",
"rollup": "^2.56.3",
"rollup-plugin-bundle-html": "^0.2.2",
"rollup-plugin-css-only": "^3.1.0",
"script-ext-html-webpack-plugin": "^2.1.5",
"sirv-cli": "^1.0.11",
"ts-node": "^10.9.1",
"tslib": "2.4.0",
"typescript": "4.9.4"
}

View File

@ -1,116 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
// rollup.config.js
import linaria from '@linaria/rollup';
import nodeResolve from "@rollup/plugin-node-resolve";
import alias from "@rollup/plugin-alias";
import image from '@rollup/plugin-image';
import json from "@rollup/plugin-json";
import ts from "@rollup/plugin-typescript";
import replace from "@rollup/plugin-replace";
import css from 'rollup-plugin-css-only';
import html from '@rollup/plugin-html';
import commonjs from "@rollup/plugin-commonjs";
const template = async ({
files,
}) => {
const scripts = (files.js || []).map(({ code }) => `<script>${code}</script>`).join('\n');
const css = (files.css || []).map(({ source }) => `<style>${source}</style>`).join('\n');
const ssr = (files.js || []).map(({ code }) => code).join('\n');
const page = new Function(`${ssr}; return page.buildTimeRendering();`)()
return `
<!doctype html>
<html>
<head>
${page.head}
${css}
</head>
<script id="built_time_data">
</script>
<body>
${page.body}
${scripts}
<script>page.mount()</script>
</body>
</html>`;
};
const makePlugins = (name) => [
alias({
entries: [
{ find: 'react', replacement: 'preact/compat' },
{ find: 'react-dom', replacement: 'preact/compat' }
]
}),
replace({
"process.env.NODE_ENV": JSON.stringify("production"),
preventAssignment: true,
}),
commonjs({
include: [/node_modules/, /dist/],
extensions: [".js"],
ignoreGlobal: true,
sourceMap: true,
}),
nodeResolve({
browser: true,
preferBuiltins: true,
}),
json(),
image(),
linaria({
sourceMap: process.env.NODE_ENV !== 'production',
}),
css(),
ts({
sourceMap: false,
outputToFilesystem: false,
}),
html({ template, fileName: name }),
];
function formatHtmlName(name) {
return name
.replace(/^[A-Z]/, letter => `${letter.toLowerCase()}`) //first letter lowercase
.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`) //snake case
.concat(".en.html"); //extension
}
const pageDefinition = (name) => ({
input: `src/pages/${name}.tsx`,
output: {
file: `dist/pages/${name}.js`,
format: "iife",
exports: 'named',
name: 'page',
},
plugins: makePlugins(formatHtmlName(name)),
});
export default [
pageDefinition("OfferTip"),
pageDefinition("OfferRefund"),
pageDefinition("DepletedTip"),
pageDefinition("RequestPayment"),
pageDefinition("ShowOrderDetails"),
]

View File

@ -1,82 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { createContext, h, VNode } from 'preact'
import { useCallback, useContext, useState } from 'preact/hooks'
import { useBackendDefaultToken, useBackendURL } from '../hooks';
interface BackendContextType {
url: string;
token?: string;
triedToLog: boolean;
resetBackend: () => void;
clearAllTokens: () => void;
addTokenCleaner: (c: () => void) => void;
updateLoginStatus: (url: string, token?: string) => void;
}
const BackendContext = createContext<BackendContextType>({
url: '',
token: undefined,
triedToLog: false,
resetBackend: () => null,
clearAllTokens: () => null,
addTokenCleaner: () => null,
updateLoginStatus: () => null,
})
function useBackendContextState(defaultUrl?: string): BackendContextType {
const [url, triedToLog, changeBackend, resetBackend] = useBackendURL(defaultUrl);
const [token, _updateToken] = useBackendDefaultToken();
const updateToken = (t?: string) => {
_updateToken(t)
}
const tokenCleaner = useCallback(() => { updateToken(undefined) }, [])
const [cleaners, setCleaners] = useState([tokenCleaner])
const addTokenCleaner = (c: () => void) => setCleaners(cs => [...cs, c])
const addTokenCleanerMemo = useCallback((c: () => void) => { addTokenCleaner(c) }, [tokenCleaner])
const clearAllTokens = () => {
cleaners.forEach(c => c())
for (let i = 0; i < localStorage.length; i++) {
const k = localStorage.key(i)
if (k && /^backend-token/.test(k)) localStorage.removeItem(k)
}
resetBackend()
}
const updateLoginStatus = (url: string, token?: string) => {
changeBackend(url);
if (token) updateToken(token);
};
return { url, token, triedToLog, updateLoginStatus, resetBackend, clearAllTokens, addTokenCleaner: addTokenCleanerMemo }
}
export const BackendContextProvider = ({ children, defaultUrl }: { children: any, defaultUrl?: string }): VNode => {
const value = useBackendContextState(defaultUrl)
return h(BackendContext.Provider, { value, children });
}
export const useBackendContext = (): BackendContextType => useContext(BackendContext);

View File

@ -1,32 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { createContext } from 'preact'
import { useContext } from 'preact/hooks'
interface Type {
currency: string;
version: string;
}
const Context = createContext<Type>(null!)
export const ConfigContextProvider = Context.Provider
export const useConfigContext = (): Type => useContext(Context);

View File

@ -1,40 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, createContext, VNode, ComponentChildren } from 'preact'
import { useContext } from 'preact/hooks'
import useSWR, { trigger, useSWRInfinite, cache, mutate } from 'swr';
interface Type {
useSWR: typeof useSWR,
useSWRInfinite: typeof useSWRInfinite,
}
const Context = createContext<Type>({} as any)
export const useFetchContext = (): Type => useContext(Context);
export const FetchContextProvider = ({ children }: { children: ComponentChildren }): VNode => {
return h(Context.Provider, { value: { useSWR, useSWRInfinite }, children });
}
export const FetchContextProviderTesting = ({ children, data }: { children: ComponentChildren, data:any }): VNode => {
return h(Context.Provider, { value: { useSWR: () => data, useSWRInfinite }, children });
}

View File

@ -1,35 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { createContext } from 'preact'
import { useContext } from 'preact/hooks'
interface Type {
id: string;
token?: string;
admin?: boolean;
changeToken: (t?:string) => void;
}
const Context = createContext<Type>({} as any)
export const InstanceContextProvider = Context.Provider
export const useInstanceContext = (): Type => useContext(Context);

View File

@ -1,35 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { createContext } from 'preact'
import { useContext } from 'preact/hooks'
interface Type {
id: string;
token?: string;
admin?: boolean;
changeToken: (t?:string) => void;
}
const Context = createContext<Type>({} as any)
export const ListenerContextProvider = Context.Provider
export const useListenerContext = (): Type => useContext(Context);

View File

@ -1,59 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { createContext, h, VNode } from 'preact'
import { useContext, useEffect } from 'preact/hooks'
import { useLang } from '../hooks'
import * as jedLib from "jed";
import { strings } from "../i18n/strings";
interface Type {
lang: string;
handler: any;
changeLanguage: (l: string) => void;
}
const initial = {
lang: 'en',
handler: null,
changeLanguage: () => {
// do not change anything
}
}
const Context = createContext<Type>(initial)
interface Props {
initial?: string,
children: any,
forceLang?: string
}
export const TranslationProvider = ({ initial, children, forceLang }: Props): VNode => {
const [lang, changeLanguage] = useLang(initial)
useEffect(() => {
if (forceLang) {
changeLanguage(forceLang)
}
})
const handler = new jedLib.Jed(strings[lang]);
return h(Context.Provider, { value: { lang, handler, changeLanguage }, children });
}
export const useTranslationContext = (): Type => useContext(Context);

View File

@ -1,76 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useState } from "preact/hooks";
import { cancelPendingRequest } from "./backend";
export interface Options {
slowTolerance: number,
}
export interface AsyncOperationApi<T> {
request: (...a: any) => void,
cancel: () => void,
data: T | undefined,
isSlow: boolean,
isLoading: boolean,
error: string | undefined
}
export function useAsync<T>(fn?: (...args: any) => Promise<T>, { slowTolerance: tooLong }: Options = { slowTolerance: 1000 }): AsyncOperationApi<T> {
const [data, setData] = useState<T | undefined>(undefined);
const [isLoading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<any>(undefined);
const [isSlow, setSlow] = useState(false)
const request = async (...args: any) => {
if (!fn) return;
setLoading(true);
const handler = setTimeout(() => {
setSlow(true)
}, tooLong)
try {
const result = await fn(...args);
setData(result);
} catch (error) {
setError(error);
}
setLoading(false);
setSlow(false)
clearTimeout(handler)
};
function cancel() {
cancelPendingRequest()
setLoading(false);
setSlow(false)
}
return {
request,
cancel,
data,
isSlow,
isLoading,
error
};
}

View File

@ -1,264 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { mutate, cache } from 'swr';
import axios, { AxiosError, AxiosResponse } from 'axios'
import { MerchantBackend } from '../declaration';
import { useBackendContext } from '../context/backend';
import { useEffect, useState } from 'preact/hooks';
import { DEFAULT_REQUEST_TIMEOUT } from '../utils/constants';
export function mutateAll(re: RegExp, value?: unknown): Array<Promise<any>> {
return cache.keys().filter(key => {
return re.test(key)
}).map(key => {
return mutate(key, value)
})
}
export type HttpResponse<T> = HttpResponseOk<T> | HttpResponseLoading<T> | HttpError;
export type HttpResponsePaginated<T> = HttpResponseOkPaginated<T> | HttpResponseLoading<T> | HttpError;
export interface RequestInfo {
url: string;
hasToken: boolean;
params: unknown;
data: unknown;
}
interface HttpResponseLoading<T> {
ok?: false;
loading: true;
clientError?: false;
serverError?: false;
data?: T;
}
export interface HttpResponseOk<T> {
ok: true;
loading?: false;
clientError?: false;
serverError?: false;
data: T;
info?: RequestInfo;
}
export type HttpResponseOkPaginated<T> = HttpResponseOk<T> & WithPagination
export interface WithPagination {
loadMore: () => void;
loadMorePrev: () => void;
isReachingEnd?: boolean;
isReachingStart?: boolean;
}
export type HttpError = HttpResponseClientError | HttpResponseServerError | HttpResponseUnexpectedError;
export interface SwrError {
info: unknown,
status: number,
message: string,
}
export interface HttpResponseServerError {
ok?: false;
loading?: false;
clientError?: false;
serverError: true;
error?: MerchantBackend.ErrorDetail;
status: number;
message: string;
info?: RequestInfo;
}
interface HttpResponseClientError {
ok?: false;
loading?: false;
clientError: true;
serverError?: false;
info?: RequestInfo;
isUnauthorized: boolean;
isNotfound: boolean;
status: number;
error?: MerchantBackend.ErrorDetail;
message: string;
}
interface HttpResponseUnexpectedError {
ok?: false;
loading?: false;
clientError?: false;
serverError?: false;
info?: RequestInfo;
status?: number;
error: unknown;
message: string;
}
type Methods = 'get' | 'post' | 'patch' | 'delete' | 'put';
interface RequestOptions {
method?: Methods;
token?: string;
data?: unknown;
params?: unknown;
}
function buildRequestOk<T>(res: AxiosResponse<T>, url: string, hasToken: boolean): HttpResponseOk<T> {
return {
ok: true, data: res.data, info: {
params: res.config.params,
data: res.config.data,
url,
hasToken,
}
}
}
// function buildResponse<T>(data?: T, error?: MerchantBackend.ErrorDetail, isValidating?: boolean): HttpResponse<T> {
// if (isValidating) return {loading: true}
// if (error) return buildRequestFailed()
// }
function buildRequestFailed(ex: AxiosError<MerchantBackend.ErrorDetail>, url: string, hasToken: boolean): HttpResponseClientError | HttpResponseServerError | HttpResponseUnexpectedError {
const status = ex.response?.status
const info: RequestInfo = {
data: ex.request?.data,
params: ex.request?.params,
url,
hasToken,
};
if (status && status >= 400 && status < 500) {
const error: HttpResponseClientError = {
clientError: true,
isNotfound: status === 404,
isUnauthorized: status === 401,
status,
info,
message: ex.response?.data?.hint || ex.message,
error: ex.response?.data
}
return error
}
if (status && status >= 500 && status < 600) {
const error: HttpResponseServerError = {
serverError: true,
status,
info,
message: `${ex.response?.data?.hint} (code ${ex.response?.data?.code})` || ex.message,
error: ex.response?.data
}
return error;
}
const error: HttpResponseUnexpectedError = {
info,
status,
error: ex,
message: ex.message
}
return error
}
const CancelToken = axios.CancelToken;
let source = CancelToken.source();
export function cancelPendingRequest() {
source.cancel('canceled by the user')
source = CancelToken.source()
}
let removeAxiosCancelToken = false
/**
* Jest mocking seems to break when using the cancelToken property.
* Using this workaround when testing while finding the correct solution
*/
export function setAxiosRequestAsTestingEnvironment() {
removeAxiosCancelToken = true
}
export async function request<T>(url: string, options: RequestOptions = {}): Promise<HttpResponseOk<T>> {
const headers = options.token ? { Authorization: `Bearer ${options.token}` } : undefined
try {
const res = await axios({
url,
responseType: 'json',
headers,
cancelToken: !removeAxiosCancelToken ? source.token : undefined,
method: options.method || 'get',
data: options.data,
params: options.params,
timeout: DEFAULT_REQUEST_TIMEOUT * 1000,
})
return buildRequestOk<T>(res, url, !!options.token)
} catch (e) {
if (axios.isAxiosError(e)) {
throw buildRequestFailed(e, url, !!options.token)
}
throw e
}
}
export function fetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> {
return request<T>(`${backend}${url}`, { token })
}
export function useBackendInstancesTestForAdmin(): HttpResponse<MerchantBackend.Instances.InstancesResponse> {
const { url, token } = useBackendContext()
type Type = MerchantBackend.Instances.InstancesResponse;
const [result, setResult] = useState<HttpResponse<Type>>({ loading: true })
useEffect(() => {
request<Type>(`${url}/management/instances`, { token })
.then(data => setResult(data))
.catch(error => setResult(error))
}, [url, token])
return result
}
export function useBackendConfig(): HttpResponse<MerchantBackend.VersionResponse> {
const { url, token } = useBackendContext()
type Type = MerchantBackend.VersionResponse;
const [result, setResult] = useState<HttpResponse<Type>>({ loading: true })
useEffect(() => {
request<Type>(`${url}/config`, { token })
.then(data => setResult(data))
.catch(error => setResult(error))
}, [url, token])
return result
}

View File

@ -1,110 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { StateUpdater, useCallback, useState } from "preact/hooks";
import { ValueOrFunction } from '../utils/types';
const calculateRootPath = () => {
const rootPath = typeof window !== undefined ? window.location.origin + window.location.pathname : '/'
return rootPath
}
export function useBackendURL(url?: string): [string, boolean, StateUpdater<string>, () => void] {
const [value, setter] = useNotNullLocalStorage('backend-url', url || calculateRootPath())
const [triedToLog, setTriedToLog] = useLocalStorage('tried-login')
const checkedSetter = (v: ValueOrFunction<string>) => {
setTriedToLog('yes')
return setter(p => (v instanceof Function ? v(p) : v).replace(/\/$/, ''))
}
const resetBackend = () => {
setTriedToLog(undefined)
}
return [value, !!triedToLog, checkedSetter, resetBackend]
}
export function useBackendDefaultToken(): [string | undefined, StateUpdater<string | undefined>] {
return useLocalStorage('backend-token')
}
export function useBackendInstanceToken(id: string): [string | undefined, StateUpdater<string | undefined>] {
const [token, setToken] = useLocalStorage(`backend-token-${id}`)
const [defaultToken, defaultSetToken] = useBackendDefaultToken()
// instance named 'default' use the default token
if (id === 'default') {
return [defaultToken, defaultSetToken]
}
return [token, setToken]
}
export function useLang(initial?: string): [string, StateUpdater<string>] {
const browserLang = typeof window !== "undefined" ? navigator.language || (navigator as any).userLanguage : undefined;
const defaultLang = (browserLang || initial || 'en').substring(0, 2)
return useNotNullLocalStorage('lang-preference', defaultLang)
}
export function useLocalStorage(key: string, initialValue?: string): [string | undefined, StateUpdater<string | undefined>] {
const [storedValue, setStoredValue] = useState<string | undefined>((): string | undefined => {
return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue;
});
const setValue = (value?: string | ((val?: string) => string | undefined)) => {
setStoredValue(p => {
const toStore = value instanceof Function ? value(p) : value
if (typeof window !== "undefined") {
if (!toStore) {
window.localStorage.removeItem(key)
} else {
window.localStorage.setItem(key, toStore);
}
}
return toStore
})
};
return [storedValue, setValue];
}
export function useNotNullLocalStorage(key: string, initialValue: string): [string, StateUpdater<string>] {
const [storedValue, setStoredValue] = useState<string>((): string => {
return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue;
});
const setValue = (value: string | ((val: string) => string)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
if (typeof window !== "undefined") {
if (!valueToStore) {
window.localStorage.removeItem(key)
} else {
window.localStorage.setItem(key, valueToStore);
}
}
};
return [storedValue, setValue];
}

View File

@ -1,187 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { MerchantBackend } from '../declaration';
import { useBackendContext } from '../context/backend';
import { fetcher, HttpError, HttpResponse, HttpResponseOk, request, SwrError } from './backend';
import useSWR, { mutate } from 'swr';
import { useInstanceContext } from '../context/instance';
interface InstanceAPI {
updateInstance: (data: MerchantBackend.Instances.InstanceReconfigurationMessage) => Promise<void>;
deleteInstance: () => Promise<void>;
clearToken: () => Promise<void>;
setNewToken: (token: string) => Promise<void>;
}
export function useManagementAPI(instanceId: string) : InstanceAPI {
const { url, token } = useBackendContext()
const updateInstance = async (instance: MerchantBackend.Instances.InstanceReconfigurationMessage): Promise<void> => {
await request(`${url}/management/instances/${instanceId}`, {
method: 'patch',
token,
data: instance
})
mutate([`/private/`, token, url], null)
};
const deleteInstance = async (): Promise<void> => {
await request(`${url}/management/instances/${instanceId}`, {
method: 'delete',
token,
})
mutate([`/private/`, token, url], null)
}
const clearToken = async (): Promise<void> => {
await request(`${url}/management/instances/${instanceId}/auth`, {
method: 'post',
token,
data: { method: 'external' }
})
mutate([`/private/`, token, url], null)
}
const setNewToken = async (newToken: string): Promise<void> => {
await request(`${url}/management/instances/${instanceId}/auth`, {
method: 'post',
token,
data: { method: 'token', token: newToken }
})
mutate([`/private/`, token, url], null)
}
return { updateInstance, deleteInstance, setNewToken, clearToken }
}
export function useInstanceAPI(): InstanceAPI {
const { url: baseUrl, token: adminToken } = useBackendContext()
const { token: instanceToken, id, admin } = useInstanceContext()
const { url, token } = !admin ? {
url: baseUrl, token: adminToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
};
const updateInstance = async (instance: MerchantBackend.Instances.InstanceReconfigurationMessage): Promise<void> => {
await request(`${url}/private/`, {
method: 'patch',
token,
data: instance
})
if (adminToken) mutate(['/private/instances', adminToken, baseUrl], null)
mutate([`/private/`, token, url], null)
};
const deleteInstance = async (): Promise<void> => {
await request(`${url}/private/`, {
method: 'delete',
token: adminToken,
})
if (adminToken) mutate(['/private/instances', adminToken, baseUrl], null)
mutate([`/private/`, token, url], null)
}
const clearToken = async (): Promise<void> => {
await request(`${url}/private/auth`, {
method: 'post',
token,
data: { method: 'external' }
})
mutate([`/private/`, token, url], null)
}
const setNewToken = async (newToken: string): Promise<void> => {
await request(`${url}/private/auth`, {
method: 'post',
token,
data: { method: 'token', token: newToken }
})
mutate([`/private/`, token, url], null)
}
return { updateInstance, deleteInstance, setNewToken, clearToken }
}
export function useInstanceDetails(): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin ? {
url: baseUrl, token: baseToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, HttpError>([`/private/`, token, url], fetcher, {
refreshInterval:0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
errorRetryCount: 0,
errorRetryInterval: 1,
shouldRetryOnError: false,
})
if (isValidating) return {loading:true, data: data?.data}
if (data) return data
if (error) return error
return {loading: true}
}
export function useManagedInstanceDetails(instanceId: string): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> {
const { url, token } = useBackendContext();
const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, HttpError>([`/management/instances/${instanceId}`, token, url], fetcher, {
refreshInterval:0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
errorRetryCount: 0,
errorRetryInterval: 1,
shouldRetryOnError: false,
})
if (isValidating) return {loading:true, data: data?.data}
if (data) return data
if (error) return error
return {loading: true}
}
export function useBackendInstances(): HttpResponse<MerchantBackend.Instances.InstancesResponse> {
const { url } = useBackendContext()
const { token } = useInstanceContext();
const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.InstancesResponse>, HttpError>(['/management/instances', token, url], fetcher)
if (isValidating) return {loading:true, data: data?.data}
if (data) return data
if (error) return error
return {loading: true}
}

View File

@ -1,68 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useState } from "preact/hooks";
/**
* returns subscriber and activator
* subscriber will receive a method (listener) that will be call when the activator runs.
* the result of calling the listener will be sent to @action
*
* @param action from <T> to <R>
* @returns activator and subscriber, undefined activator means that there is not subscriber
*/
export function useListener<T, R = any>(action: (r: T) => Promise<R>): [undefined | (() => Promise<R>), (listener?: () => T) => void] {
type RunnerHandler = { toBeRan?: () => Promise<R>; };
const [state, setState] = useState<RunnerHandler>({});
/**
* subscriber will receive a method that will be call when the activator runs
*
* @param listener function to be run when the activator runs
*/
const subscriber = (listener?: () => T) => {
if (listener) {
setState({
toBeRan: () => {
const whatWeGetFromTheListener = listener();
return action(whatWeGetFromTheListener);
}
});
} else {
setState({
toBeRan: undefined
})
}
};
/**
* activator will call runner if there is someone subscribed
*/
const activator = state.toBeRan ? async () => {
if (state.toBeRan) {
return state.toBeRan();
}
return Promise.reject();
} : undefined;
return [activator, subscriber];
}

View File

@ -1,43 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useCallback, useState } from "preact/hooks";
import { Notification } from '../utils/types';
interface Result {
notification?: Notification;
pushNotification: (n: Notification) => void;
removeNotification: () => void;
}
export function useNotification(): Result {
const [notification, setNotifications] = useState<Notification|undefined>(undefined)
const pushNotification = useCallback((n: Notification): void => {
setNotifications(n)
},[])
const removeNotification = useCallback(() => {
setNotifications(undefined)
},[])
return { notification, pushNotification, removeNotification }
}

View File

@ -1,48 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useState } from "preact/hooks";
import { Notification } from '../utils/types';
interface Result {
notifications: Notification[];
pushNotification: (n: Notification) => void;
removeNotification: (n: Notification) => void;
}
type NotificationWithDate = Notification & { since: Date }
export function useNotifications(initial: Notification[] = [], timeout = 3000): Result {
const [notifications, setNotifications] = useState<(NotificationWithDate)[]>(initial.map(i => ({...i, since: new Date() })))
const pushNotification = (n: Notification): void => {
const entry = { ...n, since: new Date() }
setNotifications(ns => [...ns, entry])
if (n.type !== 'ERROR') setTimeout(() => {
setNotifications(ns => ns.filter(x => x.since !== entry.since))
}, timeout)
}
const removeNotification = (notif: Notification) => {
setNotifications((ns: NotificationWithDate[]) => ns.filter(n => n !== notif))
}
return { notifications, pushNotification, removeNotification }
}

View File

@ -1,217 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { useEffect, useState } from 'preact/hooks';
import useSWR from 'swr';
import { useBackendContext } from '../context/backend';
import { useInstanceContext } from '../context/instance';
import { MerchantBackend } from '../declaration';
import { MAX_RESULT_SIZE, PAGE_SIZE } from '../utils/constants';
import { fetcher, HttpError, HttpResponse, HttpResponseOk, HttpResponsePaginated, mutateAll, request } from './backend';
export interface OrderAPI {
//FIXME: add OutOfStockResponse on 410
createOrder: (data: MerchantBackend.Orders.PostOrderRequest) => Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>>;
forgetOrder: (id: string, data: MerchantBackend.Orders.ForgetRequest) => Promise<HttpResponseOk<void>>;
refundOrder: (id: string, data: MerchantBackend.Orders.RefundRequest) => Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>>;
deleteOrder: (id: string) => Promise<HttpResponseOk<void>>;
getPaymentURL: (id: string) => Promise<HttpResponseOk<string>>;
}
type YesOrNo = 'yes' | 'no';
export function orderFetcher<T>(url: string, token: string, backend: string, paid?: YesOrNo, refunded?: YesOrNo, wired?: YesOrNo, searchDate?: Date, delta?: number): Promise<HttpResponseOk<T>> {
const date_ms = delta && delta < 0 && searchDate ? searchDate.getTime() + 1 : searchDate?.getTime()
const params: any = {}
if (paid !== undefined) params.paid = paid
if (delta !== undefined) params.delta = delta
if (refunded !== undefined) params.refunded = refunded
if (wired !== undefined) params.wired = wired
if (date_ms !== undefined) params.date_ms = date_ms
return request<T>(`${backend}${url}`, { token, params })
}
export function useOrderAPI(): OrderAPI {
const { url: baseUrl, token: adminToken } = useBackendContext()
const { token: instanceToken, id, admin } = useInstanceContext()
const { url, token } = !admin ? {
url: baseUrl, token: adminToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
const createOrder = async (data: MerchantBackend.Orders.PostOrderRequest): Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>> => {
const res = await request<MerchantBackend.Orders.PostOrderResponse>(`${url}/private/orders`, {
method: 'post',
token,
data
})
await mutateAll(/@"\/private\/orders"@/)
return res
}
const refundOrder = async (orderId: string, data: MerchantBackend.Orders.RefundRequest): Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>> => {
mutateAll(/@"\/private\/orders"@/)
return request<MerchantBackend.Orders.MerchantRefundResponse>(`${url}/private/orders/${orderId}/refund`, {
method: 'post',
token,
data
})
// return res
}
const forgetOrder = async (orderId: string, data: MerchantBackend.Orders.ForgetRequest): Promise<HttpResponseOk<void>> => {
mutateAll(/@"\/private\/orders"@/)
return request(`${url}/private/orders/${orderId}/forget`, {
method: 'patch',
token,
data
})
}
const deleteOrder = async (orderId: string): Promise<HttpResponseOk<void>> => {
mutateAll(/@"\/private\/orders"@/)
return request(`${url}/private/orders/${orderId}`, {
method: 'delete',
token
})
}
const getPaymentURL = async (orderId: string): Promise<HttpResponseOk<string>> => {
return request<MerchantBackend.Orders.MerchantOrderStatusResponse>(`${url}/private/orders/${orderId}`, {
method: 'get',
token
}).then((res) => {
const url = res.data.order_status === "unpaid" ? res.data.taler_pay_uri : res.data.contract_terms.fulfillment_url
const response: HttpResponseOk<string> = res as any
response.data = url || ''
return response
})
}
return { createOrder, forgetOrder, deleteOrder, refundOrder, getPaymentURL }
}
export function useOrderDetails(oderId: string): HttpResponse<MerchantBackend.Orders.MerchantOrderStatusResponse> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin ? {
url: baseUrl, token: baseToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
};
const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Orders.MerchantOrderStatusResponse>, HttpError>([`/private/orders/${oderId}`, token, url], fetcher, {
refreshInterval: 0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
})
if (isValidating) return { loading: true, data: data?.data }
if (data) return data
if (error) return error
return { loading: true }
}
export interface InstanceOrderFilter {
paid?: YesOrNo;
refunded?: YesOrNo;
wired?: YesOrNo;
date?: Date;
}
export function useInstanceOrders(args?: InstanceOrderFilter, updateFilter?: (d: Date) => void): HttpResponsePaginated<MerchantBackend.Orders.OrderHistory> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin ? {
url: baseUrl, token: baseToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
const [pageBefore, setPageBefore] = useState(1)
const [pageAfter, setPageAfter] = useState(1)
const totalAfter = pageAfter * PAGE_SIZE;
const totalBefore = args?.date ? pageBefore * PAGE_SIZE : 0;
/**
* FIXME: this can be cleaned up a little
*
* the logic of double query should be inside the orderFetch so from the hook perspective and cache
* is just one query and one error status
*/
const { data: beforeData, error: beforeError, isValidating: loadingBefore } = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>(
[`/private/orders`, token, url, args?.paid, args?.refunded, args?.wired, args?.date, totalBefore],
orderFetcher,
)
const { data: afterData, error: afterError, isValidating: loadingAfter } = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>(
[`/private/orders`, token, url, args?.paid, args?.refunded, args?.wired, args?.date, -totalAfter],
orderFetcher,
)
//this will save last result
const [lastBefore, setLastBefore] = useState<HttpResponse<MerchantBackend.Orders.OrderHistory>>({ loading: true })
const [lastAfter, setLastAfter] = useState<HttpResponse<MerchantBackend.Orders.OrderHistory>>({ loading: true })
useEffect(() => {
if (afterData) setLastAfter(afterData)
if (beforeData) setLastBefore(beforeData)
}, [afterData, beforeData])
// this has problems when there are some ids missing
if (beforeError) return beforeError
if (afterError) return afterError
const pagination = {
isReachingEnd: afterData && afterData.data.orders.length < totalAfter,
isReachingStart: (!args?.date) || (beforeData && beforeData.data.orders.length < totalBefore),
loadMore: () => {
if (!afterData) return
if (afterData.data.orders.length < MAX_RESULT_SIZE) {
setPageAfter(pageAfter + 1)
} else {
const from = afterData.data.orders[afterData.data.orders.length - 1].timestamp.t_s
if (from && updateFilter) updateFilter(new Date(from))
}
},
loadMorePrev: () => {
if (!beforeData) return
if (beforeData.data.orders.length < MAX_RESULT_SIZE) {
setPageBefore(pageBefore + 1)
} else if (beforeData) {
const from = beforeData.data.orders[beforeData.data.orders.length - 1].timestamp.t_s
if (from && updateFilter) updateFilter(new Date(from))
}
},
}
const orders = !beforeData || !afterData ? [] : (beforeData || lastBefore).data.orders.slice().reverse().concat((afterData || lastAfter).data.orders)
if (loadingAfter || loadingBefore) return { loading: true, data: { orders } }
if (beforeData && afterData) {
return { ok: true, data: { orders }, ...pagination }
}
return { loading: true }
}

View File

@ -1,223 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { useEffect } from "preact/hooks";
import useSWR, { trigger, useSWRInfinite, cache, mutate } from "swr";
import { useBackendContext } from "../context/backend";
// import { useFetchContext } from '../context/fetch';
import { useInstanceContext } from "../context/instance";
import { MerchantBackend, WithId } from "../declaration";
import {
fetcher,
HttpError,
HttpResponse,
HttpResponseOk,
mutateAll,
request,
} from "./backend";
export interface ProductAPI {
createProduct: (
data: MerchantBackend.Products.ProductAddDetail
) => Promise<void>;
updateProduct: (
id: string,
data: MerchantBackend.Products.ProductPatchDetail
) => Promise<void>;
deleteProduct: (id: string) => Promise<void>;
lockProduct: (
id: string,
data: MerchantBackend.Products.LockRequest
) => Promise<void>;
}
export function useProductAPI(): ProductAPI {
const { url: baseUrl, token: adminToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin
? {
url: baseUrl,
token: adminToken,
}
: {
url: `${baseUrl}/instances/${id}`,
token: instanceToken,
};
const createProduct = async (
data: MerchantBackend.Products.ProductAddDetail
): Promise<void> => {
await request(`${url}/private/products`, {
method: "post",
token,
data,
});
await mutateAll(/@"\/private\/products"@/, null);
};
const updateProduct = async (
productId: string,
data: MerchantBackend.Products.ProductPatchDetail
): Promise<void> => {
const r = await request(`${url}/private/products/${productId}`, {
method: "patch",
token,
data,
});
await mutateAll(/@"\/private\/products\/.*"@/);
return Promise.resolve();
};
const deleteProduct = async (productId: string): Promise<void> => {
await request(`${url}/private/products/${productId}`, {
method: "delete",
token,
});
await mutateAll(/@"\/private\/products"@/);
};
const lockProduct = async (
productId: string,
data: MerchantBackend.Products.LockRequest
): Promise<void> => {
await request(`${url}/private/products/${productId}/lock`, {
method: "post",
token,
data,
});
await mutateAll(/@"\/private\/products"@/);
};
return { createProduct, updateProduct, deleteProduct, lockProduct };
}
export function useInstanceProducts(): HttpResponse<
(MerchantBackend.Products.ProductDetail & WithId)[]
> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
// const { useSWR, useSWRInfinite } = useFetchContext();
const { url, token } = !admin
? {
url: baseUrl,
token: baseToken,
}
: {
url: `${baseUrl}/instances/${id}`,
token: instanceToken,
};
const {
data: list,
error: listError,
isValidating: listLoading,
} = useSWR<
HttpResponseOk<MerchantBackend.Products.InventorySummaryResponse>,
HttpError
>([`/private/products`, token, url], fetcher, {
refreshInterval: 0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
});
const {
data: products,
error: productError,
setSize,
size,
} = useSWRInfinite<
HttpResponseOk<MerchantBackend.Products.ProductDetail>,
HttpError
>(
(pageIndex: number) => {
if (!list?.data || !list.data.products.length || listError || listLoading)
return null;
return [
`/private/products/${list.data.products[pageIndex].product_id}`,
token,
url,
];
},
fetcher,
{
revalidateAll: true,
}
);
useEffect(() => {
if (list?.data && list.data.products.length > 0) {
setSize(list.data.products.length);
}
}, [list?.data.products.length, listLoading]);
if (listLoading) return { loading: true, data: [] };
if (listError) return listError;
if (productError) return productError;
if (list?.data && list.data.products.length === 0) {
return { ok: true, data: [] };
}
if (products) {
const dataWithId = products.map((d) => {
//take the id from the queried url
return {
...d.data,
id: d.info?.url.replace(/.*\/private\/products\//, "") || "",
};
});
return { ok: true, data: dataWithId };
}
return { loading: true };
}
export function useProductDetails(
productId: string
): HttpResponse<MerchantBackend.Products.ProductDetail> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin
? {
url: baseUrl,
token: baseToken,
}
: {
url: `${baseUrl}/instances/${id}`,
token: instanceToken,
};
const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Products.ProductDetail>,
HttpError
>([`/private/products/${productId}`, token, url], fetcher, {
refreshInterval: 0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
});
if (isValidating) return { loading: true, data: data?.data };
if (data) return data;
if (error) return error;
return { loading: true };
}

View File

@ -1,159 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import useSWR from 'swr';
import { useBackendContext } from '../context/backend';
import { useInstanceContext } from '../context/instance';
import { MerchantBackend } from '../declaration';
import { fetcher, HttpError, HttpResponse, HttpResponseOk, mutateAll, request } from './backend';
export function useReservesAPI(): ReserveMutateAPI {
const { url: baseUrl, token: adminToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin ? {
url: baseUrl, token: adminToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
};
const createReserve = async (data: MerchantBackend.Tips.ReserveCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>> => {
const res = await request<MerchantBackend.Tips.ReserveCreateConfirmation>(`${url}/private/reserves`, {
method: 'post',
token,
data
});
await mutateAll(/@"\/private\/reserves"@/);
return res
};
const authorizeTipReserve = async (pub: string, data: MerchantBackend.Tips.TipCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(`${url}/private/reserves/${pub}/authorize-tip`, {
method: 'post',
token,
data
});
await mutateAll(/@"\/private\/reserves"@/);
return res
};
const authorizeTip = async (data: MerchantBackend.Tips.TipCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(`${url}/private/tips`, {
method: 'post',
token,
data
});
await mutateAll(/@"\/private\/reserves"@/);
return res
};
const deleteReserve = async (pub: string): Promise<HttpResponse<void>> => {
const res = await request<void>(`${url}/private/reserves/${pub}`, {
method: 'delete',
token,
});
await mutateAll(/@"\/private\/reserves"@/);
return res
};
return { createReserve, authorizeTip, authorizeTipReserve, deleteReserve };
}
export interface ReserveMutateAPI {
createReserve: (data: MerchantBackend.Tips.ReserveCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>>;
authorizeTipReserve: (id: string, data: MerchantBackend.Tips.TipCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
authorizeTip: (data: MerchantBackend.Tips.TipCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
deleteReserve: (id: string) => Promise<HttpResponse<void>>;
}
export function useInstanceTips(): HttpResponse<MerchantBackend.Tips.TippingReserveStatus> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin ? {
url: baseUrl, token: baseToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>, HttpError>([`/private/reserves`, token, url], fetcher)
if (isValidating) return { loading: true, data: data?.data }
if (data) return data
if (error) return error
return { loading: true }
}
export function useReserveDetails(reserveId: string): HttpResponse<MerchantBackend.Tips.ReserveDetail> {
const { url: baseUrl } = useBackendContext();
const { token, id: instanceId, admin } = useInstanceContext();
const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}`
const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.ReserveDetail>, HttpError>([`/private/reserves/${reserveId}`, token, url], reserveDetailFetcher, {
refreshInterval:0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
})
if (isValidating) return { loading: true, data: data?.data }
if (data) return data
if (error) return error
return { loading: true }
}
export function useTipDetails(tipId: string): HttpResponse<MerchantBackend.Tips.TipDetails> {
const { url: baseUrl } = useBackendContext();
const { token, id: instanceId, admin } = useInstanceContext();
const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}`
const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.TipDetails>, HttpError>([`/private/tips/${tipId}`, token, url], tipsDetailFetcher, {
refreshInterval:0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
})
if (isValidating) return { loading: true, data: data?.data }
if (data) return data
if (error) return error
return { loading: true }
}
export function reserveDetailFetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> {
return request<T>(`${backend}${url}`, { token, params: {
tips: 'yes'
} })
}
export function tipsDetailFetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> {
return request<T>(`${backend}${url}`, { token, params: {
pickups: 'yes'
} })
}

View File

@ -1,150 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { MerchantBackend } from '../declaration';
import { useBackendContext } from '../context/backend';
import { request, mutateAll, HttpResponse, HttpError, HttpResponseOk, HttpResponsePaginated } from './backend';
import useSWR from 'swr';
import { useInstanceContext } from '../context/instance';
import { MAX_RESULT_SIZE, PAGE_SIZE } from '../utils/constants';
import { useEffect, useState } from 'preact/hooks';
async function transferFetcher<T>(url: string, token: string, backend: string, payto_uri?: string, verified?: string, position?: string, delta?: number): Promise<HttpResponseOk<T>> {
const params: any = {}
if (payto_uri !== undefined) params.payto_uri = payto_uri
if (verified !== undefined) params.verified = verified
if (delta !== undefined) {
// if (delta > 0) {
// params.after = searchDate?.getTime()
// } else {
// params.before = searchDate?.getTime()
// }
params.limit = delta
}
if (position !== undefined) params.offset = position
return request<T>(`${backend}${url}`, { token, params })
}
export function useTransferAPI(): TransferAPI {
const { url: baseUrl, token: adminToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin ? {
url: baseUrl, token: adminToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
};
const informTransfer = async (data: MerchantBackend.Transfers.TransferInformation): Promise<HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse>> => {
mutateAll(/@"\/private\/transfers"@/);
return request<MerchantBackend.Transfers.MerchantTrackTransferResponse>(`${url}/private/transfers`, {
method: 'post',
token,
data
});
};
return { informTransfer };
}
export interface TransferAPI {
informTransfer: (data: MerchantBackend.Transfers.TransferInformation) => Promise<HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse>>;
}
export interface InstanceTransferFilter {
payto_uri?: string;
verified?: 'yes' | 'no';
position?: string;
}
export function useInstanceTransfers(args?: InstanceTransferFilter, updatePosition?: (id: string) => void): HttpResponsePaginated<MerchantBackend.Transfers.TransferList> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
const { url, token } = !admin ? {
url: baseUrl, token: baseToken
} : {
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
const [pageBefore, setPageBefore] = useState(1)
const [pageAfter, setPageAfter] = useState(1)
const totalAfter = pageAfter * PAGE_SIZE;
const totalBefore = args?.position !== undefined ? pageBefore * PAGE_SIZE : 0;
/**
* FIXME: this can be cleaned up a little
*
* the logic of double query should be inside the orderFetch so from the hook perspective and cache
* is just one query and one error status
*/
const { data: beforeData, error: beforeError, isValidating: loadingBefore } = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>(
[`/private/transfers`, token, url, args?.payto_uri, args?.verified, args?.position, totalBefore],
transferFetcher,
)
const { data: afterData, error: afterError, isValidating: loadingAfter } = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>(
[`/private/transfers`, token, url, args?.payto_uri, args?.verified, args?.position, -totalAfter],
transferFetcher,
)
//this will save last result
const [lastBefore, setLastBefore] = useState<HttpResponse<MerchantBackend.Transfers.TransferList>>({ loading: true })
const [lastAfter, setLastAfter] = useState<HttpResponse<MerchantBackend.Transfers.TransferList>>({ loading: true })
useEffect(() => {
if (afterData) setLastAfter(afterData)
if (beforeData) setLastBefore(beforeData)
}, [afterData, beforeData])
// this has problems when there are some ids missing
if (beforeError) return beforeError
if (afterError) return afterError
const pagination = {
isReachingEnd: afterData && afterData.data.transfers.length < totalAfter,
isReachingStart: (!args?.position) || (beforeData && beforeData.data.transfers.length < totalBefore),
loadMore: () => {
if (!afterData) return
if (afterData.data.transfers.length < MAX_RESULT_SIZE) {
setPageAfter(pageAfter + 1)
} else {
const from = `${afterData.data.transfers[afterData.data.transfers.length - 1].transfer_serial_id}`
if (from && updatePosition) updatePosition(from)
}
},
loadMorePrev: () => {
if (!beforeData) return
if (beforeData.data.transfers.length < MAX_RESULT_SIZE) {
setPageBefore(pageBefore + 1)
} else if (beforeData) {
const from = `${beforeData.data.transfers[beforeData.data.transfers.length - 1].transfer_serial_id}`
if (from && updatePosition) updatePosition(from)
}
},
}
const transfers = !beforeData || !afterData ? [] : (beforeData || lastBefore).data.transfers.slice().reverse().concat((afterData || lastAfter).data.transfers)
if (loadingAfter || loadingBefore) return { loading: true, data: { transfers } }
if (beforeData && afterData) {
return { ok: true, data: { transfers }, ...pagination }
}
return { loading: true }
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,203 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Translation helpers for React components and template literals.
*/
/**
* Imports
*/
import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact";
import { useTranslationContext } from "../context/translation";
export function useTranslator() {
const ctx = useTranslationContext();
const jed = ctx.handler
return function str(stringSeq: TemplateStringsArray, ...values: any[]): string {
const s = toI18nString(stringSeq);
if (!s) return s
const tr = jed
.translate(s)
.ifPlural(1, s)
.fetch(...values);
return tr;
}
}
/**
* Convert template strings to a msgid
*/
function toI18nString(stringSeq: ReadonlyArray<string>): string {
let s = "";
for (let i = 0; i < stringSeq.length; i++) {
s += stringSeq[i];
if (i < stringSeq.length - 1) {
s += `%${i + 1}$s`;
}
}
return s;
}
interface TranslateSwitchProps {
target: number;
children: ComponentChildren;
}
function stringifyChildren(children: ComponentChildren): string {
let n = 1;
const ss = (children instanceof Array ? children : [children]).map((c) => {
if (typeof c === "string") {
return c;
}
return `%${n++}$s`;
});
const s = ss.join("").replace(/ +/g, " ").trim();
return s;
}
interface TranslateProps {
children: ComponentChildren;
/**
* Component that the translated element should be wrapped in.
* Defaults to "div".
*/
wrap?: any;
/**
* Props to give to the wrapped component.
*/
wrapProps?: any;
}
function getTranslatedChildren(
translation: string,
children: ComponentChildren,
): ComponentChild[] {
const tr = translation.split(/%(\d+)\$s/);
const childArray = children instanceof Array ? children : [children];
// Merge consecutive string children.
const placeholderChildren = Array<ComponentChild>();
for (let i = 0; i < childArray.length; i++) {
const x = childArray[i];
if (x === undefined) {
continue;
} else if (typeof x === "string") {
continue;
} else {
placeholderChildren.push(x);
}
}
const result = Array<ComponentChild>();
for (let i = 0; i < tr.length; i++) {
if (i % 2 == 0) {
// Text
result.push(tr[i]);
} else {
const childIdx = Number.parseInt(tr[i],10) - 1;
result.push(placeholderChildren[childIdx]);
}
}
return result;
}
/**
* Translate text node children of this component.
* If a child component might produce a text node, it must be wrapped
* in a another non-text element.
*
* Example:
* ```
* <Translate>
* Hello. Your score is <span><PlayerScore player={player} /></span>
* </Translate>
* ```
*/
export function Translate({ children }: TranslateProps): VNode {
const s = stringifyChildren(children);
const ctx = useTranslationContext()
const translation: string = ctx.handler.ngettext(s, s, 1);
const result = getTranslatedChildren(translation, children)
return <Fragment>{result}</Fragment>;
}
/**
* Switch translation based on singular or plural based on the target prop.
* Should only contain TranslateSingular and TransplatePlural as children.
*
* Example:
* ```
* <TranslateSwitch target={n}>
* <TranslateSingular>I have {n} apple.</TranslateSingular>
* <TranslatePlural>I have {n} apples.</TranslatePlural>
* </TranslateSwitch>
* ```
*/
export function TranslateSwitch({ children, target }: TranslateSwitchProps) {
let singular: VNode<TranslationPluralProps> | undefined;
let plural: VNode<TranslationPluralProps> | undefined;
// const children = this.props.children;
if (children) {
(children instanceof Array ? children : [children]).forEach((child: any) => {
if (child.type === TranslatePlural) {
plural = child;
}
if (child.type === TranslateSingular) {
singular = child;
}
});
}
if (!singular || !plural) {
console.error("translation not found");
return h("span", {}, ["translation not found"]);
}
singular.props.target = target;
plural.props.target = target;
// We're looking up the translation based on the
// singular, even if we must use the plural form.
return singular;
}
interface TranslationPluralProps {
children: ComponentChildren;
target: number;
}
/**
* See [[TranslateSwitch]].
*/
export function TranslatePlural({ children, target }: TranslationPluralProps): VNode {
const s = stringifyChildren(children);
const ctx = useTranslationContext()
const translation = ctx.handler.ngettext(s, s, 1);
const result = getTranslatedChildren(translation, children);
return <Fragment>{result}</Fragment>;
}
/**
* See [[TranslateSwitch]].
*/
export function TranslateSingular({ children, target }: TranslationPluralProps): VNode {
const s = stringifyChildren(children);
const ctx = useTranslationContext()
const translation = ctx.handler.ngettext(s, s, target);
const result = getTranslatedChildren(translation, children);
return <Fragment>{result}</Fragment>;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
# This file is part of GNU Taler
# (C) 2021 Taler Systems S.A.
# GNU Taler is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3, or (at your option) any later version.
# GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with
# GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Taler Wallet\n"
"Report-Msgid-Bugs-To: taler@gnu.org\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { Fragment, h, VNode } from "preact";
import { BackendContextProvider } from "./context/backend";
import { TranslationProvider } from "./context/translation";
// import { Page as RequestPayment } from './RequestPayment';
import { Route, Router } from "preact-router";
import { Footer } from "./components/Footer";
import "./css/pure-min.css";
export default function Application(): VNode {
return (
// <FetchContextProvider>
<BackendContextProvider>
<TranslationProvider>
<ApplicationStatusRoutes />
</TranslationProvider>
</BackendContextProvider>
// </FetchContextProvider>
);
}
function ApplicationStatusRoutes(): VNode {
return (
<Fragment>
<Router>
<Route default component={() => <div>hello!</div>} />
</Router>
<Footer />
</Fragment>
);
}

View File

@ -1,69 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { amountFractionalBase, AmountJson, Amounts } from "@gnu-taler/taler-util";
import { MerchantBackend } from "../declaration";
/**
* sums two prices,
* @param one
* @param two
* @returns
*/
const sumPrices = (one: string, two: string) => {
const [currency, valueOne] = one.split(':')
const [, valueTwo] = two.split(':')
return `${currency}:${parseInt(valueOne, 10) + parseInt(valueTwo, 10)}`
}
/**
* merge refund with the same description and a difference less than one minute
* @param prev list of refunds that will hold the merged refunds
* @param cur new refund to add to the list
* @returns list with the new refund, may be merged with the last
*/
export function mergeRefunds(prev: MerchantBackend.Orders.RefundDetails[], cur: MerchantBackend.Orders.RefundDetails) {
let tail;
if (prev.length === 0 || //empty list
cur.timestamp.t_s === 'never' || //current does not have timestamp
(tail = prev[prev.length - 1]).timestamp.t_s === 'never' || // last does not have timestamp
cur.reason !== tail.reason || //different reason
Math.abs(cur.timestamp.t_s - tail.timestamp.t_s) > 1000 * 60) {//more than 1 minute difference
prev.push(cur)
return prev
}
prev[prev.length - 1] = {
...tail,
amount: sumPrices(tail.amount, cur.amount)
}
return prev
}
export const rate = (one: string, two: string) => {
const a = Amounts.parseOrThrow(one)
const b = Amounts.parseOrThrow(two)
const af = toFloat(a)
const bf = toFloat(b)
if (bf === 0) return 0
return af / bf
}
function toFloat(amount: AmountJson) {
return amount.value + (amount.fraction / amountFractionalBase);
}

View File

@ -1,47 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
//https://tools.ietf.org/html/rfc8905
export const PAYTO_REGEX = /^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]+(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/
export const PAYTO_WIRE_METHOD_LOOKUP = /payto:\/\/([a-zA-Z][a-zA-Z0-9-.]+)\/.*/
export const AMOUNT_REGEX = /^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/
export const INSTANCE_ID_LOOKUP = /^\/instances\/([^/]*)\/?$/
export const AMOUNT_ZERO_REGEX = /^[a-zA-Z][a-zA-Z]*:0$/
export const CROCKFORD_BASE32_REGEX = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]+[*~$=U]*$/
export const URL_REGEX = /^((https?:)(\/\/\/?)([\w]*(?::[\w]*)?@)?([\d\w\.-]+)(?::(\d+))?)\/$/
// how much rows we add every time user hit load more
export const PAGE_SIZE = 20
// how bigger can be the result set
// after this threshold, load more with move the cursor
export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1;
// how much we will wait for all request, in seconds
export const DEFAULT_REQUEST_TIMEOUT = 10;
export const MAX_IMAGE_SIZE = 1024 * 1024;
export const INSTANCE_ID_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_.@-]+$/

View File

@ -1,37 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { WithId } from "../declaration";
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
export interface Actions<T extends WithId> {
element: T;
type: 'DELETE' | 'UPDATE';
}
function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
return value !== null && value !== undefined;
}
export function buildActions<T extends WithId>(instances: T[], selected: string[], action: 'DELETE'): Actions<T>[] {
return selected.map(id => instances.find(i => i.id === id))
.filter(notEmpty)
.map(id => ({ element: id, type: action }))
}

View File

@ -1,31 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { VNode } from "preact"
export interface KeyValue {
[key: string]: string;
}
export interface Notification {
message: string;
description?: string | VNode;
type: MessageType;
}
export type ValueOrFunction<T> = T | ((p: T) => T)
export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS'

View File

@ -0,0 +1,23 @@
// Simple plugin to trim extensions from the filename of relative import statements.
// Required to get linaria to work with `moduleResulution: "Node16"` imports.
// @author Florian Dold
module.exports = function({ types: t }) {
return {
name: "trim-extension",
visitor: {
ImportDeclaration: (x) => {
const src = x.node.source;
if (src.value.startsWith(".")) {
if (src.value.endsWith(".js")) {
const newVal = src.value.replace(/[.]js$/, "")
x.node.source = t.stringLiteral(newVal);
}
}
if (src.value.endsWith(".jsx")) {
const newVal = src.value.replace(/[.]jsx$/, "")
x.node.source = t.stringLiteral(newVal);
}
},
}
};
}

View File

@ -175,83 +175,44 @@ importers:
packages/merchant-backend-ui:
specifiers:
'@babel/core': 7.18.9
'@creativebulma/bulma-tooltip': ^1.2.0
'@gnu-taler/pogen': ^0.0.5
'@gnu-taler/taler-util': workspace:*
'@linaria/babel-preset': 3.0.0-beta.22
'@linaria/core': 3.0.0-beta.22
'@linaria/react': 3.0.0-beta.22
'@linaria/rollup': 3.0.0-beta.22
'@linaria/shaker': 3.0.0-beta.22
'@linaria/webpack-loader': 3.0.0-beta.22
'@rollup/plugin-alias': ^3.1.5
'@rollup/plugin-babel': ^5.3.0
'@rollup/plugin-commonjs': ^20.0.0
'@rollup/plugin-html': ^0.2.3
'@rollup/plugin-image': ^2.1.1
'@rollup/plugin-json': ^4.1.0
'@rollup/plugin-replace': ^3.0.0
'@rollup/plugin-typescript': '11'
'@types/history': ^4.7.8
'@types/mocha': ^8.2.2
'@types/mustache': ^4.1.2
'@typescript-eslint/eslint-plugin': ^4.22.0
'@typescript-eslint/parser': ^4.22.0
axios: ^0.21.1
babel-loader: ^8.2.2
base64-inline-loader: ^1.1.1
date-fns: ^2.21.1
eslint: ^7.25.0
eslint-config-preact: ^1.1.4
eslint-plugin-header: ^3.1.1
history: 4.10.1
jed: ^1.1.1
mustache: ^4.2.0
po2json: ^0.4.5
preact: 10.11.3
preact-render-to-string: ^5.1.19
preact-router: 3.2.1
qrcode-generator: ^1.4.4
rimraf: ^3.0.2
rollup: ^2.56.3
rollup-plugin-bundle-html: ^0.2.2
rollup-plugin-css-only: ^3.1.0
script-ext-html-webpack-plugin: ^2.1.5
sirv-cli: ^1.0.11
swr: ^0.5.5
ts-node: ^10.9.1
tslib: 2.4.0
typescript: 4.9.4
yup: ^0.32.9
dependencies:
'@gnu-taler/taler-util': link:../taler-util
axios: 0.21.4
date-fns: 2.29.3
history: 4.10.1
jed: 1.1.1
preact: 10.11.3
preact-router: 3.2.1_preact@10.11.3
qrcode-generator: 1.4.4
swr: 0.5.7
yup: 0.32.11
devDependencies:
'@babel/core': 7.18.9
'@creativebulma/bulma-tooltip': 1.2.0
'@gnu-taler/pogen': link:../pogen
'@linaria/babel-preset': 3.0.0-beta.22
'@linaria/core': 3.0.0-beta.22
'@linaria/react': 3.0.0-beta.22
'@linaria/rollup': 3.0.0-beta.22
'@linaria/shaker': 3.0.0-beta.22
'@linaria/webpack-loader': 3.0.0-beta.22
'@rollup/plugin-alias': 3.1.9_rollup@2.79.1
'@rollup/plugin-babel': 5.3.1_cwbsg774jzhqoll5t2xfwyzz54
'@rollup/plugin-commonjs': 20.0.0_rollup@2.79.1
'@rollup/plugin-html': 0.2.4_rollup@2.79.1
'@rollup/plugin-image': 2.1.1_rollup@2.79.1
'@rollup/plugin-json': 4.1.0_rollup@2.79.1
'@rollup/plugin-replace': 3.1.0_rollup@2.79.1
'@rollup/plugin-typescript': 11.0.0_ds5km5eydjug3un6mvhihxeysu
'@types/history': 4.7.11
'@types/mocha': 8.2.3
'@types/mustache': 4.2.1
'@typescript-eslint/eslint-plugin': 4.33.0_thyv6urrmcfyirt6hwk42bhgva
@ -265,11 +226,8 @@ importers:
po2json: 0.4.5
preact-render-to-string: 5.2.6_preact@10.11.3
rimraf: 3.0.2
rollup: 2.79.1
rollup-plugin-bundle-html: 0.2.2
rollup-plugin-css-only: 3.1.0_rollup@2.79.1
script-ext-html-webpack-plugin: 2.1.5
sirv-cli: 1.0.14
ts-node: 10.9.1_typescript@4.9.4
tslib: 2.4.0
typescript: 4.9.4
@ -3040,6 +2998,13 @@ packages:
resolution: {integrity: sha512-ooImbeXEBxf77cttbzA7X5rC5aAWm9UsXIGViFOnsqB+6M944GkB28S5R4UWRqjFd2iW4zGEkEifAU+q43pt2w==}
dev: true
/@cspotcode/source-map-support/0.8.1:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/trace-mapping': 0.3.9
dev: true
/@emotion/is-prop-valid/0.8.8:
resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
dependencies:
@ -3378,6 +3343,13 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@jridgewell/trace-mapping/0.3.9:
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@leichtgewicht/ip-codec/2.0.4:
resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==}
dev: true
@ -3479,16 +3451,6 @@ packages:
- supports-color
dev: true
/@linaria/rollup/3.0.0-beta.22:
resolution: {integrity: sha512-VSMOvDsuAC/GoIwt111v6T7NlK5xvwVi5sK6OUNBCAZLdBai72uPKQKniye6qLMsI9SUfzPbktjTPVP/Uffweg==}
engines: {node: ^12.16.0 || >=13.7.0}
dependencies:
'@linaria/babel-preset': 3.0.0-beta.22
'@rollup/pluginutils': 4.2.1
transitivePeerDependencies:
- supports-color
dev: true
/@linaria/shaker/3.0.0-beta.22:
resolution: {integrity: sha512-NOi71i/XfBJpBOT5eepRvv6B64IMdjsKwv+vxLW+IuFHx3wnqXgZsgimNK2qoXbpqy9xWsSEeB/4QA4m8GCUKQ==}
engines: {node: ^12.16.0 || >=13.7.0}
@ -3696,33 +3658,6 @@ packages:
webpack: 4.46.0
dev: true
/@rollup/plugin-alias/3.1.9_rollup@2.79.1:
resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==}
engines: {node: '>=8.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0
dependencies:
rollup: 2.79.1
slash: 3.0.0
dev: true
/@rollup/plugin-babel/5.3.1_cwbsg774jzhqoll5t2xfwyzz54:
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
engines: {node: '>= 10.0.0'}
peerDependencies:
'@babel/core': ^7.0.0
'@types/babel__core': ^7.1.9
rollup: ^1.20.0||^2.0.0
peerDependenciesMeta:
'@types/babel__core':
optional: true
dependencies:
'@babel/core': 7.18.9
'@babel/helper-module-imports': 7.18.6
'@rollup/pluginutils': 3.1.0_rollup@2.79.1
rollup: 2.79.1
dev: true
/@rollup/plugin-babel/5.3.1_vyv4jbhmcriklval33ak5sngky:
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
engines: {node: '>= 10.0.0'}
@ -3740,51 +3675,6 @@ packages:
rollup: 2.79.1
dev: true
/@rollup/plugin-commonjs/20.0.0_rollup@2.79.1:
resolution: {integrity: sha512-5K0g5W2Ol8hAcTHqcTBHiA7M58tfmYi1o9KxeJuuRNpGaTa5iLjcyemBitCBcKXaHamOBBEH2dGom6v6Unmqjg==}
engines: {node: '>= 8.0.0'}
peerDependencies:
rollup: ^2.38.3
dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.79.1
commondir: 1.0.1
estree-walker: 2.0.2
glob: 7.2.3
is-reference: 1.2.1
magic-string: 0.25.9
resolve: 1.22.1
rollup: 2.79.1
dev: true
/@rollup/plugin-html/0.2.4_rollup@2.79.1:
resolution: {integrity: sha512-x0qpNXxbmGa9Jnl4OX89AORPe2T/a4DqNK69BGRnEdaPKq6MdiUXSTam/eCkF5DxkQGcRcPq0L4vzr/E3q4mVA==}
engines: {node: '>= 8.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0
dependencies:
rollup: 2.79.1
dev: true
/@rollup/plugin-image/2.1.1_rollup@2.79.1:
resolution: {integrity: sha512-AgP4U85zuQJdUopLUCM+hTf45RepgXeTb8EJsleExVy99dIoYpt3ZlDYJdKmAc2KLkNntCDg6BPJvgJU3uGF+g==}
engines: {node: '>= 8.0.0'}
peerDependencies:
rollup: ^1.20.0 || ^2.0.0
dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.79.1
mini-svg-data-uri: 1.4.4
rollup: 2.79.1
dev: true
/@rollup/plugin-json/4.1.0_rollup@2.79.1:
resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==}
peerDependencies:
rollup: ^1.20.0 || ^2.0.0
dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.79.1
rollup: 2.79.1
dev: true
/@rollup/plugin-node-resolve/11.2.1_rollup@2.79.1:
resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==}
engines: {node: '>= 10.0.0'}
@ -3810,36 +3700,6 @@ packages:
rollup: 2.79.1
dev: true
/@rollup/plugin-replace/3.1.0_rollup@2.79.1:
resolution: {integrity: sha512-pA3XRUrSKybVYqmH5TqWNZpGxF+VV+1GrYchKgCNIj2vsSOX7CVm2RCtx8p2nrC7xvkziYyK+lSi74T93MU3YA==}
peerDependencies:
rollup: ^1.20.0 || ^2.0.0
dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.79.1
magic-string: 0.25.9
rollup: 2.79.1
dev: true
/@rollup/plugin-typescript/11.0.0_ds5km5eydjug3un6mvhihxeysu:
resolution: {integrity: sha512-goPyCWBiimk1iJgSTgsehFD5OOFHiAknrRJjqFCudcW8JtWiBlK284Xnn4flqMqg6YAjVG/EE+3aVzrL5qNSzQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^2.14.0||^3.0.0
tslib: '*'
typescript: '>=3.7.0'
peerDependenciesMeta:
rollup:
optional: true
tslib:
optional: true
dependencies:
'@rollup/pluginutils': 5.0.2_rollup@2.79.1
resolve: 1.22.1
rollup: 2.79.1
tslib: 2.4.0
typescript: 4.9.4
dev: true
/@rollup/pluginutils/3.1.0_rollup@2.79.1:
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'}
@ -3852,29 +3712,6 @@ packages:
rollup: 2.79.1
dev: true
/@rollup/pluginutils/4.2.1:
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
engines: {node: '>= 8.0.0'}
dependencies:
estree-walker: 2.0.2
picomatch: 2.3.1
dev: true
/@rollup/pluginutils/5.0.2_rollup@2.79.1:
resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0
peerDependenciesMeta:
rollup:
optional: true
dependencies:
'@types/estree': 1.0.0
estree-walker: 2.0.2
picomatch: 2.3.1
rollup: 2.79.1
dev: true
/@sindresorhus/is/0.14.0:
resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==}
engines: {node: '>=6'}
@ -3901,6 +3738,22 @@ packages:
engines: {node: '>=10.13.0'}
dev: true
/@tsconfig/node10/1.0.9:
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
dev: true
/@tsconfig/node12/1.0.11:
resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
dev: true
/@tsconfig/node14/1.0.3:
resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
dev: true
/@tsconfig/node16/1.0.3:
resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==}
dev: true
/@types/body-parser/1.19.2:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies:
@ -3941,10 +3794,6 @@ packages:
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
dev: true
/@types/estree/1.0.0:
resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
dev: true
/@types/express-serve-static-core/4.17.31:
resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==}
dependencies:
@ -4886,6 +4735,10 @@ packages:
resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
dev: true
/arg/4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
dev: true
/argparse/1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
dependencies:
@ -5233,6 +5086,7 @@ packages:
follow-redirects: 1.15.2
transitivePeerDependencies:
- debug
dev: true
/axios/0.27.2:
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
@ -5834,7 +5688,7 @@ packages:
minipass-pipeline: 1.2.4
mkdirp: 1.0.4
p-map: 4.0.0
promise-inflight: 1.0.1_bluebird@3.7.2
promise-inflight: 1.0.1
rimraf: 3.0.2
ssri: 8.0.1
tar: 6.1.11
@ -6029,30 +5883,6 @@ packages:
/check-error/1.0.2:
resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
/cheerio-select/2.1.0:
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
dependencies:
boolbase: 1.0.0
css-select: 5.1.0
css-what: 6.1.0
domelementtype: 2.3.0
domhandler: 5.0.3
domutils: 3.0.1
dev: true
/cheerio/1.0.0-rc.12:
resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==}
engines: {node: '>= 6'}
dependencies:
cheerio-select: 2.1.0
dom-serializer: 2.0.0
domhandler: 5.0.3
domutils: 3.0.1
htmlparser2: 8.0.1
parse5: 7.1.1
parse5-htmlparser2-tree-adapter: 7.0.0
dev: true
/chokidar/2.1.8:
resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==}
deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
@ -6598,6 +6428,10 @@ packages:
sha.js: 2.4.11
dev: true
/create-require/1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: true
/critters-webpack-plugin/2.5.0_html-webpack-plugin@3.2.0:
resolution: {integrity: sha512-O41TSPV2orAfrV6kSVC0SivZCtVkeypCNKb7xtrbqE/CfjrHeRaFaGuxglcjOI2IGf+oNg6E+ZoOktdlhXPTIQ==}
peerDependencies:
@ -6729,16 +6563,6 @@ packages:
nth-check: 2.1.1
dev: true
/css-select/5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
dependencies:
boolbase: 1.0.0
css-what: 6.1.0
domhandler: 5.0.3
domutils: 3.0.1
nth-check: 2.1.1
dev: true
/css-tree/1.0.0-alpha.37:
resolution: {integrity: sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==}
engines: {node: '>=8.0.0'}
@ -7158,11 +6982,6 @@ packages:
engines: {node: '>= 0.8'}
dev: true
/dequal/2.0.2:
resolution: {integrity: sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==}
engines: {node: '>=6'}
dev: false
/des.js/1.0.1:
resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==}
dependencies:
@ -7179,6 +6998,11 @@ packages:
resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
dev: true
/diff/4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
dev: true
/diff/5.0.0:
resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==}
engines: {node: '>=0.3.1'}
@ -7245,14 +7069,6 @@ packages:
entities: 2.2.0
dev: true
/dom-serializer/2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
entities: 4.4.0
dev: true
/domain-browser/1.2.0:
resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
engines: {node: '>=0.4', npm: '>=1.2'}
@ -7279,13 +7095,6 @@ packages:
domelementtype: 2.3.0
dev: true
/domhandler/5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
dependencies:
domelementtype: 2.3.0
dev: true
/domutils/1.7.0:
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
dependencies:
@ -7301,14 +7110,6 @@ packages:
domhandler: 4.3.1
dev: true
/domutils/3.0.1:
resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==}
dependencies:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
dev: true
/dot-prop/5.3.0:
resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
engines: {node: '>=8'}
@ -7462,11 +7263,6 @@ packages:
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
dev: true
/entities/4.4.0:
resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==}
engines: {node: '>=0.12'}
dev: true
/envinfo/7.8.1:
resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==}
engines: {node: '>=4'}
@ -8257,10 +8053,6 @@ packages:
resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
dev: true
/estree-walker/2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
/esutils/2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
@ -9209,13 +9001,6 @@ packages:
minimalistic-assert: 1.0.1
dev: true
/hasha/4.0.1:
resolution: {integrity: sha512-+wnvroCn3pq0CAKWfItGPyl0DJOob2qs/2D/Rh0a/O90LBzmo5GaKHwIRb6FInVvmEl1mCIHL5RqlfTLvh6FoQ==}
engines: {node: '>=8'}
dependencies:
is-stream: 1.1.0
dev: true
/hasha/5.2.2:
resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==}
engines: {node: '>=8'}
@ -9352,15 +9137,6 @@ packages:
entities: 2.2.0
dev: true
/htmlparser2/8.0.1:
resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
domutils: 3.0.1
entities: 4.4.0
dev: true
/http-cache-semantics/4.1.0:
resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
dev: true
@ -9880,12 +9656,6 @@ packages:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
dev: true
/is-reference/1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
dependencies:
'@types/estree': 1.0.0
dev: true
/is-regex/1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
@ -9909,11 +9679,6 @@ packages:
call-bind: 1.0.2
dev: true
/is-stream/1.1.0:
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
engines: {node: '>=0.10.0'}
dev: true
/is-stream/2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
@ -10546,6 +10311,10 @@ packages:
semver: 6.3.0
dev: true
/make-error/1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
dev: true
/map-age-cleaner/0.1.3:
resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==}
engines: {node: '>=6'}
@ -10744,11 +10513,6 @@ packages:
webpack-sources: 1.4.3
dev: true
/mini-svg-data-uri/1.4.4:
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
hasBin: true
dev: true
/minimalistic-assert/1.0.1:
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
dev: true
@ -11630,13 +11394,6 @@ packages:
engines: {node: '>=6'}
dev: true
/parse5-htmlparser2-tree-adapter/7.0.0:
resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==}
dependencies:
domhandler: 5.0.3
parse5: 7.1.1
dev: true
/parse5/4.0.0:
resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==}
dev: true
@ -11645,12 +11402,6 @@ packages:
resolution: {integrity: sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==}
dev: true
/parse5/7.1.1:
resolution: {integrity: sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==}
dependencies:
entities: 4.4.0
dev: true
/parseurl/1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
@ -12661,6 +12412,15 @@ packages:
engines: {node: '>=0.4.0'}
dev: true
/promise-inflight/1.0.1:
resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
peerDependencies:
bluebird: '*'
peerDependenciesMeta:
bluebird:
optional: true
dev: true
/promise-inflight/1.0.1_bluebird@3.7.2:
resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
peerDependencies:
@ -13215,23 +12975,6 @@ packages:
inherits: 2.0.4
dev: true
/rollup-plugin-bundle-html/0.2.2:
resolution: {integrity: sha512-nK4Z/k3MVjfCcnC5T15ksHw3JyRJx110oduy3VBW0ki2qI0tu4pLlgXyltBgtd+gpiFCPqEnfy89XRPG+eCOwA==}
dependencies:
cheerio: 1.0.0-rc.12
hasha: 4.0.1
dev: true
/rollup-plugin-css-only/3.1.0_rollup@2.79.1:
resolution: {integrity: sha512-TYMOE5uoD76vpj+RTkQLzC9cQtbnJNktHPB507FzRWBVaofg7KhIqq1kGbcVOadARSozWF883Ho9KpSPKH8gqA==}
engines: {node: '>=10.12.0'}
peerDependencies:
rollup: 1 || 2
dependencies:
'@rollup/pluginutils': 4.2.1
rollup: 2.79.1
dev: true
/rollup-plugin-terser/7.0.2_rollup@2.79.1:
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
@ -13364,18 +13107,6 @@ packages:
ajv-keywords: 5.1.0_ajv@8.11.0
dev: true
/script-ext-html-webpack-plugin/2.1.5:
resolution: {integrity: sha512-nMjd5dtsnoB8dS+pVM9ZL4mC9O1uVtTxrDS99OGZsZxFbkZE6pw0HCMued/cncDrKivIShO9vwoyOTvsGqQHEQ==}
engines: {node: '>=6.11.5'}
peerDependencies:
html-webpack-plugin: ^3.0.0 || ^4.0.0
webpack: ^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0
dependencies:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
/select-hose/2.0.0:
resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==}
dev: true
@ -14178,14 +13909,6 @@ packages:
stable: 0.1.8
dev: true
/swr/0.5.7:
resolution: {integrity: sha512-Jh1Efgu8nWZV9rU4VLUMzBzcwaZgi4znqbVXvAtUy/0JzSiN6bNjLaJK8vhY/Rtp7a83dosz5YuehfBNwC/ZoQ==}
peerDependencies:
react: ^16.11.0 || ^17.0.0
dependencies:
dequal: 2.0.2
dev: false
/swr/1.3.0:
resolution: {integrity: sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==}
peerDependencies:
@ -14493,6 +14216,36 @@ packages:
tslib: 2.4.1
dev: true
/ts-node/10.9.1_typescript@4.9.4:
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
hasBin: true
peerDependencies:
'@swc/core': '>=1.2.50'
'@swc/wasm': '>=1.2.50'
'@types/node': '*'
typescript: '>=2.7'
peerDependenciesMeta:
'@swc/core':
optional: true
'@swc/wasm':
optional: true
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.9
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.3
acorn: 8.8.1
acorn-walk: 8.2.0
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 4.9.4
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: true
/ts-pnp/1.2.0_typescript@4.6.4:
resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==}
engines: {node: '>=6'}
@ -14926,6 +14679,10 @@ packages:
hasBin: true
dev: true
/v8-compile-cache-lib/3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
dev: true
/v8-compile-cache/2.3.0:
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
dev: true
@ -15696,6 +15453,11 @@ packages:
yargs-parser: 21.1.1
dev: true
/yn/3.1.1:
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
engines: {node: '>=6'}
dev: true
/yocto-queue/0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}