修正了一堆东西
This commit is contained in:
parent
376b5964a1
commit
6e3f55480a
@ -1,8 +0,0 @@
|
|||||||
import {request} from "~/composables/network";
|
|
||||||
|
|
||||||
export async function getBlogRecentPost(): Promise<IBlogResponse<IPostsData>> {
|
|
||||||
return request({
|
|
||||||
url: "/blog/index.php/api/posts",
|
|
||||||
method: "get"
|
|
||||||
})
|
|
||||||
}
|
|
16
api/git.ts
16
api/git.ts
@ -1,16 +0,0 @@
|
|||||||
import {request} from "~/composables/network";
|
|
||||||
|
|
||||||
const TOKEN: string = "fb8aec429ea7d0a36f7238dbffda9d2d66c7b045"
|
|
||||||
|
|
||||||
export async function getActivity(): Promise<IActivity[]> {
|
|
||||||
return request({
|
|
||||||
url: '/git/api/v1/users/cantyonion/activities/feeds',
|
|
||||||
method: 'get',
|
|
||||||
headers: {
|
|
||||||
Authorization: ` ${TOKEN}`,
|
|
||||||
},
|
|
||||||
params: {
|
|
||||||
limit: 10
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
4
app.vue
4
app.vue
@ -16,8 +16,8 @@ emitter.on('startLoading', on => {
|
|||||||
<div ref="loading" class="loading"></div>
|
<div ref="loading" class="loading"></div>
|
||||||
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
||||||
<nav-bar/>
|
<nav-bar/>
|
||||||
<NuxtPage/>
|
<nuxt-page/>
|
||||||
<footer-main/>
|
<footer-main/>
|
||||||
|
<alert-notification/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
47
components/alert/Notification.vue
Normal file
47
components/alert/Notification.vue
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
const notifications = ref<INotification[]>([]);
|
||||||
|
|
||||||
|
const typeClasses = {
|
||||||
|
success: 'bg-green-100 text-green-800 border border-green-400',
|
||||||
|
error: 'bg-red-100 text-red-800 border border-red-400',
|
||||||
|
warning: 'bg-yellow-100 text-yellow-800 border border-yellow-400',
|
||||||
|
info: 'bg-blue-100 text-blue-800 border border-blue-400',
|
||||||
|
};
|
||||||
|
|
||||||
|
const addNotification = (notification: INotification) => {
|
||||||
|
notifications.value.push(notification);
|
||||||
|
|
||||||
|
// 自动移除通知
|
||||||
|
setTimeout(() => {
|
||||||
|
notifications.value.shift();
|
||||||
|
}, 5000);
|
||||||
|
console.log(1)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
emitter.on('eventBus', addNotification)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
emitter.off('eventBus', addNotification);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="fixed top-5 right-5 space-y-4 z-50">
|
||||||
|
<div
|
||||||
|
v-for="(notification, index) in notifications"
|
||||||
|
:key="index"
|
||||||
|
:class="[
|
||||||
|
'p-4 rounded-lg shadow-lg transition-all duration-300',
|
||||||
|
typeClasses[notification.level]
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<h3 class="font-bold text-lg">{{ notification.title }}</h3>
|
||||||
|
<p>{{ notification.message }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -30,7 +30,7 @@ const handleReload = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<!-- 正式内容 -->
|
<!-- 正式内容 -->
|
||||||
<span v-if="empty" class="text-2xl font-bold">最近没有动态</span>
|
<span v-if="empty" class="text-center text-2xl font-bold">最近没有动态</span>
|
||||||
<slot v-else name="default"/>
|
<slot v-else name="default"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,6 +7,12 @@ type Widget = {
|
|||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
navColor: {
|
||||||
|
default: false,
|
||||||
|
type: Boolean
|
||||||
|
}
|
||||||
|
})
|
||||||
// 使用 ref 跟踪是否滚动
|
// 使用 ref 跟踪是否滚动
|
||||||
const scrolled = ref(false)
|
const scrolled = ref(false)
|
||||||
const littleWidget: Widget[] = [
|
const littleWidget: Widget[] = [
|
||||||
@ -94,7 +100,7 @@ const handleScroll = () => {
|
|||||||
|
|
||||||
const debouncedScroll = debounce(handleScroll, 10)
|
const debouncedScroll = debounce(handleScroll, 10)
|
||||||
|
|
||||||
const alwaysBlueBackground = computed(() => route.name !== 'index')
|
const alwaysBlueBackground = computed(() => route.name !== 'index' || props.navColor)
|
||||||
|
|
||||||
// 在组件挂载和卸载时添加和移除事件监听
|
// 在组件挂载和卸载时添加和移除事件监听
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -3,6 +3,7 @@ import mitt from 'mitt'
|
|||||||
type Events = {
|
type Events = {
|
||||||
openPost: IPost
|
openPost: IPost
|
||||||
startLoading: boolean
|
startLoading: boolean
|
||||||
|
eventBus: INotification
|
||||||
}
|
}
|
||||||
|
|
||||||
const emitter = mitt<Events>()
|
const emitter = mitt<Events>()
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
export const getAssetURL = (image: string) => {
|
export const getAssetURL = (image: string) => {
|
||||||
// 参数一: 相对路径
|
// 参数一: 相对路径
|
||||||
// 参数二: 当前路径的URL
|
// 参数二: 当前路径的URL
|
||||||
return new URL(`../assets/images/${image}`, import.meta.url).href
|
if (import.meta.client)
|
||||||
|
return new URL(`../assets/images/${image}`, import.meta.url).href
|
||||||
|
return `/_nuxt/assets/images/${image}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const debounce = (fn: Function, delay: number) => {
|
export const debounce = (fn: Function, delay: number) => {
|
||||||
|
@ -1,24 +1,53 @@
|
|||||||
import axios from 'axios'
|
import type { AsyncData, UseFetchOptions } from '#app'
|
||||||
import type {AxiosRequestConfig, AxiosError} from 'axios'
|
|
||||||
|
|
||||||
export async function request<T = unknown>(config: AxiosRequestConfig<any>): Promise<T> {
|
export function requestCore<DataT>(url: string, options: UseFetchOptions<DataT>) {
|
||||||
const instance = axios.create({
|
// const config = useRuntimeConfig()
|
||||||
baseURL: import.meta.env.VITE_BASE_URL,
|
return useFetch(url, {
|
||||||
timeout: 5000,
|
baseURL: "https://cantyonion.site",
|
||||||
headers: {},
|
retry: false,
|
||||||
|
onRequest({ options }) {
|
||||||
|
let token: string = ""
|
||||||
|
if (import.meta.client) {
|
||||||
|
token = localStorage.getItem("token") || ""
|
||||||
|
}
|
||||||
|
if (!options.headers.get('Authorization'))
|
||||||
|
options.headers.set('Authorization', `Bearer ${token}`)
|
||||||
|
},
|
||||||
|
onResponse({ response }) {
|
||||||
|
// HTTP状态码2XX/3XX执行,否则不执行
|
||||||
|
if (response.status < 200 || response.status >= 400) {
|
||||||
|
console.error(`HTTP 错误: ${response.status}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 业务code状态码判断
|
||||||
|
// if (response._data.code !== 200) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
onResponseError({ response }) {
|
||||||
|
emitter.emit('eventBus', {
|
||||||
|
level: 'error',
|
||||||
|
title: `HTTP错误:${response.status}`,
|
||||||
|
message: response.statusText
|
||||||
|
})
|
||||||
|
},
|
||||||
|
...options
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
instance.interceptors.request.use(config => {
|
export function get<DataT, ErrorT>(url: string, options?: UseFetchOptions<DataT>): () => Promise<DataT> {
|
||||||
return config
|
let result: AsyncData<DataT, ErrorT> | null = null
|
||||||
}, error => error)
|
|
||||||
|
|
||||||
// bug fixed on csdn https://blog.csdn.net/qq_45325810/article/details/120704910
|
return async (): Promise<DataT> => {
|
||||||
instance.interceptors.response.use(resource => {
|
if (result != null) {
|
||||||
if (resource.status === 200) return resource
|
await result.refresh()
|
||||||
return Promise.reject(new Error(resource.data))
|
return result.data.value
|
||||||
}, (error: AxiosError) => {
|
}
|
||||||
return Promise.reject(error.response ? error.response.data : error.code)
|
|
||||||
})
|
|
||||||
|
|
||||||
return instance.request<T>(config).then(res => res.data)
|
result = await requestCore(url, {
|
||||||
|
method: 'GET',
|
||||||
|
...options,
|
||||||
|
}) as AsyncData<DataT, ErrorT>
|
||||||
|
return result.data.value
|
||||||
|
}
|
||||||
}
|
}
|
60
error.vue
Normal file
60
error.vue
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { NuxtError } from '#app'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
error: Object as () => NuxtError
|
||||||
|
})
|
||||||
|
|
||||||
|
const errorMessages: Record<string, { icon: string; title: string; message: string }> = {
|
||||||
|
'400': {
|
||||||
|
icon: '❓',
|
||||||
|
title: '哦豁!请求有点问题',
|
||||||
|
message: '请求似乎有点小问题,服务器君有点摸不着头脑呢!',
|
||||||
|
},
|
||||||
|
'403': {
|
||||||
|
icon: '🔒',
|
||||||
|
title: '哎呀!大门被锁住了',
|
||||||
|
message: '你没有权限访问这个角落,试试返回首页吧!',
|
||||||
|
},
|
||||||
|
'404': {
|
||||||
|
icon: '🌌',
|
||||||
|
title: '哎呀!你发现了超级基地的秘密角落',
|
||||||
|
message: '看起来你找的页面藏到了未知的时空层里!',
|
||||||
|
},
|
||||||
|
'500': {
|
||||||
|
icon: '🚀',
|
||||||
|
title: '糟糕!超级基地出了一点故障',
|
||||||
|
message: '服务器君正在努力解决问题,请稍等片刻!',
|
||||||
|
},
|
||||||
|
'502': {
|
||||||
|
icon: '👽',
|
||||||
|
title: '糟糕!基地信号被外星人拦截了',
|
||||||
|
message: '网络通道似乎遇到了问题,请稍后再试!',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
icon: '⚠️',
|
||||||
|
title: '哦豁!此时遇到了点小麻烦',
|
||||||
|
message: '请求在穿越洋葱星球时迷路了,请返回首页重新探索!',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorContent = computed(() => errorMessages[props.error!.statusCode] || errorMessages['default']);
|
||||||
|
console.error(props.error)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
emitter.emit("startLoading", false)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="relative flex min-h-screen flex-col overflow-hidden bg-blue-50">
|
||||||
|
<nav-bar :nav-color="true"/>
|
||||||
|
<alert :title="errorContent.title" :message="errorContent.message" :icon="errorContent.icon"/>
|
||||||
|
<footer-main/>
|
||||||
|
<alert-notification/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -11,13 +11,13 @@ export default defineNuxtConfig({
|
|||||||
head: {
|
head: {
|
||||||
title: 'CANTYONION.SITE',
|
title: 'CANTYONION.SITE',
|
||||||
meta: [
|
meta: [
|
||||||
{name: 'viewport', content: 'width=device-width, initial-scale=1'},
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||||
{name: 'charset', content: 'utf-8'},
|
{ name: 'charset', content: 'utf-8' },
|
||||||
{name: 'keywords', content: 'cantyonion, onion, 洋葱, 博客, 学习, 主页, index'},
|
{ name: 'keywords', content: 'cantyonion, onion, 洋葱, 博客, 学习, 主页, index' },
|
||||||
{name: 'description', content: 'cantyonion的超级基地,进来看看吧!'},
|
{ name: 'description', content: 'cantyonion的超级基地,进来看看吧!' },
|
||||||
],
|
],
|
||||||
link: [
|
link: [
|
||||||
{rel: 'icon', href: '/favicon.png'},
|
{ rel: 'icon', href: '/favicon.png' },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -43,14 +43,11 @@ export default defineNuxtConfig({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
server: {
|
},
|
||||||
proxy: {
|
runtimeConfig: {
|
||||||
'/api': {
|
public: {
|
||||||
target: 'https://cantyonion.site',
|
gitApiKey: 'fb8aec429ea7d0a36f7238dbffda9d2d66c7b045',
|
||||||
changeOrigin: true,
|
baseURL: 'https://cantyonion.site'
|
||||||
rewrite: (path) => path.replace(/^\/api/, '')
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"@pinia-plugin-persistedstate/nuxt": "^1.2.1",
|
"@pinia-plugin-persistedstate/nuxt": "^1.2.1",
|
||||||
"@pinia/nuxt": "^0.7.0",
|
"@pinia/nuxt": "^0.7.0",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
|
"mathjax": "^3.2.2",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nuxt": "^3.14.159",
|
"nuxt": "^3.14.159",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
|
11
pages/auth.vue
Normal file
11
pages/auth.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
pages/blog/[cid].vue
Normal file
14
pages/blog/[cid].vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const route = useRoute()
|
||||||
|
const cid = route.params.cid as string
|
||||||
|
onMounted( () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -1,48 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const errorCode = route.params.code as string
|
const errorCode = parseInt(route.params.code as string, 10)
|
||||||
const errorMessages: Record<string, { icon: string; title: string; message: string }> = {
|
|
||||||
'400': {
|
|
||||||
icon: '❓',
|
|
||||||
title: '哦豁!请求有点问题',
|
|
||||||
message: '请求似乎有点小问题,服务器君有点摸不着头脑呢!',
|
|
||||||
},
|
|
||||||
'403': {
|
|
||||||
icon: '🔒',
|
|
||||||
title: '哎呀!大门被锁住了',
|
|
||||||
message: '你没有权限访问这个角落,试试返回首页吧!',
|
|
||||||
},
|
|
||||||
'404': {
|
|
||||||
icon: '🌌',
|
|
||||||
title: '哎呀!你发现了超级基地的秘密角落',
|
|
||||||
message: '看起来你找的页面藏到了未知的时空层里!',
|
|
||||||
},
|
|
||||||
'500': {
|
|
||||||
icon: '🚀',
|
|
||||||
title: '糟糕!超级基地出了一点故障',
|
|
||||||
message: '服务器君正在努力解决问题,请稍等片刻!',
|
|
||||||
},
|
|
||||||
'502': {
|
|
||||||
icon: '👽',
|
|
||||||
title: '糟糕!基地信号被外星人拦截了',
|
|
||||||
message: '网络通道似乎遇到了问题,请稍后再试!',
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
icon: '⚠️',
|
|
||||||
title: '哦豁!此时遇到了点小麻烦',
|
|
||||||
message: '请求在穿越洋葱星球时迷路了,请返回首页重新探索!',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const errorContent = computed(() => errorMessages[errorCode] || errorMessages['default']);
|
if (import.meta.server)
|
||||||
|
throw createError({
|
||||||
|
statusCode: errorCode,
|
||||||
|
})
|
||||||
|
else
|
||||||
|
showError({statusCode: errorCode})
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
emitter.emit("startLoading", false)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<alert :title="errorContent.title" :message="errorContent.message" :icon="errorContent.icon"/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed, onMounted, ref} from "vue";
|
import {computed, onMounted, ref} from "vue";
|
||||||
import {getBlogRecentPost} from "~/api/blog";
|
|
||||||
import {getActivity} from "~/api/git"
|
|
||||||
|
|
||||||
const recentPosts = ref<IPost[] | null>(null);
|
const recentPosts = ref<IPost[] | null>(null);
|
||||||
const recentActivities = ref<IActivity[] | null>(null);
|
const recentActivities = ref<IActivity[] | null>(null);
|
||||||
@ -14,6 +12,17 @@ const isError = ref({
|
|||||||
git: false
|
git: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const getBlogRecentPost = get<IBlogResponse<IPostsData>, any>('/blog/index.php/api/posts')
|
||||||
|
const getActivity = get<IActivity[], any>('/git/api/v1/users/cantyonion/activities/feeds', {
|
||||||
|
headers: {
|
||||||
|
Authorization: ` ${config.public.gitApiKey}`
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
limit: 10,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const hasPosts = computed(() => (recentPosts.value ?? []).length > 0);
|
const hasPosts = computed(() => (recentPosts.value ?? []).length > 0);
|
||||||
const hasActivities = computed(() => (recentActivities.value ?? []).length > 0);
|
const hasActivities = computed(() => (recentActivities.value ?? []).length > 0);
|
||||||
const postsData = computed(() => {
|
const postsData = computed(() => {
|
||||||
@ -41,30 +50,33 @@ const activitiesData = computed((): IActivity<IContent>[] => {
|
|||||||
const reloadPosts = async () => {
|
const reloadPosts = async () => {
|
||||||
try {
|
try {
|
||||||
isLoading.value.blog = true
|
isLoading.value.blog = true
|
||||||
const postData = await getBlogRecentPost();
|
const postData = await getBlogRecentPost()
|
||||||
recentPosts.value = postData.data.dataSet
|
recentPosts.value = postData!.data.dataSet
|
||||||
isLoading.value.blog = false
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
isLoading.value.blog = false
|
|
||||||
isError.value.blog = true
|
isError.value.blog = true
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
isLoading.value.blog = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reloadActivities = async () => {
|
const reloadActivities = async () => {
|
||||||
try {
|
try {
|
||||||
isLoading.value.git = true
|
isLoading.value.git = true
|
||||||
recentActivities.value = await getActivity()
|
recentActivities.value = await getActivity()
|
||||||
isLoading.value.git = false
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
isLoading.value.git = false
|
|
||||||
isError.value.git = true
|
isError.value.git = true
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
isLoading.value.git = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
await reloadPosts()
|
||||||
await reloadPosts()
|
await reloadActivities()
|
||||||
await reloadActivities()
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
emitter.emit('startLoading', false)
|
emitter.emit('startLoading', false)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
5
types/notification.ts
Normal file
5
types/notification.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
declare interface INotification {
|
||||||
|
level: 'warning' | 'error' | 'info' | 'success'
|
||||||
|
title: string
|
||||||
|
message: string
|
||||||
|
}
|
@ -3319,6 +3319,11 @@ make-dir@^3.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver "^6.0.0"
|
semver "^6.0.0"
|
||||||
|
|
||||||
|
mathjax@^3.2.2:
|
||||||
|
version "3.2.2"
|
||||||
|
resolved "https://registry.npmmirror.com/mathjax/-/mathjax-3.2.2.tgz#c754d7b46a679d7f3fa03543d6b8bf124ddf9f6b"
|
||||||
|
integrity sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw==
|
||||||
|
|
||||||
mdn-data@2.0.28:
|
mdn-data@2.0.28:
|
||||||
version "2.0.28"
|
version "2.0.28"
|
||||||
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
|
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user