Merge pull request #44 from hyle-team/dev

Dev
This commit is contained in:
Dmitrii Kolpakov 2026-01-18 17:25:56 +07:00 committed by GitHub
commit ae36606123
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 2745 additions and 3794 deletions

View file

@ -1 +1,22 @@
submodules/ submodules/
node_modules/
dist/
build/
.next/
*.log
*.tmp
.temp/
migrations/
public/
static/
.vscode/
.idea/
.env*
next-env.d.ts
.eslintrc.cjs
prettier.config.mjs

13
.eslintrc.cjs Normal file
View file

@ -0,0 +1,13 @@
module.exports = {
// Or 'jejolare/backend' for Node.js app
extends: ['jejolare/frontend'],
// This is needed only if you use TypeScript
settings: {
'import/resolver': {
typescript: {
project: './tsconfig.json',
},
},
},
};

View file

@ -1,61 +0,0 @@
{
"env": {
"es2021": true,
"node": true
},
"extends": [
"airbnb-base",
"plugin:@typescript-eslint/recommended",
"next/core-web-vitals",
"plugin:prettier/recommended",
"prettier"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "import"],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"no-console": "off",
"no-void": "off",
"import/extensions": "off",
"no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_"
}
],
"func-names": "off",
"consistent-return": "off",
"no-restricted-syntax": "off",
"class-methods-use-this": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_"
}
],
"@typescript-eslint/explicit-member-accessibility": "off",
"@typescript-eslint/no-explicit-any": "error",
"lines-between-class-members": "off",
"camelcase": "off",
"no-underscore-dangle": "off",
"no-shadow": "off",
"no-await-in-loop": "off",
"radix": "off",
"no-plusplus": "off",
"no-promise-executor-return": "off",
"import/no-duplicates": "off",
"import/prefer-default-export": "off",
"import/no-cycle": "off"
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".ts"]
}
}
}
}

View file

@ -9,91 +9,91 @@
# the `language` matrix defined below to confirm you have the correct set of # the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages. # supported CodeQL languages.
# #
name: "CodeQL Advanced" name: 'CodeQL Advanced'
on: on:
push: push:
branches: [ "master" ] branches: ['master']
pull_request: pull_request:
branches: [ "master" ] branches: ['master']
schedule: schedule:
- cron: '21 18 * * 6' - cron: '21 18 * * 6'
jobs: jobs:
analyze: analyze:
name: Analyze (${{ matrix.language }}) name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see: # Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql # - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only) # - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements. # Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions: permissions:
# required for all workflows # required for all workflows
security-events: write security-events: write
# required to fetch internal or private CodeQL packs # required to fetch internal or private CodeQL packs
packages: read packages: read
# only required for workflows in private repositories # only required for workflows in private repositories
actions: read actions: read
contents: read contents: read
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- language: javascript-typescript - language: javascript-typescript
build-mode: none build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both # Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both # Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
# Add any setup steps before running the `github/codeql-action/init` action. # Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node` # This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds. # or others). This is typically only required for manual builds.
# - name: Setup runtime (example) # - name: Setup runtime (example)
# uses: actions/setup-example@v1 # uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v4 uses: github/codeql-action/init@v4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }} build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file. # By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file. # Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality # queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with # If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above # "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step # to set the build mode to "manual" for that language. Then modify this step
# to build your code. # to build your code.
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- name: Run manual build steps - name: Run manual build steps
if: matrix.build-mode == 'manual' if: matrix.build-mode == 'manual'
shell: bash shell: bash
run: | run: |
echo 'If you are using a "manual" build mode for one or more of the' \ echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \ 'languages you are analyzing, replace this with the commands to build' \
'your code, for example:' 'your code, for example:'
echo ' make bootstrap' echo ' make bootstrap'
echo ' make release' echo ' make release'
exit 1 exit 1
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4 uses: github/codeql-action/analyze@v4
with: with:
category: "/language:${{matrix.language}}" category: '/language:${{matrix.language}}'

View file

@ -5,4 +5,5 @@ coverage
.next .next
.env .env
*.lock *.lock
submodules/ submodules/
migrations/

3
next-env.d.ts vendored
View file

@ -1,5 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
import './.next/types/routes.d.ts';
// NOTE: This file should not be edited // NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information. // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.

