refactor(fe2): Use webflow api for dashboard stories (#3342)

* Initial work

* fallback image

* Update api name

* Add read time

* Filter out newly updated stories

* Update webflow.ts

* Add NuxtImg

* Update webflow.ts

* Update Card.vue

* Rename to Webflow Items

* Helm changes

* Rename webflow to blog

* useAsyncData

* Throw error if no API
This commit is contained in:
andrewwallacespeckle
2024-10-22 14:58:31 +01:00
committed by GitHub
parent d48b935722
commit 083531e5c9
13 changed files with 609 additions and 99 deletions
+2
View File
@@ -42,3 +42,5 @@ NUXT_PUBLIC_ENABLE_DIRECT_PREVIEWS=true
# Ghost API
NUXT_PUBLIC_GHOST_API_KEY=
NUXT_WEBFLOW_API_TOKEN=8c9bea4c120742a21ff308cda0bea73f13e89ffe26dcc886990bba353549b652
@@ -0,0 +1,49 @@
<template>
<NuxtLink :to="webflowItem.url" target="_blank">
<div
class="bg-foundation border border-outline-3 rounded-xl flex flex-col overflow-hidden hover:border-outline-5 transition"
>
<NuxtImg
v-if="webflowItem.featureImageUrl"
:src="webflowItem.featureImageUrl"
:alt="webflowItem.title"
class="h-32 w-full object-cover"
width="400"
height="225"
/>
<div
v-else
class="bg-foundation-page w-full h-32 flex items-center justify-center"
>
<HeaderLogoBlock no-link minimal class="scale-150" />
</div>
<div class="p-5 pb-4">
<h3 class="text-body-2xs text-foreground truncate">
{{ webflowItem.title }}
</h3>
<p class="text-body-3xs text-foreground-2 mt-2">
<span v-tippy="createdOn.full">
{{ createdOn.relative }}
</span>
<template v-if="webflowItem.readTime">
<span class="pl-1 pr-2"></span>
{{ webflowItem.readTime }}m read
</template>
</p>
</div>
</div>
</NuxtLink>
</template>
<script lang="ts" setup>
import type { WebflowItem } from '~/lib/dashboard/helpers/types'
const props = defineProps<{
webflowItem: WebflowItem
}>()
const createdOn = computed(() => ({
full: formattedFullDate(props.webflowItem.createdOn),
relative: formattedRelativeDate(props.webflowItem.createdOn, { capitalize: true })
}))
</script>
@@ -0,0 +1,31 @@
<template>
<section v-if="!error">
<h2 class="text-heading-sm text-foreground-2 mb-4">Blog</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
<DashboardBlogCard
v-for="webflowItem in webflowItems"
:key="webflowItem.id"
:webflow-item="webflowItem"
/>
</div>
</section>
<section v-else />
</template>
<script setup lang="ts">
import type { WebflowItem } from '~/lib/dashboard/helpers/types'
const logger = useLogger()
const { data: webflowData, error } = await useAsyncData<{
items: WebflowItem[]
}>('webflow-items', () =>
$fetch('/api/webflow', {
onResponseError({ response }) {
logger.error('API Response Error:', response.status, response.statusText)
}
})
)
const webflowItems = computed(() => webflowData.value?.items || [])
</script>
@@ -1,41 +0,0 @@
<template>
<NuxtLink :to="tutorial.url" target="_blank">
<div
class="bg-foundation border border-outline-3 rounded-xl flex flex-col overflow-hidden hover:border-outline-5 transition"
>
<div
:style="{ backgroundImage: `url(${tutorial.featureImage})` }"
class="bg-foundation-page bg-cover bg-center w-full h-32"
/>
<div class="p-5 pb-4">
<h3 v-if="tutorial.title" class="text-body-2xs text-foreground truncate">
{{ tutorial.title }}
</h3>
<p class="text-body-3xs text-foreground-2 mt-2">
<span v-tippy="updatedAt.full">
{{ updatedAt.relative }}
</span>
<template v-if="tutorial.readingTime">
<span class="pl-1 pr-2"></span>
{{ tutorial.readingTime }}m read
</template>
</p>
</div>
</div>
</NuxtLink>
</template>
<script lang="ts" setup>
import type { TutorialItem } from '~~/lib/dashboard/helpers/types'
const props = defineProps<{
tutorial: TutorialItem
}>()
const updatedAt = computed(() => {
return {
full: formattedFullDate(props.tutorial.publishedAt),
relative: formattedRelativeDate(props.tutorial.publishedAt, { capitalize: true })
}
})
</script>
@@ -1,13 +1,13 @@
import type { Nullable } from '@speckle/shared'
import { type LayoutDialogButton } from '@speckle/ui-components'
export type TutorialItem = {
export type WebflowItem = {
id: string
readingTime?: number
publishedAt?: Nullable<string>
url?: string
title?: string
featureImage?: Nullable<string>
title: string
createdOn: string
lastPublished: string
featureImageUrl?: string
url: string
readTime?: number
}
export type QuickStartItem = {
+2
View File
@@ -33,6 +33,7 @@ export default defineNuxtConfig({
modules: [
'@nuxt/eslint',
'@nuxt/devtools',
'@nuxt/image',
'@nuxtjs/tailwindcss',
[
'~/lib/core/nuxt-modules/apollo/module.ts',
@@ -47,6 +48,7 @@ export default defineNuxtConfig({
],
runtimeConfig: {
redisUrl: '',
webflowApiToken: '',
public: {
...featureFlags,
apiOrigin: 'UNDEFINED',
+1
View File
@@ -96,6 +96,7 @@
"@graphql-codegen/visitor-plugin-common": "5.3.1",
"@nuxt/devtools": "^1.3.9",
"@nuxt/eslint": "^0.3.13",
"@nuxt/image": "^1.8.1",
"@nuxtjs/tailwindcss": "^6.3.0",
"@parcel/watcher": "^2.4.1",
"@speckle/tailwind-theme": "workspace:^",
+1 -41
View File
@@ -53,16 +53,7 @@
</div>
</section>
</div>
<section>
<h2 class="text-heading-sm text-foreground-2">News &amp; tutorials</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 pt-5">
<DashboardTutorialCard
v-for="tutorial in tutorials"
:key="tutorial.id"
:tutorial="tutorial"
/>
</div>
</section>
<DashboardBlogWrapper />
</div>
<ProjectsAddDialog v-model:open="openNewProject" />
@@ -74,10 +65,8 @@ import {
dashboardProjectsPageWorkspacesQuery
} from '~~/lib/dashboard/graphql/queries'
import type { QuickStartItem } from '~~/lib/dashboard/helpers/types'
import { getResizedGhostImage } from '~~/lib/dashboard/helpers/utils'
import { useQuery } from '@vue/apollo-composable'
import { useMixpanel } from '~~/lib/core/composables/mp'
import GhostContentAPI from '@tryghost/content-api'
import {
docsPageUrl,
forumPageUrl,
@@ -97,7 +86,6 @@ definePageMeta({
alias: ['/profile', '/dashboard']
})
const config = useRuntimeConfig()
const mixpanel = useMixpanel()
const isWorkspacesEnabled = useIsWorkspacesEnabled()
const { result: projectsResult } = useQuery(dashboardProjectsPageQuery)
@@ -109,20 +97,11 @@ const { result: workspacesResult } = useQuery(
})
)
const { triggerNotification } = useGlobalToast()
const { data: tutorials } = await useLazyAsyncData('tutorials', fetchTutorials, {
server: false
})
const { isGuest } = useActiveUser()
const router = useRouter()
const openNewProject = ref(false)
const ghostContentApi = new GhostContentAPI({
url: 'https://v1.speckle.systems',
key: config.public.ghostApiKey,
version: 'v5.0'
})
const quickStartItems = shallowRef<QuickStartItem[]>([
{
title: 'Install Speckle manager',
@@ -171,25 +150,6 @@ const createProjectButton = shallowRef<LayoutDialogButton[]>([
const projects = computed(() => projectsResult.value?.activeUser?.projects.items)
const hasProjects = computed(() => (projects.value ? projects.value.length > 0 : false))
async function fetchTutorials() {
const posts = await ghostContentApi.posts.browse({
limit: 8,
filter: 'visibility:public'
})
return posts
.filter((post) => post.url)
.map((post) => ({
id: post.id,
readingTime: post.reading_time,
publishedAt: post.published_at,
// Temporary replacement until we swap to WebFlow API
url: post.url?.replace('https://v1.speckle.systems', 'https://speckle.systems'),
title: post.title,
featureImage: getResizedGhostImage({ url: post.feature_image, width: 600 })
}))
}
const onDownloadManager = (extension: ManagerExtension) => {
try {
downloadManager(extension)
+97
View File
@@ -0,0 +1,97 @@
import { ensureError } from '@speckle/shared'
import type { WebflowItem } from '~/lib/dashboard/helpers/types'
type WebflowApiResponse = {
items: Array<{
id: string
lastPublished: string
createdOn: string
fieldData: {
name: string
slug: string
'feature-image'?: {
url: string
}
html?: string
}
}>
}
const calculateReadTime = (content: string): number => {
const wordsPerMinute = 280
const wordCount = content.trim().split(/\s+/).length
return Math.ceil(wordCount / wordsPerMinute)
}
// Used to filter to last 6 months' articles to prevent old,
// recently edited posts from appearing at the top
const getSixMonthsAgo = (): Date => {
const date = new Date()
date.setMonth(date.getMonth() - 6)
return date
}
export default defineEventHandler(async (): Promise<{ items: WebflowItem[] }> => {
const { webflowApiToken } = useRuntimeConfig()
if (!webflowApiToken) {
throw createError({
statusCode: 500,
fatal: true,
message: 'Webflow API token is not set'
})
}
const url =
'https://api.webflow.com/v2/collections/66d822d3199be6f73a6c3c2c/items?limit=16&sortBy=lastPublished&sortOrder=desc'
try {
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${webflowApiToken}`,
'accept-version': '2.0.0'
}
})
if (!response.ok) {
const errMsg = `Webflow API Error: ${response.status} ${response.statusText}`
throw createError({
statusCode: response.status,
fatal: true,
message: errMsg
})
}
const data = (await response.json()) as WebflowApiResponse
const sixMonthsAgo = getSixMonthsAgo()
const filteredItems = data.items
.filter((item) => new Date(item.createdOn) > sixMonthsAgo)
.sort((a, b) => new Date(b.createdOn).getTime() - new Date(a.createdOn).getTime())
.slice(0, 8) // Take only the first 8 items after filtering and sorting
return {
items: filteredItems.map(
(item): WebflowItem => ({
id: item.id,
title: item.fieldData.name,
createdOn: item.createdOn,
lastPublished: item.lastPublished,
featureImageUrl: item.fieldData['feature-image']?.url,
url: `https://speckle.systems/blog/${item.fieldData.slug}`,
readTime: item.fieldData.html
? calculateReadTime(item.fieldData.html)
: undefined
})
)
}
} catch (e) {
const errMsg = ensureError(e).message
throw createError({
statusCode: 500,
fatal: true,
message: `Error fetching webflow items: ${errMsg}`
})
}
})
@@ -91,9 +91,9 @@ spec:
secretKeyRef:
name: {{ default .Values.secretName .Values.redis.connectionString.secretName }}
key: {{ default "redis_url" .Values.redis.connectionString.secretKey }}
{{- if .Values.frontend_2.ghostApiKey }}
- name: NUXT_PUBLIC_GHOST_API_KEY
value: {{ .Values.frontend_2.ghostApiKey | quote }}
{{- if .Values.frontend_2.webflowApiKey }}
- name: NUXT_PUBLIC_WEBFLOW_API_KEY
value: {{ .Values.frontend_2.webflowApiKey | quote }}
{{- end }}
{{- if .Values.analytics.datadog_app_id }}
- name: NUXT_PUBLIC_DATADOG_APP_ID
+2 -2
View File
@@ -1684,9 +1684,9 @@
"description": "The Docker image to be used for the Speckle Frontend 2 component. If blank, defaults to speckle/speckle-frontend-2:{{ .Values.docker_image_tag }}. If provided, this value should be the full path including tag. The docker_image_tag value will be ignored.",
"default": ""
},
"ghostApiKey": {
"webflowApiKey": {
"type": "string",
"description": "API Key for Ghost, which provides the blog content for the new web application frontend.",
"description": "API Key for Webflow, which provides the blog content for the new web application frontend.",
"default": ""
},
"logClientApiToken": {
+2 -2
View File
@@ -1009,9 +1009,9 @@ frontend_2:
## @param frontend_2.image The Docker image to be used for the Speckle Frontend 2 component. If blank, defaults to speckle/speckle-frontend-2:{{ .Values.docker_image_tag }}. If provided, this value should be the full path including tag. The docker_image_tag value will be ignored.
##
image: ''
## @param frontend_2.ghostApiKey API Key for Ghost, which provides the blog content for the new web application frontend.
## @param frontend_2.webflowApiKey API Key for Webflow, which provides the blog content for the new web application frontend.
##
ghostApiKey: ''
webflowApiKey: ''
## @param frontend_2.logClientApiToken SEQ API token
##
logClientApiToken: ''
+412 -3
View File
@@ -10597,6 +10597,13 @@ __metadata:
languageName: node
linkType: hard
"@fastify/accept-negotiator@npm:^1.1.0":
version: 1.1.0
resolution: "@fastify/accept-negotiator@npm:1.1.0"
checksum: 10/5c8f263680af0aece8c1fdea4d4c094a7f82cc5ed90b709357eb52a01e3388d1ac74a17e5a1d5d53f2d3ca93ae50d283ee451a6435b2cbe1b9847fff4d7d0732
languageName: node
linkType: hard
"@fastify/busboy@npm:^2.0.0":
version: 2.1.1
resolution: "@fastify/busboy@npm:2.1.1"
@@ -12817,6 +12824,28 @@ __metadata:
languageName: node
linkType: hard
"@nuxt/image@npm:^1.8.1":
version: 1.8.1
resolution: "@nuxt/image@npm:1.8.1"
dependencies:
"@nuxt/kit": "npm:^3.13.2"
consola: "npm:^3.2.3"
defu: "npm:^6.1.4"
h3: "npm:^1.12.0"
image-meta: "npm:^0.2.1"
ipx: "npm:^2.1.0"
node-fetch-native: "npm:^1.6.4"
ohash: "npm:^1.1.4"
pathe: "npm:^1.1.2"
std-env: "npm:^3.7.0"
ufo: "npm:^1.5.4"
dependenciesMeta:
ipx:
optional: true
checksum: 10/63efc91906870d00403a07999f5d6ab588bef68f18f7dcca30ebb7b68ad91bfdb8fb42a8af358280653a4f0c042dd9a45f348da827b98134012b44bd668817bc
languageName: node
linkType: hard
"@nuxt/kit@npm:3.12.2":
version: 3.12.2
resolution: "@nuxt/kit@npm:3.12.2"
@@ -12950,6 +12979,34 @@ __metadata:
languageName: node
linkType: hard
"@nuxt/kit@npm:^3.13.2":
version: 3.13.2
resolution: "@nuxt/kit@npm:3.13.2"
dependencies:
"@nuxt/schema": "npm:3.13.2"
c12: "npm:^1.11.2"
consola: "npm:^3.2.3"
defu: "npm:^6.1.4"
destr: "npm:^2.0.3"
globby: "npm:^14.0.2"
hash-sum: "npm:^2.0.0"
ignore: "npm:^5.3.2"
jiti: "npm:^1.21.6"
klona: "npm:^2.0.6"
knitwork: "npm:^1.1.0"
mlly: "npm:^1.7.1"
pathe: "npm:^1.1.2"
pkg-types: "npm:^1.2.0"
scule: "npm:^1.3.0"
semver: "npm:^7.6.3"
ufo: "npm:^1.5.4"
unctx: "npm:^2.3.1"
unimport: "npm:^3.12.0"
untyped: "npm:^1.4.2"
checksum: 10/0c8a67cb1b42841e0f7a297eefc703398674e220565b5aa56056646b1aa29f08482ff8db2e778f85f2e4059d1c9a6fb97a204a70904dd8ce66aaa0a70d403c98
languageName: node
linkType: hard
"@nuxt/kit@npm:^3.4.3":
version: 3.5.0
resolution: "@nuxt/kit@npm:3.5.0"
@@ -13149,6 +13206,26 @@ __metadata:
languageName: node
linkType: hard
"@nuxt/schema@npm:3.13.2":
version: 3.13.2
resolution: "@nuxt/schema@npm:3.13.2"
dependencies:
compatx: "npm:^0.1.8"
consola: "npm:^3.2.3"
defu: "npm:^6.1.4"
hookable: "npm:^5.5.3"
pathe: "npm:^1.1.2"
pkg-types: "npm:^1.2.0"
scule: "npm:^1.3.0"
std-env: "npm:^3.7.0"
ufo: "npm:^1.5.4"
uncrypto: "npm:^0.1.3"
unimport: "npm:^3.12.0"
untyped: "npm:^1.4.2"
checksum: 10/698520eb9095f7ab40f9308a9f8153578f506cf6b27635493a04e0e363d7f9d43e2174f718adc8fc948205d5300e0b1d1097f044d0615515c9374455f192a3ab
languageName: node
linkType: hard
"@nuxt/schema@npm:3.5.0":
version: 3.5.0
resolution: "@nuxt/schema@npm:3.5.0"
@@ -14585,6 +14662,22 @@ __metadata:
languageName: node
linkType: hard
"@rollup/pluginutils@npm:^5.1.2":
version: 5.1.2
resolution: "@rollup/pluginutils@npm:5.1.2"
dependencies:
"@types/estree": "npm:^1.0.0"
estree-walker: "npm:^2.0.2"
picomatch: "npm:^2.3.1"
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
checksum: 10/cc1fe3285ab48915a6535ab2f0c90dc511bd3e63143f8e9994bb036c6c5071fd14d641cff6c89a7fde6a4faa85227d4e2cf46ee36b7d962099e0b9e4c9b8a4b0
languageName: node
linkType: hard
"@rollup/rollup-android-arm-eabi@npm:4.13.0":
version: 4.13.0
resolution: "@rollup/rollup-android-arm-eabi@npm:4.13.0"
@@ -16267,6 +16360,7 @@ __metadata:
"@jsonforms/vue-vanilla": "npm:^3.3.0"
"@nuxt/devtools": "npm:^1.3.9"
"@nuxt/eslint": "npm:^0.3.13"
"@nuxt/image": "npm:^1.8.1"
"@nuxtjs/tailwindcss": "npm:^6.3.0"
"@parcel/watcher": "npm:^2.4.1"
"@speckle/shared": "workspace:^"
@@ -25644,6 +25738,31 @@ __metadata:
languageName: node
linkType: hard
"c12@npm:^1.11.2":
version: 1.11.2
resolution: "c12@npm:1.11.2"
dependencies:
chokidar: "npm:^3.6.0"
confbox: "npm:^0.1.7"
defu: "npm:^6.1.4"
dotenv: "npm:^16.4.5"
giget: "npm:^1.2.3"
jiti: "npm:^1.21.6"
mlly: "npm:^1.7.1"
ohash: "npm:^1.1.3"
pathe: "npm:^1.1.2"
perfect-debounce: "npm:^1.0.0"
pkg-types: "npm:^1.2.0"
rc9: "npm:^2.1.2"
peerDependencies:
magicast: ^0.3.4
peerDependenciesMeta:
magicast:
optional: true
checksum: 10/ba568cac969692a3324135e6d292e2e7d414a0394753c3afdefcb1fb799cd26dda05f8258e58b22c95253db7ec0cd29788780d291b6c6f60ddf88d5ba414d325
languageName: node
linkType: hard
"c12@npm:^1.4.1":
version: 1.4.1
resolution: "c12@npm:1.4.1"
@@ -26754,7 +26873,7 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^2.19.0, commander@npm:^2.20.0":
"commander@npm:^2.19.0, commander@npm:^2.20.0, commander@npm:^2.20.3":
version: 2.20.3
resolution: "commander@npm:2.20.3"
checksum: 10/90c5b6898610cd075984c58c4f88418a4fb44af08c1b1415e9854c03171bec31b336b7f3e4cefe33de994b3f12b03c5e2d638da4316df83593b9e82554e7e95b
@@ -27045,6 +27164,13 @@ __metadata:
languageName: node
linkType: hard
"confbox@npm:^0.1.8":
version: 0.1.8
resolution: "confbox@npm:0.1.8"
checksum: 10/4ebcfb1c6a3b25276734ec5722e88768eb61fc02f98e11960b845c5c62bc27fd05f493d2a8244d9675b24ef95afe4c0d511cdcad02c72f5eeea463cc26687999
languageName: node
linkType: hard
"config-chain@npm:^1.1.13":
version: 1.1.13
resolution: "config-chain@npm:1.1.13"
@@ -27194,6 +27320,13 @@ __metadata:
languageName: node
linkType: hard
"cookie-es@npm:^1.2.2":
version: 1.2.2
resolution: "cookie-es@npm:1.2.2"
checksum: 10/0fd742c11caa185928e450543f84df62d4b2c1fc7b5041196b57b7db04e1c6ac6585fb40e4f579a2819efefd2d6a9cbb4d17f71240d05f4dcd8f74ae81341a20
languageName: node
linkType: hard
"cookie-parser@npm:^1.4.6, cookie-parser@npm:~1.4.4":
version: 1.4.6
resolution: "cookie-parser@npm:1.4.6"
@@ -27598,6 +27731,15 @@ __metadata:
languageName: node
linkType: hard
"crossws@npm:>=0.2.0 <0.4.0":
version: 0.3.1
resolution: "crossws@npm:0.3.1"
dependencies:
uncrypto: "npm:^0.1.3"
checksum: 10/d358a58b364b3314a0e42ee66b1432c01d416128e53eda983eb121abdad5ff39831a1f1ea3e90e80157ceaa0fc925f5193c151b156aa62af9e0c9bcb2fb2a15a
languageName: node
linkType: hard
"crossws@npm:^0.2.0, crossws@npm:^0.2.2, crossws@npm:^0.2.4":
version: 0.2.4
resolution: "crossws@npm:0.2.4"
@@ -27791,6 +27933,13 @@ __metadata:
languageName: node
linkType: hard
"cssfilter@npm:0.0.10":
version: 0.0.10
resolution: "cssfilter@npm:0.0.10"
checksum: 10/1e45182f42de848f092f50a313113c28a88e4ac98333bf1603ee1c3b200384a3bc83c12e35cd61135e3b0f218295f600d51120ca1f926b7958b2d3262d711214
languageName: node
linkType: hard
"cssnano-preset-default@npm:^6.0.1":
version: 6.0.1
resolution: "cssnano-preset-default@npm:6.0.1"
@@ -28833,7 +28982,7 @@ __metadata:
languageName: node
linkType: hard
"destr@npm:^2.0.3":
"destr@npm:^2.0.2, destr@npm:^2.0.3":
version: 2.0.3
resolution: "destr@npm:2.0.3"
checksum: 10/dbb756baa876810ec0ca4bcb702d86cc3b480ed14f36bf5747718ed211f96bca5520b63a4109eb181ad940ee2a645677d9a63d4a0ed11a7510619dae97317201
@@ -33251,6 +33400,24 @@ __metadata:
languageName: node
linkType: hard
"h3@npm:^1.10.0, h3@npm:^1.12.0":
version: 1.13.0
resolution: "h3@npm:1.13.0"
dependencies:
cookie-es: "npm:^1.2.2"
crossws: "npm:>=0.2.0 <0.4.0"
defu: "npm:^6.1.4"
destr: "npm:^2.0.3"
iron-webcrypto: "npm:^1.2.1"
ohash: "npm:^1.1.4"
radix3: "npm:^1.1.2"
ufo: "npm:^1.5.4"
uncrypto: "npm:^0.1.3"
unenv: "npm:^1.10.0"
checksum: 10/ecdbe3cdddc767ea6f9be9939b14192dd296eb434641bbecc5b665f7210de8c03910ae40931668788395b5de6cd517afaa628d7b5ce0fb60786fce1ad6e81bcb
languageName: node
linkType: hard
"h3@npm:^1.10.2, h3@npm:^1.11.1":
version: 1.11.1
resolution: "h3@npm:1.11.1"
@@ -34173,6 +34340,13 @@ __metadata:
languageName: node
linkType: hard
"ignore@npm:^5.3.2":
version: 5.3.2
resolution: "ignore@npm:5.3.2"
checksum: 10/cceb6a457000f8f6a50e1196429750d782afce5680dd878aa4221bd79972d68b3a55b4b1458fc682be978f4d3c6a249046aa0880637367216444ab7b014cfc98
languageName: node
linkType: hard
"image-meta@npm:^0.2.0":
version: 0.2.0
resolution: "image-meta@npm:0.2.0"
@@ -34180,6 +34354,13 @@ __metadata:
languageName: node
linkType: hard
"image-meta@npm:^0.2.1":
version: 0.2.1
resolution: "image-meta@npm:0.2.1"
checksum: 10/b8cb692727aaf47821b845714cbae5b2423a34802355bfcd815ad11610c051c10bd5b70b1634b2a5b7eb807801479983930f000e192eb956ef79c0660e7589a0
languageName: node
linkType: hard
"immediate@npm:~3.0.5":
version: 3.0.6
resolution: "immediate@npm:3.0.6"
@@ -34506,6 +34687,32 @@ __metadata:
languageName: node
linkType: hard
"ipx@npm:^2.1.0":
version: 2.1.0
resolution: "ipx@npm:2.1.0"
dependencies:
"@fastify/accept-negotiator": "npm:^1.1.0"
citty: "npm:^0.1.5"
consola: "npm:^3.2.3"
defu: "npm:^6.1.4"
destr: "npm:^2.0.2"
etag: "npm:^1.8.1"
h3: "npm:^1.10.0"
image-meta: "npm:^0.2.0"
listhen: "npm:^1.5.6"
ofetch: "npm:^1.3.3"
pathe: "npm:^1.1.2"
sharp: "npm:^0.32.6"
svgo: "npm:^3.2.0"
ufo: "npm:^1.3.2"
unstorage: "npm:^1.10.1"
xss: "npm:^1.0.14"
bin:
ipx: bin/ipx.mjs
checksum: 10/e91e5bb702fabf9f7e3bf866028e1b356be1f1d425bb2544d0ad60b6d8925c866259fb717a7d2ab4e805d338fc352ddcfb834582f50b1974428719c754660259
languageName: node
linkType: hard
"iron-webcrypto@npm:^0.7.0":
version: 0.7.0
resolution: "iron-webcrypto@npm:0.7.0"
@@ -34520,6 +34727,13 @@ __metadata:
languageName: node
linkType: hard
"iron-webcrypto@npm:^1.2.1":
version: 1.2.1
resolution: "iron-webcrypto@npm:1.2.1"
checksum: 10/c1f52ccfe2780efa5438c134538ee4b26c96a87d22f351d896781219efbce25b4fe716d1cb7f248e02da96881760541135acbcc7c0622ffedf71cb0e227bebf9
languageName: node
linkType: hard
"is-absolute-url@npm:^4.0.0":
version: 4.0.1
resolution: "is-absolute-url@npm:4.0.1"
@@ -36168,6 +36382,15 @@ __metadata:
languageName: node
linkType: hard
"jiti@npm:^2.1.2":
version: 2.3.3
resolution: "jiti@npm:2.3.3"
bin:
jiti: lib/jiti-cli.mjs
checksum: 10/21d1e89d909101c702769537e75ad87b433f980bc6938a6f64a90d1fbe7cb1510a0d4b82d4020b3093b47cebff5568af5e39d883e26aa4413564ced43b8cfd84
languageName: node
linkType: hard
"jju@npm:~1.4.0":
version: 1.4.0
resolution: "jju@npm:1.4.0"
@@ -37224,6 +37447,35 @@ __metadata:
languageName: node
linkType: hard
"listhen@npm:^1.5.6":
version: 1.9.0
resolution: "listhen@npm:1.9.0"
dependencies:
"@parcel/watcher": "npm:^2.4.1"
"@parcel/watcher-wasm": "npm:^2.4.1"
citty: "npm:^0.1.6"
clipboardy: "npm:^4.0.0"
consola: "npm:^3.2.3"
crossws: "npm:>=0.2.0 <0.4.0"
defu: "npm:^6.1.4"
get-port-please: "npm:^3.1.2"
h3: "npm:^1.12.0"
http-shutdown: "npm:^1.2.2"
jiti: "npm:^2.1.2"
mlly: "npm:^1.7.1"
node-forge: "npm:^1.3.1"
pathe: "npm:^1.1.2"
std-env: "npm:^3.7.0"
ufo: "npm:^1.5.4"
untun: "npm:^0.1.3"
uqr: "npm:^0.1.2"
bin:
listen: bin/listhen.mjs
listhen: bin/listhen.mjs
checksum: 10/72b869c8604301352c5d5825a7737705f0df2ce1795af8e779b6f956ba71302e13b12b2d35142687fb4e1e8ccc2747e2be3c9cbf20f7f96b73f897881aa3c384
languageName: node
linkType: hard
"listhen@npm:^1.7.2":
version: 1.7.2
resolution: "listhen@npm:1.7.2"
@@ -37830,6 +38082,13 @@ __metadata:
languageName: node
linkType: hard
"lru-cache@npm:^10.4.3":
version: 10.4.3
resolution: "lru-cache@npm:10.4.3"
checksum: 10/e6e90267360476720fa8e83cc168aa2bf0311f3f2eea20a6ba78b90a885ae72071d9db132f40fda4129c803e7dcec3a6b6a6fbb44ca90b081630b810b5d6a41a
languageName: node
linkType: hard
"lru-cache@npm:^4.1.5":
version: 4.1.5
resolution: "lru-cache@npm:4.1.5"
@@ -39756,6 +40015,18 @@ __metadata:
languageName: node
linkType: hard
"mlly@npm:^1.7.2":
version: 1.7.2
resolution: "mlly@npm:1.7.2"
dependencies:
acorn: "npm:^8.12.1"
pathe: "npm:^1.1.2"
pkg-types: "npm:^1.2.0"
ufo: "npm:^1.5.4"
checksum: 10/c28e9f32cfc7b204e4d089a9af01b6af30547f39dd97244486fe208523c1453828b694430ebfa2d06297116861d464f150d3273040bf5e11ef5a357958f142d5
languageName: node
linkType: hard
"mocha-junit-reporter@npm:^2.0.2":
version: 2.0.2
resolution: "mocha-junit-reporter@npm:2.0.2"
@@ -41355,6 +41626,13 @@ __metadata:
languageName: node
linkType: hard
"ohash@npm:^1.1.4":
version: 1.1.4
resolution: "ohash@npm:1.1.4"
checksum: 10/b11445234e59c9c2b00f357f8f00b6ba00e14c84fc0a232cdc14eb1d80066479b09d27af0201631e84b7a15ba7c4a1939f4cc47f2030e9bf83c9e8afc3ff7dfd
languageName: node
linkType: hard
"oidc-token-hash@npm:^5.0.3":
version: 5.0.3
resolution: "oidc-token-hash@npm:5.0.3"
@@ -42767,6 +43045,17 @@ __metadata:
languageName: node
linkType: hard
"pkg-types@npm:^1.2.0":
version: 1.2.1
resolution: "pkg-types@npm:1.2.1"
dependencies:
confbox: "npm:^0.1.8"
mlly: "npm:^1.7.2"
pathe: "npm:^1.1.2"
checksum: 10/d61f4b7a2351b55b22f1d08f5f9b4236928d5660886131cc0e11576362e2b3bfcb54084bb4a0ba79147b707a27dcae87444a86e731113e152ffd3b6155ce5a5a
languageName: node
linkType: hard
"playwright-core@npm:1.27.1, playwright-core@npm:>=1.2.0":
version: 1.27.1
resolution: "playwright-core@npm:1.27.1"
@@ -48785,7 +49074,7 @@ __metadata:
languageName: node
linkType: hard
"svgo@npm:^3.3.2":
"svgo@npm:^3.2.0, svgo@npm:^3.3.2":
version: 3.3.2
resolution: "svgo@npm:3.3.2"
dependencies:
@@ -50303,6 +50592,19 @@ __metadata:
languageName: node
linkType: hard
"unenv@npm:^1.10.0":
version: 1.10.0
resolution: "unenv@npm:1.10.0"
dependencies:
consola: "npm:^3.2.3"
defu: "npm:^6.1.4"
mime: "npm:^3.0.0"
node-fetch-native: "npm:^1.6.4"
pathe: "npm:^1.1.2"
checksum: 10/23198e150fd3b4db4d7abe444b75ee05a0d36768bd6d94a6aaf5dca830db82e707ccc0f6cca22671327b62c5cd85ada08d4665bf7652afec9de0bdc7a4546249
languageName: node
linkType: hard
"unenv@npm:^1.5.1":
version: 1.5.2
resolution: "unenv@npm:1.5.2"
@@ -50455,6 +50757,27 @@ __metadata:
languageName: node
linkType: hard
"unimport@npm:^3.12.0":
version: 3.13.1
resolution: "unimport@npm:3.13.1"
dependencies:
"@rollup/pluginutils": "npm:^5.1.2"
acorn: "npm:^8.12.1"
escape-string-regexp: "npm:^5.0.0"
estree-walker: "npm:^3.0.3"
fast-glob: "npm:^3.3.2"
local-pkg: "npm:^0.5.0"
magic-string: "npm:^0.30.11"
mlly: "npm:^1.7.1"
pathe: "npm:^1.1.2"
pkg-types: "npm:^1.2.0"
scule: "npm:^1.3.0"
strip-literal: "npm:^2.1.0"
unplugin: "npm:^1.14.1"
checksum: 10/e8ec260915ebec7641f4bf545a6f272188235d1fe25876646465c954b163ac661b769918fac7a4351f2ae3170aae9e34a8739a270c2ad284461d399cb8413e0e
languageName: node
linkType: hard
"unimport@npm:^3.5.0":
version: 3.6.0
resolution: "unimport@npm:3.6.0"
@@ -50867,6 +51190,21 @@ __metadata:
languageName: node
linkType: hard
"unplugin@npm:^1.14.1":
version: 1.14.1
resolution: "unplugin@npm:1.14.1"
dependencies:
acorn: "npm:^8.12.1"
webpack-virtual-modules: "npm:^0.6.2"
peerDependencies:
webpack-sources: ^3
peerDependenciesMeta:
webpack-sources:
optional: true
checksum: 10/ad82ec5b8de5ae4fb7d24f8ed7d71071e15855d335365d7ab6f2e074d5d666589dd52e9f2a16017da19d7c43f60e50e09bc529420bf9f29ac7c90cc3cf13ef28
languageName: node
linkType: hard
"unplugin@npm:^1.3.1":
version: 1.3.1
resolution: "unplugin@npm:1.3.1"
@@ -50903,6 +51241,65 @@ __metadata:
languageName: node
linkType: hard
"unstorage@npm:^1.10.1":
version: 1.12.0
resolution: "unstorage@npm:1.12.0"
dependencies:
anymatch: "npm:^3.1.3"
chokidar: "npm:^3.6.0"
destr: "npm:^2.0.3"
h3: "npm:^1.12.0"
listhen: "npm:^1.7.2"
lru-cache: "npm:^10.4.3"
mri: "npm:^1.2.0"
node-fetch-native: "npm:^1.6.4"
ofetch: "npm:^1.3.4"
ufo: "npm:^1.5.4"
peerDependencies:
"@azure/app-configuration": ^1.7.0
"@azure/cosmos": ^4.1.1
"@azure/data-tables": ^13.2.2
"@azure/identity": ^4.4.1
"@azure/keyvault-secrets": ^4.8.0
"@azure/storage-blob": ^12.24.0
"@capacitor/preferences": ^6.0.2
"@netlify/blobs": ^6.5.0 || ^7.0.0
"@planetscale/database": ^1.19.0
"@upstash/redis": ^1.34.0
"@vercel/kv": ^1.0.1
idb-keyval: ^6.2.1
ioredis: ^5.4.1
peerDependenciesMeta:
"@azure/app-configuration":
optional: true
"@azure/cosmos":
optional: true
"@azure/data-tables":
optional: true
"@azure/identity":
optional: true
"@azure/keyvault-secrets":
optional: true
"@azure/storage-blob":
optional: true
"@capacitor/preferences":
optional: true
"@netlify/blobs":
optional: true
"@planetscale/database":
optional: true
"@upstash/redis":
optional: true
"@vercel/kv":
optional: true
idb-keyval:
optional: true
ioredis:
optional: true
checksum: 10/b648d79e9913a87152228a080355d9ccf780900eb78bd32f8dab9cc55eb66ab45876e9fc1ed49f1c7a4171600e78c33430e2527740d991df9d071872409b9c37
languageName: node
linkType: hard
"unstorage@npm:^1.10.2":
version: 1.10.2
resolution: "unstorage@npm:1.10.2"
@@ -53443,6 +53840,18 @@ __metadata:
languageName: node
linkType: hard
"xss@npm:^1.0.14":
version: 1.0.15
resolution: "xss@npm:1.0.15"
dependencies:
commander: "npm:^2.20.3"
cssfilter: "npm:0.0.10"
bin:
xss: bin/xss
checksum: 10/074ad54babac9dd5107466dbf30d3b871dbedae1f8e7b8f4e3b76d60da8b92bd0f66f18ccd26b8524545444ef784b78c526cee089a907aa904f83c8b8d7958f6
languageName: node
linkType: hard
"xtend@npm:^2.2.0":
version: 2.2.0
resolution: "xtend@npm:2.2.0"