mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 14:01:56 +08:00
Chore: add more strictly eslint
This commit is contained in:
parent
5ff1742361
commit
df0bfb5e10
@ -1,3 +1,17 @@
|
||||
extends:
|
||||
- standard-with-typescript
|
||||
- react-app
|
||||
parser: '@typescript-eslint/parser'
|
||||
parserOptions:
|
||||
project: './tsconfig.json'
|
||||
rules:
|
||||
comma-dangle: [error, always-multiline]
|
||||
'@typescript-eslint/indent': [error, 4]
|
||||
'@typescript-eslint/explicit-function-return-type': off
|
||||
'@typescript-eslint/restrict-template-expressions': off
|
||||
'@typescript-eslint/strict-boolean-expressions': off
|
||||
'@typescript-eslint/no-non-null-assertion': off
|
||||
'@typescript-eslint/consistent-type-assertions': off
|
||||
'@typescript-eslint/promise-function-async': off
|
||||
'@typescript-eslint/no-floating-promises': off
|
||||
'@typescript-eslint/no-invalid-void-type': off
|
||||
|
@ -39,9 +39,12 @@
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^7.29.0",
|
||||
"eslint-config-react-app": "^6.0.0",
|
||||
"eslint-config-standard-with-typescript": "^20.0.0",
|
||||
"eslint-plugin-flowtype": "^5.7.2",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.24.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"sass": "^1.35.1",
|
||||
|
@ -14,7 +14,7 @@ const iconMap = {
|
||||
success: 'check',
|
||||
info: 'info',
|
||||
warning: 'info',
|
||||
error: 'close'
|
||||
error: 'close',
|
||||
}
|
||||
|
||||
export function Alert (props: AlertProps) {
|
||||
|
@ -10,7 +10,7 @@ export interface ButtonSelectOptions<T = string> {
|
||||
|
||||
export interface ButtonSelectProps<T = string> extends BaseComponentProps {
|
||||
// options
|
||||
options: ButtonSelectOptions<T>[]
|
||||
options: Array<ButtonSelectOptions<T>>
|
||||
|
||||
// active value
|
||||
value: T
|
||||
|
@ -26,7 +26,7 @@ export function Input (props: InputProps) {
|
||||
type = 'text',
|
||||
disabled = false,
|
||||
onChange = noop,
|
||||
onBlur = noop
|
||||
onBlur = noop,
|
||||
} = props
|
||||
const classname = classnames('input', `text-${align}`, { 'focus:shadow-none': inside }, className)
|
||||
|
||||
|
@ -10,7 +10,7 @@ const TYPE_ICON_MAP = {
|
||||
info: 'info',
|
||||
success: 'check',
|
||||
warning: 'info-o',
|
||||
error: 'close'
|
||||
error: 'close',
|
||||
}
|
||||
|
||||
type NoticeType = 'success' | 'info' | 'warning' | 'error'
|
||||
@ -38,7 +38,7 @@ export function Message (props: MessageProps) {
|
||||
icon = <Icon type="info" size={16} />,
|
||||
content = '',
|
||||
type = 'info',
|
||||
duration = 1500
|
||||
duration = 1500,
|
||||
} = props
|
||||
|
||||
const { visible, show, hide } = useVisible()
|
||||
@ -85,7 +85,7 @@ export function showMessage (args: ArgsProps) {
|
||||
content,
|
||||
removeComponent,
|
||||
duration,
|
||||
onClose
|
||||
onClose,
|
||||
}
|
||||
|
||||
render(<Message {...props} />, container)
|
||||
@ -94,23 +94,23 @@ export function showMessage (args: ArgsProps) {
|
||||
export const info = (
|
||||
content: string,
|
||||
duration?: number,
|
||||
onClose?: typeof noop
|
||||
onClose?: typeof noop,
|
||||
) => showMessage({ type: 'info', content, duration, onClose })
|
||||
|
||||
export const success = (
|
||||
content: string,
|
||||
duration?: number,
|
||||
onClose?: typeof noop
|
||||
onClose?: typeof noop,
|
||||
) => showMessage({ type: 'success', content, duration, onClose })
|
||||
|
||||
export const warning = (
|
||||
content: string,
|
||||
duration?: number,
|
||||
onClose?: typeof noop
|
||||
onClose?: typeof noop,
|
||||
) => showMessage({ type: 'warning', content, duration, onClose })
|
||||
|
||||
export const error = (
|
||||
content: string,
|
||||
duration?: number,
|
||||
onClose?: typeof noop
|
||||
onClose?: typeof noop,
|
||||
) => showMessage({ type: 'error', content, duration, onClose })
|
||||
|
@ -45,7 +45,7 @@ export function Modal (props: ModalProps) {
|
||||
bodyStyle,
|
||||
className,
|
||||
style,
|
||||
children
|
||||
children,
|
||||
} = props
|
||||
|
||||
const { translation } = useI18n()
|
||||
|
@ -23,18 +23,18 @@ interface SelectProps extends BaseComponentProps {
|
||||
export function Select (props: SelectProps) {
|
||||
const { value, onSelect, children, className: cn, style } = props
|
||||
|
||||
const portalRef = useRef<HTMLDivElement>()
|
||||
const portalRef = useRef<HTMLDivElement>(document.createElement('div'))
|
||||
const attachmentRef = useRef<HTMLDivElement>(null)
|
||||
const targetRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [showDropDownList, setShowDropDownList] = useState(false)
|
||||
const [hasCreateDropList, setHasCreateDropList] = useState(false)
|
||||
const dropdownListStyles = useMemo(() => {
|
||||
if (targetRef.current) {
|
||||
if (targetRef.current != null) {
|
||||
const targetRectInfo = targetRef.current.getBoundingClientRect()
|
||||
return {
|
||||
top: Math.floor(targetRectInfo.top) - 10,
|
||||
left: Math.floor(targetRectInfo.left) - 10
|
||||
left: Math.floor(targetRectInfo.left) - 10,
|
||||
}
|
||||
}
|
||||
return {}
|
||||
@ -49,23 +49,17 @@ export function Select (props: SelectProps) {
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const current = portalRef.current
|
||||
document.body.appendChild(current)
|
||||
document.addEventListener('click', handleGlobalClick, true)
|
||||
return () => {
|
||||
document.addEventListener('click', handleGlobalClick, true)
|
||||
if (portalRef.current) {
|
||||
document.body.removeChild(portalRef.current)
|
||||
}
|
||||
document.body.removeChild(current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
function handleShowDropList () {
|
||||
if (!hasCreateDropList) {
|
||||
if (!portalRef.current) {
|
||||
// create container element
|
||||
const container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
portalRef.current = container
|
||||
}
|
||||
setHasCreateDropList(true)
|
||||
}
|
||||
setShowDropDownList(true)
|
||||
@ -100,9 +94,9 @@ export function Select (props: SelectProps) {
|
||||
onClick: (e: React.MouseEvent<HTMLLIElement>) => {
|
||||
onSelect?.(child.props.value, e)
|
||||
setShowDropDownList(false)
|
||||
rawOnClickEvent && rawOnClickEvent(e)
|
||||
rawOnClickEvent?.(e)
|
||||
},
|
||||
className
|
||||
className,
|
||||
}))
|
||||
})
|
||||
}, [children, value, onSelect])
|
||||
@ -131,7 +125,7 @@ export function Select (props: SelectProps) {
|
||||
<Icon type="triangle-down" />
|
||||
</div>
|
||||
{
|
||||
hasCreateDropList && createPortal(dropDownList, portalRef?.current!)
|
||||
hasCreateDropList && createPortal(dropDownList, portalRef.current)
|
||||
}
|
||||
</>
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ export default function App () {
|
||||
{ path: '/logs', name: 'Logs', component: Logs },
|
||||
{ path: '/rules', name: 'Rules', component: Rules, noMobile: true },
|
||||
{ path: '/connections', name: 'Connections', component: Connections, noMobile: true },
|
||||
{ path: '/settings', name: 'Settings', component: Settings }
|
||||
{ path: '/settings', name: 'Settings', component: Settings },
|
||||
]
|
||||
|
||||
return (
|
||||
@ -36,7 +36,7 @@ export default function App () {
|
||||
<Route exact path="/" component={() => <Redirect to="/proxies"/>} />
|
||||
{
|
||||
routes.map(
|
||||
route => <Route exact={false} path={route.path} key={route.path} component={route.component} />
|
||||
route => <Route exact={false} path={route.path} key={route.path} component={route.component} />,
|
||||
)
|
||||
}
|
||||
</Switch>
|
||||
|
@ -34,7 +34,7 @@ export function Devices (props: DevicesProps) {
|
||||
onClick={() => handleSelected(device.label)}>
|
||||
{ device.label } ({ device.number })
|
||||
</div>
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
@ -47,7 +47,7 @@ interface ITableInstance<D extends object = {}> extends
|
||||
TableInstance<D>,
|
||||
UseFiltersInstanceProps<D> {}
|
||||
|
||||
function formatTraffic(num: number) {
|
||||
function formatTraffic (num: number) {
|
||||
const s = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let idx = 0
|
||||
while (~~(num / 1024) && idx < s.length) {
|
||||
@ -58,7 +58,7 @@ function formatTraffic(num: number) {
|
||||
return `${idx === 0 ? num : num.toFixed(2)} ${s[idx]}`
|
||||
}
|
||||
|
||||
function formatSpeed(upload: number, download: number) {
|
||||
function formatSpeed (upload: number, download: number) {
|
||||
switch (true) {
|
||||
case upload === 0 && download === 0:
|
||||
return '-'
|
||||
@ -89,7 +89,7 @@ interface formatConnection {
|
||||
completed: boolean
|
||||
}
|
||||
|
||||
export default function Connections() {
|
||||
export default function Connections () {
|
||||
const { translation, lang } = useI18n()
|
||||
const t = useMemo(() => translation('Connections').t, [translation])
|
||||
const connStreamReader = useConnectionStreamReader()
|
||||
@ -98,12 +98,12 @@ export default function Connections() {
|
||||
// total
|
||||
const [traffic, setTraffic] = useObject({
|
||||
uploadTotal: 0,
|
||||
downloadTotal: 0
|
||||
downloadTotal: 0,
|
||||
})
|
||||
|
||||
// close all connections
|
||||
const { visible, show, hide } = useVisible()
|
||||
function handleCloseConnections() {
|
||||
function handleCloseConnections () {
|
||||
client.closeAllConnections().finally(() => hide())
|
||||
}
|
||||
|
||||
@ -122,8 +122,8 @@ export default function Connections() {
|
||||
type: c.metadata.type,
|
||||
network: c.metadata.network.toUpperCase(),
|
||||
speed: { upload: c.uploadSpeed, download: c.downloadSpeed },
|
||||
completed: !!c.completed
|
||||
})
|
||||
completed: !!c.completed,
|
||||
}),
|
||||
), [connections])
|
||||
const devices = useMemo(() => {
|
||||
const gb = groupBy(connections, 'metadata.sourceIP')
|
||||
@ -133,7 +133,7 @@ export default function Connections() {
|
||||
// table
|
||||
const tableRef = useRef<HTMLDivElement>(null)
|
||||
const { x: scrollX } = useScroll(tableRef)
|
||||
const columns: TableColumnOption<formatConnection>[] = useMemo(() => [
|
||||
const columns: Array<TableColumnOption<formatConnection>> = useMemo(() => [
|
||||
{ Header: t(`columns.${Columns.Host}`), accessor: Columns.Host, minWidth: 260, width: 260 },
|
||||
{ Header: t(`columns.${Columns.Network}`), accessor: Columns.Network, minWidth: 80, width: 80 },
|
||||
{ Header: t(`columns.${Columns.Type}`), accessor: Columns.Type, minWidth: 120, width: 120 },
|
||||
@ -142,31 +142,32 @@ export default function Connections() {
|
||||
{
|
||||
id: Columns.Speed,
|
||||
Header: t(`columns.${Columns.Speed}`),
|
||||
accessor(originalRow: formatConnection) {
|
||||
accessor (originalRow: formatConnection) {
|
||||
return [originalRow.speed.upload, originalRow.speed.download]
|
||||
},
|
||||
sortType(rowA, rowB) {
|
||||
sortType (rowA, rowB) {
|
||||
const speedA = rowA.original.speed
|
||||
const speedB = rowB.original.speed
|
||||
return speedA.download === speedB.download
|
||||
? speedA.upload - speedB.upload
|
||||
: speedA.download - speedB.download
|
||||
},
|
||||
minWidth: 200, width: 200,
|
||||
sortDescFirst: true
|
||||
minWidth: 200,
|
||||
width: 200,
|
||||
sortDescFirst: true,
|
||||
},
|
||||
{ Header: t(`columns.${Columns.Upload}`), accessor: Columns.Upload, minWidth: 100, width: 100, sortDescFirst: true },
|
||||
{ Header: t(`columns.${Columns.Download}`), accessor: Columns.Download, minWidth: 100, width: 100, sortDescFirst: true },
|
||||
{ Header: t(`columns.${Columns.SourceIP}`), accessor: Columns.SourceIP, minWidth: 140, width: 140 },
|
||||
{ Header: t(`columns.${Columns.Time}`), accessor: Columns.Time, minWidth: 120, width: 120, sortType(rowA, rowB) { return rowB.original.time - rowA.original.time } },
|
||||
] as TableColumnOption<formatConnection>[], [t])
|
||||
{ Header: t(`columns.${Columns.Time}`), accessor: Columns.Time, minWidth: 120, width: 120, sortType (rowA, rowB) { return rowB.original.time - rowA.original.time } },
|
||||
] as Array<TableColumnOption<formatConnection>>, [t])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
function handleConnection(snapshots: API.Snapshot[]) {
|
||||
function handleConnection (snapshots: API.Snapshot[]) {
|
||||
for (const snapshot of snapshots) {
|
||||
setTraffic({
|
||||
uploadTotal: snapshot.uploadTotal,
|
||||
downloadTotal: snapshot.downloadTotal
|
||||
downloadTotal: snapshot.downloadTotal,
|
||||
})
|
||||
|
||||
feed(snapshot.connections)
|
||||
@ -186,19 +187,19 @@ export default function Connections() {
|
||||
headerGroups,
|
||||
rows,
|
||||
prepareRow,
|
||||
setFilter
|
||||
setFilter,
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
autoResetSortBy: false,
|
||||
autoResetFilters: false,
|
||||
initialState: { sortBy: [{ id: Columns.Time, desc: false }] }
|
||||
initialState: { sortBy: [{ id: Columns.Time, desc: false }] },
|
||||
} as ITableOptions<formatConnection>,
|
||||
useResizeColumns,
|
||||
useBlockLayout,
|
||||
useFilters,
|
||||
useSortBy
|
||||
useSortBy,
|
||||
) as ITableInstance<formatConnection>
|
||||
const headerGroup = useMemo(() => headerGroups[0], [headerGroups])
|
||||
const renderCell = useCallback(function (cell: Cell<formatConnection>) {
|
||||
@ -244,7 +245,7 @@ export default function Connections() {
|
||||
{...realColumn.getHeaderProps()}
|
||||
className={classnames('connections-th', {
|
||||
resizing: realColumn.isResizing,
|
||||
fixed: scrollX > 0 && realColumn.id === Columns.Host
|
||||
fixed: scrollX > 0 && realColumn.id === Columns.Host,
|
||||
})}
|
||||
key={id}>
|
||||
<div {...realColumn.getSortByToggleProps()}>
|
||||
@ -275,7 +276,7 @@ export default function Connections() {
|
||||
const classname = classnames(
|
||||
'connections-block',
|
||||
{ 'text-center': shouldCenter.has(cell.column.id), completed: row.original.completed },
|
||||
{ fixed: scrollX > 0 && cell.column.id === Columns.Host }
|
||||
{ fixed: scrollX > 0 && cell.column.id === Columns.Host },
|
||||
)
|
||||
return (
|
||||
<div {...cell.getCellProps()} className={classname} key={cell.column.id}>
|
||||
|
@ -9,7 +9,7 @@ class Store {
|
||||
|
||||
appendToSet (connections: API.Connections[]) {
|
||||
const mapping = connections.reduce(
|
||||
(map, c) => map.set(c.id, c), new Map<string, API.Connections>()
|
||||
(map, c) => map.set(c.id, c), new Map<string, API.Connections>(),
|
||||
)
|
||||
|
||||
for (const id of this.connections.keys()) {
|
||||
@ -18,7 +18,7 @@ class Store {
|
||||
this.connections.delete(id)
|
||||
} else {
|
||||
const connection = this.connections.get(id)
|
||||
if (connection) {
|
||||
if (connection != null) {
|
||||
connection.completed = true
|
||||
connection.uploadSpeed = 0
|
||||
connection.downloadSpeed = 0
|
||||
|
@ -15,7 +15,7 @@ export default function ExternalController () {
|
||||
const [value, set] = useObject({
|
||||
hostname: '',
|
||||
port: '',
|
||||
secret: ''
|
||||
secret: '',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -15,7 +15,7 @@ export default function Logs () {
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const ul = listRef.current
|
||||
if (ul) {
|
||||
if (ul != null) {
|
||||
ul.scrollTop = ul.scrollHeight
|
||||
}
|
||||
})
|
||||
@ -26,7 +26,7 @@ export default function Logs () {
|
||||
setLogs(logsRef.current)
|
||||
}
|
||||
|
||||
if (logsStreamReader) {
|
||||
if (logsStreamReader != null) {
|
||||
logsStreamReader.subscribe('data', handleLog)
|
||||
logsRef.current = logsStreamReader.buffer()
|
||||
setLogs(logsRef.current)
|
||||
@ -47,7 +47,7 @@ export default function Logs () {
|
||||
<span className="mr-4 text-gray-400 text-opacity-90">{ dayjs(log.time).format('YYYY-MM-DD HH:mm:ss') }</span>
|
||||
<span>[{ log.type }] { log.payload }</span>
|
||||
</li>
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
|
@ -9,10 +9,10 @@ export default function Overview () {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
opacity: 0.3
|
||||
opacity: 0.3,
|
||||
}}>
|
||||
<img src={logo} alt="Logo" style={{
|
||||
width: 200
|
||||
width: 200,
|
||||
}}/>
|
||||
|
||||
<h1 style={{ color: '#54759A', marginTop: 20 }}>Coming Soon...</h1>
|
||||
|
@ -28,9 +28,7 @@ export function Group (props: GroupProps) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of list) {
|
||||
client.closeConnection(id)
|
||||
}
|
||||
await Promise.all(list.map(id => client.closeConnection(id)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,12 +24,12 @@ export function Provider (props: ProvidersProps) {
|
||||
|
||||
function handleHealthChech () {
|
||||
show()
|
||||
client.healthCheckProvider(provider.name).then(() => update()).finally(() => hide())
|
||||
client.healthCheckProvider(provider.name).then(async () => await update()).finally(() => hide())
|
||||
}
|
||||
|
||||
function handleUpdate () {
|
||||
show()
|
||||
client.updateProvider(provider.name).then(() => update()).finally(() => hide())
|
||||
client.updateProvider(provider.name).then(async () => await update()).finally(() => hide())
|
||||
}
|
||||
|
||||
const proxies = useMemo(() => {
|
||||
|
@ -18,7 +18,7 @@ const TagColors = {
|
||||
'#909399': 0,
|
||||
'#00c520': 260,
|
||||
'#ff9a28': 600,
|
||||
'#ff3e5e': Infinity
|
||||
'#ff3e5e': Infinity,
|
||||
}
|
||||
|
||||
export function Proxy (props: ProxyProps) {
|
||||
@ -42,7 +42,7 @@ export function Proxy (props: ProxyProps) {
|
||||
const validDelay = result.isErr() ? 0 : result.value
|
||||
set(draft => {
|
||||
const proxy = draft.proxies.find(p => p.name === config.name)
|
||||
if (proxy) {
|
||||
if (proxy != null) {
|
||||
proxy.history.push({ time: Date.now().toString(), delay: validDelay })
|
||||
}
|
||||
})
|
||||
@ -50,20 +50,21 @@ export function Proxy (props: ProxyProps) {
|
||||
|
||||
const delay = useMemo(
|
||||
() => config.history?.length ? config.history.slice(-1)[0].delay : 0,
|
||||
[config]
|
||||
[config],
|
||||
)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
EE.subscribe(Action.SPEED_NOTIFY, speedTest)
|
||||
return () => EE.unsubscribe(Action.SPEED_NOTIFY, speedTest)
|
||||
const handler = () => { speedTest() }
|
||||
EE.subscribe(Action.SPEED_NOTIFY, handler)
|
||||
return () => EE.unsubscribe(Action.SPEED_NOTIFY, handler)
|
||||
}, [speedTest])
|
||||
|
||||
const hasError = useMemo(() => delay === 0, [delay])
|
||||
const color = useMemo(() =>
|
||||
Object.keys(TagColors).find(
|
||||
threshold => delay <= TagColors[threshold as keyof typeof TagColors]
|
||||
const color = useMemo(
|
||||
() => Object.keys(TagColors).find(
|
||||
threshold => delay <= TagColors[threshold as keyof typeof TagColors],
|
||||
),
|
||||
[delay]
|
||||
[delay],
|
||||
)
|
||||
|
||||
const backgroundColor = hasError ? undefined : color
|
||||
|
@ -17,12 +17,12 @@ enum sortType {
|
||||
const sortMap = {
|
||||
[sortType.None]: 'sort',
|
||||
[sortType.Asc]: 'sort-ascending',
|
||||
[sortType.Desc]: 'sort-descending'
|
||||
[sortType.Desc]: 'sort-descending',
|
||||
}
|
||||
|
||||
export function compareDesc (a: API.Proxy, b: API.Proxy) {
|
||||
const lastDelayA = a.history.length ? a.history.slice(-1)[0].delay : 0
|
||||
const lastDelayB = b.history.length ? b.history.slice(-1)[0].delay : 0
|
||||
const lastDelayA = (a.history.length > 0) ? a.history.slice(-1)[0].delay : 0
|
||||
const lastDelayB = (b.history.length > 0) ? b.history.slice(-1)[0].delay : 0
|
||||
return (lastDelayB || Number.MAX_SAFE_INTEGER) - (lastDelayA || Number.MAX_SAFE_INTEGER)
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ function ProxyGroups () {
|
||||
|
||||
const list = useMemo(
|
||||
() => general.mode === 'global' ? [global] : groups,
|
||||
[general, groups, global]
|
||||
[general, groups, global],
|
||||
)
|
||||
|
||||
return <>
|
||||
@ -100,16 +100,16 @@ function Proxies () {
|
||||
}
|
||||
|
||||
const { current: sort, next } = useRound(
|
||||
[sortType.Asc, sortType.Desc, sortType.None]
|
||||
[sortType.Asc, sortType.Desc, sortType.None],
|
||||
)
|
||||
const sortedProxies = useMemo(() => {
|
||||
switch (sort) {
|
||||
case sortType.Desc:
|
||||
return proxies.slice().sort((a, b) => compareDesc(a, b))
|
||||
case sortType.Asc:
|
||||
return proxies.slice().sort((a, b) => -1 * compareDesc(a, b))
|
||||
default:
|
||||
return proxies.slice()
|
||||
case sortType.Desc:
|
||||
return proxies.slice().sort((a, b) => compareDesc(a, b))
|
||||
case sortType.Asc:
|
||||
return proxies.slice().sort((a, b) => -1 * compareDesc(a, b))
|
||||
default:
|
||||
return proxies.slice()
|
||||
}
|
||||
}, [sort, proxies])
|
||||
const handleSort = next
|
||||
|
@ -23,7 +23,7 @@ export function Provider (props: ProvidersProps) {
|
||||
|
||||
function handleUpdate () {
|
||||
show()
|
||||
client.updateRuleProvider(provider.name).then(() => update()).finally(() => hide())
|
||||
client.updateRuleProvider(provider.name).then(async () => await update()).finally(() => hide())
|
||||
}
|
||||
|
||||
const updateClassnames = classnames('rule-provider-icon', { 'rule-provider-loading': visible })
|
||||
|
@ -23,7 +23,7 @@ export default function Settings () {
|
||||
const [info, set] = useObject({
|
||||
socks5ProxyPort: 7891,
|
||||
httpProxyPort: 7890,
|
||||
mixedProxyPort: 0
|
||||
mixedProxyPort: 0,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
@ -39,12 +39,12 @@ export default function Settings () {
|
||||
|
||||
async function handleStartAtLoginChange (state: boolean) {
|
||||
await jsBridge?.setStartAtLogin(state)
|
||||
fetchClashXData()
|
||||
await fetchClashXData()
|
||||
}
|
||||
|
||||
async function handleSetSystemProxy (state: boolean) {
|
||||
await jsBridge?.setSystemProxy(state)
|
||||
fetchClashXData()
|
||||
await fetchClashXData()
|
||||
}
|
||||
|
||||
function changeLanguage (language: Lang) {
|
||||
@ -73,7 +73,7 @@ export default function Settings () {
|
||||
|
||||
const {
|
||||
hostname: externalControllerHost,
|
||||
port: externalControllerPort
|
||||
port: externalControllerPort,
|
||||
} = apiInfo
|
||||
|
||||
const { allowLan, mode } = general
|
||||
@ -86,7 +86,7 @@ export default function Settings () {
|
||||
const options = [
|
||||
{ label: t('values.global'), value: 'Global' },
|
||||
{ label: t('values.rules'), value: 'Rule' },
|
||||
{ label: t('values.direct'), value: 'Direct' }
|
||||
{ label: t('values.direct'), value: 'Direct' },
|
||||
]
|
||||
if (premium) {
|
||||
options.push({ label: t('values.script'), value: 'Script' })
|
||||
|
@ -7,12 +7,12 @@ import logo from '@assets/logo.png'
|
||||
import './style.scss'
|
||||
|
||||
interface SidebarProps {
|
||||
routes: {
|
||||
routes: Array<{
|
||||
path: string
|
||||
name: string
|
||||
noMobile?: boolean
|
||||
exact?: boolean
|
||||
}[]
|
||||
}>
|
||||
}
|
||||
|
||||
export default function Sidebar (props: SidebarProps) {
|
||||
@ -27,7 +27,7 @@ export default function Sidebar (props: SidebarProps) {
|
||||
<li className={classnames('item', { 'no-mobile': noMobile })} key={name}>
|
||||
<NavLink to={path} activeClassName="active" exact={!!exact}>{ t(name) }</NavLink>
|
||||
</li>
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -6,7 +6,7 @@ const EN = {
|
||||
Rules: 'Rules',
|
||||
Settings: 'Setting',
|
||||
Connections: 'Connections',
|
||||
Version: 'Version'
|
||||
Version: 'Version',
|
||||
},
|
||||
Settings: {
|
||||
title: 'Settings',
|
||||
@ -19,7 +19,7 @@ const EN = {
|
||||
socks5ProxyPort: 'Socks5 proxy port',
|
||||
httpProxyPort: 'HTTP proxy port',
|
||||
mixedProxyPort: 'Mixed proxy port',
|
||||
externalController: 'External controller'
|
||||
externalController: 'External controller',
|
||||
},
|
||||
values: {
|
||||
cn: '中文',
|
||||
@ -27,7 +27,7 @@ const EN = {
|
||||
global: 'Global',
|
||||
rules: 'Rules',
|
||||
direct: 'Direct',
|
||||
script: 'Script'
|
||||
script: 'Script',
|
||||
},
|
||||
versionString: 'Current ClashX is the latest version:{{version}}',
|
||||
checkUpdate: 'Check Update',
|
||||
@ -36,17 +36,17 @@ const EN = {
|
||||
note: 'Please note that modifying this configuration will only configure Dashboard. Will not modify your Clash configuration file. Please make sure that the external controller address matches the address in the Clash configuration file, otherwise, Dashboard will not be able to connect to Clash.',
|
||||
host: 'Host',
|
||||
port: 'Port',
|
||||
secret: 'Secret'
|
||||
}
|
||||
secret: 'Secret',
|
||||
},
|
||||
},
|
||||
Logs: {
|
||||
title: 'Logs'
|
||||
title: 'Logs',
|
||||
},
|
||||
Rules: {
|
||||
title: 'Rules',
|
||||
providerTitle: 'Providers',
|
||||
providerUpdateTime: 'Last updated at',
|
||||
ruleCount: 'Rule count'
|
||||
ruleCount: 'Rule count',
|
||||
},
|
||||
Connections: {
|
||||
title: 'Connections',
|
||||
@ -54,14 +54,14 @@ const EN = {
|
||||
total: {
|
||||
text: 'total',
|
||||
upload: 'upload',
|
||||
download: 'download'
|
||||
download: 'download',
|
||||
},
|
||||
closeAll: {
|
||||
title: 'Warning',
|
||||
content: 'This would close all connections'
|
||||
content: 'This would close all connections',
|
||||
},
|
||||
filter: {
|
||||
all: 'All'
|
||||
all: 'All',
|
||||
},
|
||||
columns: {
|
||||
host: 'Host',
|
||||
@ -73,8 +73,8 @@ const EN = {
|
||||
speed: 'Speed',
|
||||
upload: 'Upload',
|
||||
download: 'Download',
|
||||
sourceIP: 'Source IP'
|
||||
}
|
||||
sourceIP: 'Source IP',
|
||||
},
|
||||
},
|
||||
Proxies: {
|
||||
title: 'Proxies',
|
||||
@ -91,7 +91,7 @@ const EN = {
|
||||
'obfs-host': 'Obfs-host',
|
||||
uuid: 'UUID',
|
||||
alterId: 'AlterId',
|
||||
tls: 'TLS'
|
||||
tls: 'TLS',
|
||||
},
|
||||
groupTitle: 'Policy Group',
|
||||
providerTitle: 'Providers',
|
||||
@ -99,12 +99,12 @@ const EN = {
|
||||
expandText: 'Expand',
|
||||
collapseText: 'Collapse',
|
||||
speedTestText: 'Speed Test',
|
||||
breakConnectionsText: 'Close connections which include the group'
|
||||
breakConnectionsText: 'Close connections which include the group',
|
||||
},
|
||||
Modal: {
|
||||
ok: 'Ok',
|
||||
cancel: 'Cancel'
|
||||
}
|
||||
cancel: 'Cancel',
|
||||
},
|
||||
}
|
||||
|
||||
export default EN
|
||||
|
@ -3,7 +3,7 @@ import zh_CN from './zh_CN'
|
||||
|
||||
export const Language = {
|
||||
en_US,
|
||||
zh_CN
|
||||
zh_CN,
|
||||
}
|
||||
|
||||
export type Lang = keyof typeof Language
|
||||
|
@ -6,7 +6,7 @@ const CN = {
|
||||
Rules: '规则',
|
||||
Settings: '设置',
|
||||
Connections: '连接',
|
||||
Version: '版本'
|
||||
Version: '版本',
|
||||
},
|
||||
Settings: {
|
||||
title: '设置',
|
||||
@ -19,7 +19,7 @@ const CN = {
|
||||
socks5ProxyPort: 'Socks5 代理端口',
|
||||
httpProxyPort: 'HTTP 代理端口',
|
||||
mixedProxyPort: '混合代理端口',
|
||||
externalController: '外部控制设置'
|
||||
externalController: '外部控制设置',
|
||||
},
|
||||
values: {
|
||||
cn: '中文',
|
||||
@ -27,7 +27,7 @@ const CN = {
|
||||
global: '全局',
|
||||
rules: '规则',
|
||||
direct: '直连',
|
||||
script: '脚本'
|
||||
script: '脚本',
|
||||
},
|
||||
versionString: '当前 ClashX 已是最新版本:{{version}}',
|
||||
checkUpdate: '检查更新',
|
||||
@ -36,17 +36,17 @@ const CN = {
|
||||
note: '请注意,修改该配置项并不会修改你的 Clash 配置文件,请确认修改后的外部控制地址和 Clash 配置文件内的地址一致,否则会导致 Dashboard 无法连接。',
|
||||
host: 'Host',
|
||||
port: '端口',
|
||||
secret: '密钥'
|
||||
}
|
||||
secret: '密钥',
|
||||
},
|
||||
},
|
||||
Logs: {
|
||||
title: '日志'
|
||||
title: '日志',
|
||||
},
|
||||
Rules: {
|
||||
title: '规则',
|
||||
providerTitle: '规则集',
|
||||
providerUpdateTime: '最后更新于',
|
||||
ruleCount: '规则条数'
|
||||
ruleCount: '规则条数',
|
||||
},
|
||||
Connections: {
|
||||
title: '连接',
|
||||
@ -54,14 +54,14 @@ const CN = {
|
||||
total: {
|
||||
text: '总量',
|
||||
upload: '上传',
|
||||
download: '下载'
|
||||
download: '下载',
|
||||
},
|
||||
closeAll: {
|
||||
title: '警告',
|
||||
content: '将会关闭所有连接'
|
||||
content: '将会关闭所有连接',
|
||||
},
|
||||
filter: {
|
||||
all: '全部'
|
||||
all: '全部',
|
||||
},
|
||||
columns: {
|
||||
host: '域名',
|
||||
@ -73,8 +73,8 @@ const CN = {
|
||||
speed: '速率',
|
||||
upload: '上传',
|
||||
download: '下载',
|
||||
sourceIP: '来源 IP'
|
||||
}
|
||||
sourceIP: '来源 IP',
|
||||
},
|
||||
},
|
||||
Proxies: {
|
||||
title: '代理',
|
||||
@ -91,7 +91,7 @@ const CN = {
|
||||
'obfs-host': 'Obfs-host',
|
||||
uuid: 'UUID',
|
||||
alterId: 'AlterId',
|
||||
tls: 'TLS'
|
||||
tls: 'TLS',
|
||||
},
|
||||
groupTitle: '策略组',
|
||||
providerTitle: '代理集',
|
||||
@ -99,12 +99,12 @@ const CN = {
|
||||
expandText: '展开',
|
||||
collapseText: '收起',
|
||||
speedTestText: '测速',
|
||||
breakConnectionsText: '切换时打断包含策略组的连接'
|
||||
breakConnectionsText: '切换时打断包含策略组的连接',
|
||||
},
|
||||
Modal: {
|
||||
ok: '确 定',
|
||||
cancel: '取 消'
|
||||
}
|
||||
cancel: '取 消',
|
||||
},
|
||||
}
|
||||
|
||||
export default CN
|
||||
|
@ -2,11 +2,11 @@ export function createAsyncSingleton<T> (fn: () => Promise<T>): () => Promise<T>
|
||||
let promise: Promise<T> | null = null
|
||||
|
||||
return async function () {
|
||||
if (promise) {
|
||||
return promise
|
||||
if (promise != null) {
|
||||
return await promise
|
||||
}
|
||||
promise = fn()
|
||||
return promise
|
||||
return await promise
|
||||
.catch(e => {
|
||||
promise = null
|
||||
throw e
|
||||
|
@ -53,7 +53,7 @@ export function useInterval (callback: () => void, delay: number) {
|
||||
return () => clearInterval(id)
|
||||
}
|
||||
},
|
||||
[delay]
|
||||
[delay],
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ export class JsBridge {
|
||||
instance: JsBridgeAPI | null = null
|
||||
|
||||
constructor (callback: () => void) {
|
||||
if (window.WebViewJavascriptBridge) {
|
||||
if (window.WebViewJavascriptBridge != null) {
|
||||
this.instance = window.WebViewJavascriptBridge
|
||||
}
|
||||
|
||||
@ -97,12 +97,12 @@ export class JsBridge {
|
||||
return callback?.(null)
|
||||
}
|
||||
|
||||
if (window.WebViewJavascriptBridge) {
|
||||
if (window.WebViewJavascriptBridge != null) {
|
||||
return callback(window.WebViewJavascriptBridge)
|
||||
}
|
||||
|
||||
// setup callback
|
||||
if (window.WVJBCallbacks) {
|
||||
if (window.WVJBCallbacks != null) {
|
||||
return window.WVJBCallbacks.push(callback)
|
||||
}
|
||||
|
||||
@ -115,63 +115,63 @@ export class JsBridge {
|
||||
setTimeout(() => document.documentElement.removeChild(WVJBIframe), 0)
|
||||
}
|
||||
|
||||
public callHandler<T> (handleName: string, data?: any) {
|
||||
return new Promise<T>((resolve) => {
|
||||
public async callHandler<T> (handleName: string, data?: any) {
|
||||
return await new Promise<T>((resolve) => {
|
||||
this.instance?.callHandler(
|
||||
handleName,
|
||||
data,
|
||||
resolve
|
||||
resolve,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
public ping () {
|
||||
return this.callHandler('ping')
|
||||
public async ping () {
|
||||
return await this.callHandler('ping')
|
||||
}
|
||||
|
||||
public readConfigString () {
|
||||
return this.callHandler<string>('readConfigString')
|
||||
public async readConfigString () {
|
||||
return await this.callHandler<string>('readConfigString')
|
||||
}
|
||||
|
||||
public getPasteboard () {
|
||||
return this.callHandler<string>('getPasteboard')
|
||||
public async getPasteboard () {
|
||||
return await this.callHandler<string>('getPasteboard')
|
||||
}
|
||||
|
||||
public getAPIInfo () {
|
||||
return this.callHandler<{ host: string, port: string, secret: string }>('apiInfo')
|
||||
public async getAPIInfo () {
|
||||
return await this.callHandler<{ host: string, port: string, secret: string }>('apiInfo')
|
||||
}
|
||||
|
||||
public setPasteboard (data: string) {
|
||||
return this.callHandler('setPasteboard', data)
|
||||
public async setPasteboard (data: string) {
|
||||
return await this.callHandler('setPasteboard', data)
|
||||
}
|
||||
|
||||
public writeConfigWithString (data: string) {
|
||||
return this.callHandler('writeConfigWithString', data)
|
||||
public async writeConfigWithString (data: string) {
|
||||
return await this.callHandler('writeConfigWithString', data)
|
||||
}
|
||||
|
||||
public setSystemProxy (data: boolean) {
|
||||
return this.callHandler('setSystemProxy', data)
|
||||
public async setSystemProxy (data: boolean) {
|
||||
return await this.callHandler('setSystemProxy', data)
|
||||
}
|
||||
|
||||
public getStartAtLogin () {
|
||||
return this.callHandler<boolean>('getStartAtLogin')
|
||||
public async getStartAtLogin () {
|
||||
return await this.callHandler<boolean>('getStartAtLogin')
|
||||
}
|
||||
|
||||
public getProxyDelay (name: string) {
|
||||
return this.callHandler<number>('speedTest', name)
|
||||
public async getProxyDelay (name: string) {
|
||||
return await this.callHandler<number>('speedTest', name)
|
||||
}
|
||||
|
||||
public setStartAtLogin (data: boolean) {
|
||||
return this.callHandler<boolean>('setStartAtLogin', data)
|
||||
public async setStartAtLogin (data: boolean) {
|
||||
return await this.callHandler<boolean>('setStartAtLogin', data)
|
||||
}
|
||||
|
||||
public isSystemProxySet () {
|
||||
return this.callHandler<boolean>('isSystemProxySet')
|
||||
public async isSystemProxySet () {
|
||||
return await this.callHandler<boolean>('isSystemProxySet')
|
||||
}
|
||||
}
|
||||
|
||||
export function setupJsBridge (callback: () => void) {
|
||||
if (jsBridge) {
|
||||
if (jsBridge != null) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
|
@ -94,32 +94,32 @@ export interface Connections {
|
||||
}
|
||||
|
||||
export class Client {
|
||||
private axiosClient: AxiosInstance
|
||||
constructor(url: string, secret?: string) {
|
||||
private readonly axiosClient: AxiosInstance
|
||||
constructor (url: string, secret?: string) {
|
||||
this.axiosClient = axios.create({
|
||||
baseURL: url,
|
||||
headers: secret ? { Authorization: `Bearer ${secret}` } : {}
|
||||
headers: secret ? { Authorization: `Bearer ${secret}` } : {},
|
||||
})
|
||||
}
|
||||
|
||||
getConfig() {
|
||||
return this.axiosClient.get<Config>('configs')
|
||||
async getConfig () {
|
||||
return await this.axiosClient.get<Config>('configs')
|
||||
}
|
||||
|
||||
updateConfig(config: Partial<Config>) {
|
||||
return this.axiosClient.patch<void>('configs', config)
|
||||
async updateConfig (config: Partial<Config>) {
|
||||
return await this.axiosClient.patch<void>('configs', config)
|
||||
}
|
||||
|
||||
getRules() {
|
||||
return this.axiosClient.get<Rules>('rules')
|
||||
async getRules () {
|
||||
return await this.axiosClient.get<Rules>('rules')
|
||||
}
|
||||
|
||||
async getProxyProviders () {
|
||||
const resp = await this.axiosClient.get<ProxyProviders>('providers/proxies', {
|
||||
validateStatus(status) {
|
||||
validateStatus (status) {
|
||||
// compatible old version
|
||||
return (status >= 200 && status < 300) || status === 404
|
||||
}
|
||||
},
|
||||
})
|
||||
if (resp.status === 404) {
|
||||
resp.data = { providers: {} }
|
||||
@ -127,56 +127,56 @@ export class Client {
|
||||
return resp
|
||||
}
|
||||
|
||||
getRuleProviders () {
|
||||
return this.axiosClient.get<RuleProviders>('providers/rules')
|
||||
async getRuleProviders () {
|
||||
return await this.axiosClient.get<RuleProviders>('providers/rules')
|
||||
}
|
||||
|
||||
updateProvider (name: string) {
|
||||
return this.axiosClient.put<void>(`providers/proxies/${encodeURIComponent(name)}`)
|
||||
async updateProvider (name: string) {
|
||||
return await this.axiosClient.put<void>(`providers/proxies/${encodeURIComponent(name)}`)
|
||||
}
|
||||
|
||||
updateRuleProvider (name: string) {
|
||||
return this.axiosClient.put<void>(`providers/rules/${encodeURIComponent(name)}`)
|
||||
async updateRuleProvider (name: string) {
|
||||
return await this.axiosClient.put<void>(`providers/rules/${encodeURIComponent(name)}`)
|
||||
}
|
||||
|
||||
healthCheckProvider (name: string) {
|
||||
return this.axiosClient.get<void>(`providers/proxies/${encodeURIComponent(name)}/healthcheck`)
|
||||
async healthCheckProvider (name: string) {
|
||||
return await this.axiosClient.get<void>(`providers/proxies/${encodeURIComponent(name)}/healthcheck`)
|
||||
}
|
||||
|
||||
getProxies () {
|
||||
return this.axiosClient.get<Proxies>('proxies')
|
||||
async getProxies () {
|
||||
return await this.axiosClient.get<Proxies>('proxies')
|
||||
}
|
||||
|
||||
getProxy (name: string) {
|
||||
return this.axiosClient.get<Proxy>(`proxies/${encodeURIComponent(name)}`)
|
||||
async getProxy (name: string) {
|
||||
return await this.axiosClient.get<Proxy>(`proxies/${encodeURIComponent(name)}`)
|
||||
}
|
||||
|
||||
getVersion () {
|
||||
return this.axiosClient.get<{ version: string, premium?: boolean }>('version')
|
||||
async getVersion () {
|
||||
return await this.axiosClient.get<{ version: string, premium?: boolean }>('version')
|
||||
}
|
||||
|
||||
getProxyDelay (name: string) {
|
||||
return this.axiosClient.get<{ delay: number }>(`proxies/${encodeURIComponent(name)}/delay`, {
|
||||
async getProxyDelay (name: string) {
|
||||
return await this.axiosClient.get<{ delay: number }>(`proxies/${encodeURIComponent(name)}/delay`, {
|
||||
params: {
|
||||
timeout: 5000,
|
||||
url: 'http://www.gstatic.com/generate_204'
|
||||
}
|
||||
url: 'http://www.gstatic.com/generate_204',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
closeAllConnections () {
|
||||
return this.axiosClient.delete('connections')
|
||||
async closeAllConnections () {
|
||||
return await this.axiosClient.delete('connections')
|
||||
}
|
||||
|
||||
closeConnection (id: string) {
|
||||
return this.axiosClient.delete(`connections/${id}`)
|
||||
async closeConnection (id: string) {
|
||||
return await this.axiosClient.delete(`connections/${id}`)
|
||||
}
|
||||
|
||||
getConnections () {
|
||||
return this.axiosClient.get<Snapshot>('connections')
|
||||
async getConnections () {
|
||||
return await this.axiosClient.get<Snapshot>('connections')
|
||||
}
|
||||
|
||||
changeProxySelected (name: string, select: string) {
|
||||
return this.axiosClient.put<void>(`proxies/${encodeURIComponent(name)}`, { name: select })
|
||||
async changeProxySelected (name: string, select: string) {
|
||||
return await this.axiosClient.put<void>(`proxies/${encodeURIComponent(name)}`, { name: select })
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,9 @@ export class StreamReader<T> {
|
||||
{
|
||||
bufferLength: 0,
|
||||
retryInterval: 5000,
|
||||
headers: {}
|
||||
headers: {},
|
||||
},
|
||||
config
|
||||
config,
|
||||
)
|
||||
|
||||
this.config.useWebsocket
|
||||
@ -60,13 +60,13 @@ export class StreamReader<T> {
|
||||
this.config.url,
|
||||
{
|
||||
mode: 'cors',
|
||||
headers: this.config.token ? { Authorization: `Bearer ${this.config.token}` } : {}
|
||||
}
|
||||
headers: this.config.token ? { Authorization: `Bearer ${this.config.token}` } : {},
|
||||
},
|
||||
), e => e as Error)
|
||||
if (result.isErr()) {
|
||||
this.retry(result.error)
|
||||
return
|
||||
} else if (!result.value.body) {
|
||||
} else if (result.value.body == null) {
|
||||
this.retry(new Error('fetch body error'))
|
||||
return
|
||||
}
|
||||
@ -87,7 +87,7 @@ export class StreamReader<T> {
|
||||
const lines = decoder.decode(result.value.value).trim().split('\n')
|
||||
const data = lines.map(l => JSON.parse(l))
|
||||
this.EE.emit('data', data)
|
||||
if (this.config.bufferLength! > 0) {
|
||||
if (this.config.bufferLength > 0) {
|
||||
this.innerBuffer.push(...data)
|
||||
if (this.innerBuffer.length > this.config.bufferLength) {
|
||||
this.innerBuffer.splice(0, this.innerBuffer.length - this.config.bufferLength)
|
||||
@ -99,7 +99,7 @@ export class StreamReader<T> {
|
||||
protected retry (err: Error) {
|
||||
if (!this.isClose) {
|
||||
this.EE.emit('error', err)
|
||||
window.setTimeout(this.loop, this.config.retryInterval)
|
||||
window.setTimeout(() => { this.loop() }, this.config.retryInterval)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ export const SsCipher = [
|
||||
'AES-256-CFB',
|
||||
'CHACHA20',
|
||||
'CHACHA20-IETF',
|
||||
'XCHACHA20'
|
||||
'XCHACHA20',
|
||||
]
|
||||
|
||||
/**
|
||||
@ -31,7 +31,7 @@ export const VmessCipher = [
|
||||
'auto',
|
||||
'none',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305'
|
||||
'chacha20-poly1305',
|
||||
]
|
||||
|
||||
/**
|
||||
@ -41,17 +41,17 @@ export function pickCipherWithAlias (c: string) {
|
||||
const cipher = c.toUpperCase()
|
||||
|
||||
switch (cipher) {
|
||||
case 'CHACHA20-IETF-POLY1305':
|
||||
return 'AEAD_CHACHA20_POLY1305'
|
||||
case 'XCHACHA20-IETF-POLY1305':
|
||||
return 'AEAD_XCHACHA20_POLY1305'
|
||||
case 'AES-128-GCM':
|
||||
return 'AEAD_AES_128_GCM'
|
||||
case 'AES-196-GCM':
|
||||
return 'AEAD_AES_196_GCM'
|
||||
case 'AES-256-GCM':
|
||||
return 'AEAD_AES_256_GCM'
|
||||
case 'CHACHA20-IETF-POLY1305':
|
||||
return 'AEAD_CHACHA20_POLY1305'
|
||||
case 'XCHACHA20-IETF-POLY1305':
|
||||
return 'AEAD_XCHACHA20_POLY1305'
|
||||
case 'AES-128-GCM':
|
||||
return 'AEAD_AES_128_GCM'
|
||||
case 'AES-196-GCM':
|
||||
return 'AEAD_AES_196_GCM'
|
||||
case 'AES-256-GCM':
|
||||
return 'AEAD_AES_256_GCM'
|
||||
}
|
||||
|
||||
return SsCipher.find(c => c === cipher) || ''
|
||||
return SsCipher.find(c => c === cipher) ?? ''
|
||||
}
|
||||
|
@ -5,13 +5,13 @@
|
||||
export const ProxyType = {
|
||||
Shadowsocks: 'ss',
|
||||
Vmess: 'vmess',
|
||||
Socks5: 'socks5'
|
||||
Socks5: 'socks5',
|
||||
}
|
||||
|
||||
export type Proxy = ShadowsocksProxy & VmessProxy & Socks5Proxy
|
||||
|
||||
export const SsProxyConfigList = [
|
||||
'name', 'type', 'server', 'port', 'cipher', 'password', 'obfs', 'obfs-host'
|
||||
'name', 'type', 'server', 'port', 'cipher', 'password', 'obfs', 'obfs-host',
|
||||
]
|
||||
export interface ShadowsocksProxy {
|
||||
name?: string
|
||||
@ -32,7 +32,7 @@ export interface ShadowsocksProxy {
|
||||
}
|
||||
|
||||
export const VmessProxyConfigList = [
|
||||
'name', 'type', 'server', 'port', 'uuid', 'alterid', 'cipher', 'tls'
|
||||
'name', 'type', 'server', 'port', 'uuid', 'alterid', 'cipher', 'tls',
|
||||
]
|
||||
export interface VmessProxy {
|
||||
name?: string
|
||||
|
@ -34,7 +34,7 @@ export function useI18n () {
|
||||
}
|
||||
return { t }
|
||||
},
|
||||
[lang]
|
||||
[lang],
|
||||
)
|
||||
|
||||
return { lang, locales, setLang, translation }
|
||||
@ -42,7 +42,7 @@ export function useI18n () {
|
||||
|
||||
export const version = atom({
|
||||
version: '',
|
||||
premium: false
|
||||
premium: false,
|
||||
})
|
||||
|
||||
export function useVersion () {
|
||||
@ -57,7 +57,7 @@ export function useVersion () {
|
||||
set(
|
||||
result.isErr()
|
||||
? { version: '', premium: false }
|
||||
: { version: result.value.data.version, premium: !!result.value.data.premium }
|
||||
: { version: result.value.data.version, premium: !!result.value.data.premium },
|
||||
)
|
||||
})
|
||||
|
||||
@ -83,7 +83,7 @@ export function useRuleProviders () {
|
||||
}
|
||||
|
||||
export const configAtom = atomWithStorage('profile', {
|
||||
breakConnections: false
|
||||
breakConnections: false,
|
||||
})
|
||||
|
||||
export function useConfig () {
|
||||
@ -128,7 +128,7 @@ export function useGeneral () {
|
||||
redirPort: data['redir-port'],
|
||||
mode: data.mode.toLowerCase() as Models.Data['general']['mode'],
|
||||
logLevel: data['log-level'],
|
||||
allowLan: data['allow-lan']
|
||||
allowLan: data['allow-lan'],
|
||||
} as Models.Data['general']
|
||||
})
|
||||
|
||||
@ -143,8 +143,8 @@ export const proxies = atomWithImmer({
|
||||
type: 'Selector',
|
||||
now: '',
|
||||
history: [],
|
||||
all: []
|
||||
} as API.Group
|
||||
all: [],
|
||||
} as API.Group,
|
||||
})
|
||||
|
||||
export function useProxy () {
|
||||
@ -187,7 +187,7 @@ export function useProxy () {
|
||||
global: allProxy.global,
|
||||
update: mutate,
|
||||
markProxySelected,
|
||||
set
|
||||
set,
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ export const proxyMapping = atom((get) => {
|
||||
const providers = get(proxyProvider)
|
||||
const proxyMap = new Map<string, API.Proxy>()
|
||||
for (const p of ps.proxies) {
|
||||
proxyMap.set(p.name, p as API.Proxy)
|
||||
proxyMap.set(p.name, p)
|
||||
}
|
||||
|
||||
for (const provider of providers) {
|
||||
@ -214,7 +214,7 @@ export function useClashXData () {
|
||||
return {
|
||||
isClashX: false,
|
||||
startAtLogin: false,
|
||||
systemProxy: false
|
||||
systemProxy: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ export function useRule () {
|
||||
|
||||
const logsAtom = atom({
|
||||
key: '',
|
||||
instance: null as StreamReader<Log> | null
|
||||
instance: null as StreamReader<Log> | null,
|
||||
})
|
||||
|
||||
export function useLogsStreamReader () {
|
||||
@ -269,7 +269,7 @@ export function useLogsStreamReader () {
|
||||
const instance = new StreamReader<Log>({ url: logUrl, bufferLength: 200, token: apiInfo.secret, useWebsocket })
|
||||
setItem({ key, instance })
|
||||
|
||||
if (oldInstance) {
|
||||
if (oldInstance != null) {
|
||||
oldInstance.destory()
|
||||
}
|
||||
|
||||
@ -285,6 +285,6 @@ export function useConnectionStreamReader () {
|
||||
const url = `${apiInfo.protocol}//${apiInfo.hostname}:${apiInfo.port}/connections`
|
||||
return useMemo(
|
||||
() => version.version ? new StreamReader<Snapshot>({ url, bufferLength: 200, token: apiInfo.secret, useWebsocket }) : null,
|
||||
[apiInfo.secret, url, useWebsocket, version.version]
|
||||
[apiInfo.secret, url, useWebsocket, version.version],
|
||||
)
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { atom, useAtom } from "jotai";
|
||||
import { isClashX, jsBridge } from "@lib/jsBridge";
|
||||
import { atomWithStorage, useAtomValue } from "jotai/utils";
|
||||
import { useLocation } from "react-use";
|
||||
import { Client } from "@lib/request";
|
||||
import { atom, useAtom } from 'jotai'
|
||||
import { isClashX, jsBridge } from '@lib/jsBridge'
|
||||
import { atomWithStorage, useAtomValue } from 'jotai/utils'
|
||||
import { useLocation } from 'react-use'
|
||||
import { Client } from '@lib/request'
|
||||
|
||||
const clashxConfigAtom = atom(async () => {
|
||||
if (!isClashX()) {
|
||||
@ -14,29 +14,29 @@ const clashxConfigAtom = atom(async () => {
|
||||
hostname: info.host,
|
||||
port: info.port,
|
||||
secret: info.secret,
|
||||
protocol: 'http:'
|
||||
protocol: 'http:',
|
||||
}
|
||||
})
|
||||
|
||||
export const localStorageAtom = atomWithStorage<{
|
||||
hostname: string;
|
||||
port: string;
|
||||
secret: string;
|
||||
}[]>('externalControllers', [])
|
||||
export const localStorageAtom = atomWithStorage<Array<{
|
||||
hostname: string
|
||||
port: string
|
||||
secret: string
|
||||
}>>('externalControllers', [])
|
||||
|
||||
export function useAPIInfo() {
|
||||
export function useAPIInfo () {
|
||||
const clashx = useAtomValue(clashxConfigAtom)
|
||||
const location = useLocation()
|
||||
const localStorage = useAtomValue(localStorageAtom)
|
||||
|
||||
if (clashx) {
|
||||
if (clashx != null) {
|
||||
return clashx
|
||||
}
|
||||
|
||||
let url: URL | undefined;
|
||||
let url: URL | undefined
|
||||
{
|
||||
const meta = document.querySelector<HTMLMetaElement>('meta[name="external-controller"]')
|
||||
if (meta?.content?.match(/^https?:/)) {
|
||||
if ((meta?.content?.match(/^https?:/)) != null) {
|
||||
// [protocol]://[secret]@[hostname]:[port]
|
||||
url = new URL(meta.content)
|
||||
}
|
||||
@ -54,15 +54,15 @@ export function useAPIInfo() {
|
||||
|
||||
const clientAtom = atom({
|
||||
key: '',
|
||||
instance: null as Client | null
|
||||
instance: null as Client | null,
|
||||
})
|
||||
|
||||
export function useClient() {
|
||||
export function useClient () {
|
||||
const {
|
||||
hostname,
|
||||
port,
|
||||
secret,
|
||||
protocol
|
||||
protocol,
|
||||
} = useAPIInfo()
|
||||
|
||||
const [item, setItem] = useAtom(clientAtom)
|
||||
|
95
yarn.lock
95
yarn.lock
@ -422,6 +422,16 @@
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
|
||||
"@typescript-eslint/parser@^4.0.0":
|
||||
version "4.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.1.tgz#5181b81658414f47291452c15bf6cd44a32f85bd"
|
||||
integrity sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "4.28.1"
|
||||
"@typescript-eslint/types" "4.28.1"
|
||||
"@typescript-eslint/typescript-estree" "4.28.1"
|
||||
debug "^4.3.1"
|
||||
|
||||
"@typescript-eslint/parser@^4.28.0":
|
||||
version "4.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.0.tgz#2404c16751a28616ef3abab77c8e51d680a12caa"
|
||||
@ -440,11 +450,24 @@
|
||||
"@typescript-eslint/types" "4.28.0"
|
||||
"@typescript-eslint/visitor-keys" "4.28.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@4.28.1":
|
||||
version "4.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz#fd3c20627cdc12933f6d98b386940d8d0ce8a991"
|
||||
integrity sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.28.1"
|
||||
"@typescript-eslint/visitor-keys" "4.28.1"
|
||||
|
||||
"@typescript-eslint/types@4.28.0":
|
||||
version "4.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.0.tgz#a33504e1ce7ac51fc39035f5fe6f15079d4dafb0"
|
||||
integrity sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==
|
||||
|
||||
"@typescript-eslint/types@4.28.1":
|
||||
version "4.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.1.tgz#d0f2ecbef3684634db357b9bbfc97b94b828f83f"
|
||||
integrity sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg==
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.28.0":
|
||||
version "4.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz#e66d4e5aa2ede66fec8af434898fe61af10c71cf"
|
||||
@ -458,6 +481,19 @@
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.28.1":
|
||||
version "4.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz#af882ae41740d1f268e38b4d0fad21e7e8d86a81"
|
||||
integrity sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.28.1"
|
||||
"@typescript-eslint/visitor-keys" "4.28.1"
|
||||
debug "^4.3.1"
|
||||
globby "^11.0.3"
|
||||
is-glob "^4.0.1"
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.28.0":
|
||||
version "4.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz#255c67c966ec294104169a6939d96f91c8a89434"
|
||||
@ -466,6 +502,14 @@
|
||||
"@typescript-eslint/types" "4.28.0"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.28.1":
|
||||
version "4.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz#162a515ee255f18a6068edc26df793cdc1ec9157"
|
||||
integrity sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.28.1"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@vitejs/plugin-react-refresh@^1.3.3":
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react-refresh/-/plugin-react-refresh-1.3.3.tgz#d5afb3e0463f368a8afadfdd7305fe5c5fe78a6a"
|
||||
@ -1016,6 +1060,19 @@ eslint-config-react-app@^6.0.0:
|
||||
dependencies:
|
||||
confusing-browser-globals "^1.0.10"
|
||||
|
||||
eslint-config-standard-with-typescript@^20.0.0:
|
||||
version "20.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-20.0.0.tgz#0c550eca0a216cbf8da9013eb6e311acd3102d87"
|
||||
integrity sha512-IoySf3r0a2+P3Z6GMjv8p1HuOQ6GWQbMpdt9G8uEbkGpnNWAGBXpgaiutbZHbaQAvG5pkVtYepCfHUxYbVDLCA==
|
||||
dependencies:
|
||||
"@typescript-eslint/parser" "^4.0.0"
|
||||
eslint-config-standard "^16.0.0"
|
||||
|
||||
eslint-config-standard@^16.0.0:
|
||||
version "16.0.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz#6c8761e544e96c531ff92642eeb87842b8488516"
|
||||
integrity sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==
|
||||
|
||||
eslint-import-resolver-node@^0.3.4:
|
||||
version "0.3.4"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
|
||||
@ -1032,6 +1089,14 @@ eslint-module-utils@^2.6.1:
|
||||
debug "^3.2.7"
|
||||
pkg-dir "^2.0.0"
|
||||
|
||||
eslint-plugin-es@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893"
|
||||
integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==
|
||||
dependencies:
|
||||
eslint-utils "^2.0.0"
|
||||
regexpp "^3.0.0"
|
||||
|
||||
eslint-plugin-flowtype@^5.7.2:
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.7.2.tgz#482a42fe5d15ee614652ed256d37543d584d7bc0"
|
||||
@ -1078,6 +1143,23 @@ eslint-plugin-jsx-a11y@^6.4.1:
|
||||
jsx-ast-utils "^3.1.0"
|
||||
language-tags "^1.0.5"
|
||||
|
||||
eslint-plugin-node@^11.1.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d"
|
||||
integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==
|
||||
dependencies:
|
||||
eslint-plugin-es "^3.0.0"
|
||||
eslint-utils "^2.0.0"
|
||||
ignore "^5.1.1"
|
||||
minimatch "^3.0.4"
|
||||
resolve "^1.10.1"
|
||||
semver "^6.1.0"
|
||||
|
||||
eslint-plugin-promise@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz#fb2188fb734e4557993733b41aa1a688f46c6f24"
|
||||
integrity sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==
|
||||
|
||||
eslint-plugin-react-hooks@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556"
|
||||
@ -1109,7 +1191,7 @@ eslint-scope@^5.1.1:
|
||||
esrecurse "^4.3.0"
|
||||
estraverse "^4.1.1"
|
||||
|
||||
eslint-utils@^2.1.0:
|
||||
eslint-utils@^2.0.0, eslint-utils@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
|
||||
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
|
||||
@ -1469,7 +1551,7 @@ ignore@^4.0.6:
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||
|
||||
ignore@^5.1.4:
|
||||
ignore@^5.1.1, ignore@^5.1.4:
|
||||
version "5.1.8"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
|
||||
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
|
||||
@ -2258,6 +2340,11 @@ regexp.prototype.flags@^1.3.1:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
|
||||
regexpp@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
|
||||
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
|
||||
|
||||
regexpp@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
|
||||
@ -2283,7 +2370,7 @@ resolve-pathname@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
|
||||
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
|
||||
|
||||
resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.20.0:
|
||||
resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.20.0:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
||||
@ -2362,7 +2449,7 @@ screenfull@^5.1.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@^6.3.0:
|
||||
semver@^6.1.0, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
Loading…
x
Reference in New Issue
Block a user