6022
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,8 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev --webpack",
"build": "next build", "build": "next build --webpack",
"start": "next start -p 30289", "start": "next start -p 30289",
"lint": "next lint", "lint": "next lint",
"eslint": "eslint . --ext .js,.ts,.tsx,.jsx --fix", "eslint": "eslint . --ext .js,.ts,.tsx,.jsx --fix",
@ -17,61 +17,46 @@
"lint-staged": { "lint-staged": {
"*.{js,jsx,ts,tsx}": [ "*.{js,jsx,ts,tsx}": [
"prettier --write", "prettier --write",
"eslint --fix --config .eslintrc.json" "eslint --fix --config .eslintrc.cjs"
] ]
}, },
"dependencies": { "dependencies": {
"@react-hook/window-size": "^3.1.1", "@react-hook/window-size": "^3.1.1",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"antd": "^5.23.2", "antd": "^5.23.2",
"apexcharts": "^3.54.1", "apexcharts": "^4.0.0",
"axios": "^1.4.0", "axios": "^1.13.2",
"decimal.js": "^10.4.3", "decimal.js": "^10.4.3",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"echarts-for-react": "^3.0.2", "echarts-for-react": "^3.0.2",
"eslint-config-next": "13.2.4",
"express": "^4.18.2",
"jimp": "^0.22.8",
"nanoid": "^4.0.1", "nanoid": "^4.0.1",
"next": "^13.2.4", "next": "^16.1.1",
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"node-fetch": "^3.3.1", "node-fetch": "^3.3.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"react": "18.2.0", "react": "^18.3.1",
"react-apexcharts": "^1.5.0", "react-apexcharts": "^1.5.0",
"react-dom": "18.2.0", "react-dom": "^18.3.1",
"react-intersection-observer": "^9.10.3", "react-intersection-observer": "^9.10.3",
"react-joyride": "^2.9.3", "react-joyride": "^2.9.3",
"sequelize": "^6.37.3",
"sha256": "^0.2.0", "sha256": "^0.2.0",
"socket.io": "^4.6.1", "socket.io": "^4.6.1",
"socket.io-client": "^4.6.1", "socket.io-client": "^4.6.1",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsc": "^2.0.4", "tsc": "^2.0.4",
"tsx": "^4.15.7", "tsx": "^4.15.7",
"url-loader": "^4.1.1",
"uuidv4": "^6.2.13" "uuidv4": "^6.2.13"
}, },
"devDependencies": { "devDependencies": {
"@svgr/webpack": "^8.0.1", "@svgr/webpack": "^8.0.1",
"@types/big.js": "^6.2.0",
"@types/crypto-js": "^4.1.1",
"@types/express": "^4.17.17",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/pg": "^8.10.2",
"@types/react": "18.2.16", "@types/react": "18.2.16",
"@types/react-dom": "^18.2.7", "@types/react-dom": "^18.2.7",
"@types/sha256": "^0.2.0", "@types/sha256": "^0.2.0",
"@typescript-eslint/eslint-plugin": "^5.55.0", "eslint-config-jejolare": "^3.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.1",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.5.3",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^15.5.2", "lint-staged": "^15.5.2",
"prettier": "3.5.3", "sass": "^1.59.2"
"sass": "^1.59.2",
"url-loader": "^4.1.1"
} }
} }

View file

@ -1,12 +0,0 @@
/** @type {import("prettier").Config} */
module.exports = {
semi: true,
singleQuote: true,
trailingComma: 'all',
printWidth: 100,
tabWidth: 4,
useTabs: true,
bracketSpacing: true,
arrowParens: 'always',
endOfLine: 'lf',
};

4
prettier.config.mjs Normal file
View file

@ -0,0 +1,4 @@
/** @type {import("prettier").Config} */
import prettierConfig from 'eslint-config-jejolare/prettier';
export default prettierConfig;

View file

@ -1,77 +1,77 @@
.tooltip { .tooltip {
transition: none !important; transition: none !important;
width: 300px; width: 300px;
&__body { &__body {
width: 100%; width: 100%;
text-align: center; text-align: center;
border-radius: 10px; border-radius: 10px;
background-color: #fff; background-color: #fff;
padding: 15px; padding: 15px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
} }
&__index { &__index {
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
color: #0c0c3a; color: #0c0c3a;
line-height: 100%; line-height: 100%;
} }
&__content, &__content,
&__content p { &__content p {
color: #0c0c3a; color: #0c0c3a;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
line-height: 140%; line-height: 140%;
} }
&__btn { &__btn {
padding: 13px; padding: 13px;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
line-height: 100%; line-height: 100%;
} }
&__skip { &__skip {
margin-top: 8px; margin-top: 8px;
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
line-height: 140%; line-height: 140%;
color: var(--guide-skip-btn); color: var(--guide-skip-btn);
background-color: transparent !important; background-color: transparent !important;
} }
} }
@media screen and (max-width:620px) { @media screen and (max-width: 620px) {
.tooltip { .tooltip {
width: 200px; width: 200px;
&__body { &__body {
gap: 8px; gap: 8px;
padding: 10px; padding: 10px;
} }
&__content, &__content,
&__content p { &__content p {
font-size: 12px; font-size: 12px;
} }
&__btn, &__btn,
&__skip { &__skip {
font-size: 12px; font-size: 12px;
} }
&__btn { &__btn {
padding: 10px; padding: 10px;
} }
&__index { &__index {
font-size: 16px; font-size: 16px;
} }
} }
} }

View file

@ -8,7 +8,9 @@ import { BadgeStatusProps } from './types';
function BadgeStatus({ type, icon }: BadgeStatusProps) { function BadgeStatus({ type, icon }: BadgeStatusProps) {
return ( return (
<div className={classes(styles.badge, type === 'high' && styles.high, icon && styles.icon)}> <span
className={classes(styles.badge, type === 'high' && styles.high, icon && styles.icon)}
>
<Image <Image
className={styles.badge__img} className={styles.badge__img}
src={type === 'instant' ? LightningImg : RocketImg} src={type === 'instant' ? LightningImg : RocketImg}
@ -19,7 +21,7 @@ function BadgeStatus({ type, icon }: BadgeStatusProps) {
{type === 'instant' ? 'instant' : 'high volume'} {type === 'instant' ? 'instant' : 'high volume'}
</span> </span>
)} )}
</div> </span>
); );
} }

View file

@ -2,7 +2,7 @@
width: fit-content; width: fit-content;
padding: 1px 4px; padding: 1px 4px;
padding-right: 8px; padding-right: 8px;
display: flex; display: flex !important;
align-items: center; align-items: center;
gap: 2px; gap: 2px;
border-radius: 100px; border-radius: 100px;

View file

@ -72,4 +72,4 @@
font-size: 36px; font-size: 36px;
} }
} }
} }

View file

@ -7,7 +7,8 @@ import Head from 'next/head';
import { StoreProvider } from '@/store/store-reducer'; import { StoreProvider } from '@/store/store-reducer';
import NextApp, { AppContext, AppProps } from 'next/app'; import NextApp, { AppContext, AppProps } from 'next/app';
import { ThemeProvider } from 'next-themes'; import { ThemeProvider } from 'next-themes';
import GetConfigRes, { GetConfigResData } from '@/interfaces/responses/config/GetConfigRes'; import axios, { AxiosError } from 'axios';
import { GetConfigResData } from '@/interfaces/responses/config/GetConfigRes';
import inter from '@/utils/font'; import inter from '@/utils/font';
import { API_URL } from '@/constants'; import { API_URL } from '@/constants';
import NProgress from 'nprogress'; import NProgress from 'nprogress';
@ -21,8 +22,18 @@ NProgress.configure({
trickleSpeed: 200, trickleSpeed: 200,
}); });
Router.events.on('routeChangeStart', () => { Router.events.on('routeChangeStart', (url) => {
NProgress.start(); if (typeof window === 'undefined') return;
const currentUrl = window.location.href;
const nextUrl = new URL(url, window.location.origin).href;
const currentPath = new URL(currentUrl).pathname;
const nextPath = new URL(nextUrl).pathname;
if (currentPath !== nextPath) {
NProgress.start();
}
}); });
Router.events.on('routeChangeComplete', () => { Router.events.on('routeChangeComplete', () => {
@ -86,27 +97,20 @@ App.getInitialProps = async (context: AppContext) => {
try { try {
const pageProps = await NextApp.getInitialProps(context); const pageProps = await NextApp.getInitialProps(context);
const configRes = await fetch(`${API_URL}/api/config`, { const configRes = await axios.get(`${API_URL}/api/config`, {
method: 'GET',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
credentials: 'include', withCredentials: true,
}); });
if (!configRes.ok) {
console.error(`Failed to fetch config: ${configRes.status}`);
return pageProps;
}
const configData = (await configRes.json()) as GetConfigRes;
return { return {
...pageProps, ...pageProps,
config: configData.data, config: configRes.data.data,
}; };
} catch (error) { } catch (error) {
console.error('Unable to fetch config data:', error); const err = error as AxiosError;
console.error('Unable to fetch config data:', err?.response?.status, err?.message);
return NextApp.getInitialProps(context); return NextApp.getInitialProps(context);
} }

View file

@ -64,10 +64,13 @@ const AdminPanel: React.FC = () => {
})), })),
); );
} else { } else {
message.error('Error fetching featured pairs'); message.error({
content: 'Error fetching featured pairs',
className: styles.message,
});
} }
} catch (error) { } catch (error) {
message.error('Error fetching featured pairs'); message.error({ content: 'Error fetching featured pairs', className: styles.message });
} }
}; };
@ -121,7 +124,10 @@ const AdminPanel: React.FC = () => {
}); });
} }
} catch (error) { } catch (error) {
message.error('Error adding featured pair'); message.error({
content: 'Error adding featured pair',
className: styles.message,
});
} }
}; };
@ -149,7 +155,7 @@ const AdminPanel: React.FC = () => {
} }
} }
} catch (error) { } catch (error) {
message.error('Error removing featured pair'); message.error({ content: 'Error removing featured pair', className: styles.message });
} }
}; };

View file

@ -12,7 +12,6 @@ import Popup from '@/components/UI/Popup/Popup';
import CreateOfferPopup from '@/components/default/CreateOfferPopup/CreateOfferPopup'; import CreateOfferPopup from '@/components/default/CreateOfferPopup/CreateOfferPopup';
import Link from 'next/link'; import Link from 'next/link';
import MainPageTitle from '@/components/default/MainPageTitle/MainPageTitle'; import MainPageTitle from '@/components/default/MainPageTitle/MainPageTitle';
import { GetServerSidePropsContext } from 'next';
import HomeProps from '@/interfaces/props/pages/p2p/HomeProps'; import HomeProps from '@/interfaces/props/pages/p2p/HomeProps';
import OfferData from '@/interfaces/responses/offers/OfferData'; import OfferData from '@/interfaces/responses/offers/OfferData';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
@ -103,8 +102,8 @@ function Home(props: HomeProps) {
); );
} }
export async function getServerSideProps(context: GetServerSidePropsContext) { export async function getServerSideProps() {
const stats = await getStats(context.req.headers.host); const stats = await getStats();
return { return {
props: { props: {

View file

@ -68,7 +68,6 @@
@media screen and (max-width: 1024px) { @media screen and (max-width: 1024px) {
.trading__top { .trading__top {
&_trades, &_trades,
&_form { &_form {
max-width: 100%; max-width: 100%;
@ -90,4 +89,4 @@
.trading__top { .trading__top {
height: 370px; height: 370px;
} }
} }

View file

@ -33,7 +33,7 @@ main {
padding: 80px 100px; padding: 80px 100px;
&.with-separators { &.with-separators {
>* { > * {
padding: 40px 0; padding: 40px 0;
border-bottom: 1px solid var(--delimiter-color); border-bottom: 1px solid var(--delimiter-color);
@ -115,7 +115,7 @@ h6 {
} }
h1, h1,
h1>* { h1 > * {
color: var(--font-main-color); color: var(--font-main-color);
font-size: 48px; font-size: 48px;
} }
@ -174,9 +174,8 @@ svg {
} }
@media screen and (max-width: 700px) { @media screen and (max-width: 700px) {
h1, h1,
h1>* { h1 > * {
font-size: 32px; font-size: 32px;
} }
} }
@ -290,4 +289,4 @@ svg {
left: 0; left: 0;
width: 100%; width: 100%;
height: 3px; height: 3px;
} }

View file

@ -74,8 +74,8 @@ export async function getPage(
.then((res) => res.data); .then((res) => res.data);
} }
export async function getStats(host: string | undefined = undefined): Promise<GetStatsRes> { export async function getStats(): Promise<GetStatsRes> {
return (await fetch(`${host ? `https://${host}` : ''}/api/offers/get-stats`).then((res) => return (await fetch(`${baseUrl}/api/offers/get-stats`).then((res) =>
res.json(), res.json(),
)) as GetStatsRes; )) as GetStatsRes;
} }

View file

@ -96,7 +96,7 @@
"incremental": true, "incremental": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve" "jsx": "react-jsx"
}, },
"ts-node": { "ts-node": {
"esm": true, "esm": true,