Compare commits

..

8 Commits

17 changed files with 468 additions and 182 deletions

View File

@ -29,19 +29,27 @@ const dataResponseSchema = z.object({
}) })
export type TNewsResponse = z.infer<typeof newsResponseSchema> export type TNewsResponse = z.infer<typeof newsResponseSchema>
export type TNewsResponseData = z.infer<typeof dataResponseSchema>
export type TAuthorResponse = z.infer<typeof authorSchema> export type TAuthorResponse = z.infer<typeof authorSchema>
type TParameters = { type TParameters = {
categories?: string[] categories?: string[]
tags?: string[] tags?: string[]
active?: boolean
limit?: number
page?: number
} & THttpServer } & THttpServer
export const getNews = async (parameters?: TParameters) => { export const getNews = async (parameters?: TParameters) => {
const { categories, tags, ...restParameters } = parameters || {} const { categories, tags, active, limit, page, ...restParameters } =
parameters || {}
try { try {
const { data } = await HttpServer(restParameters).get(`/api/news`, { const { data } = await HttpServer(restParameters).get(`/api/news`, {
params: { params: {
...(categories && { categories: categories.join('+') }), ...(categories && { categories: categories.join('+') }),
...(tags && { tags: tags.join('+') }), ...(tags && { tags: tags.join('+') }),
...(active && { active }),
...(limit && { limit }),
...(page && { page }),
}, },
}) })
return dataResponseSchema.parse(data) return dataResponseSchema.parse(data)

View File

@ -0,0 +1,10 @@
import { useAsyncError } from 'react-router'
export const ErrorAwait = () => {
const error = useAsyncError()
return (
<p>
{error instanceof Error ? error.message : 'An unexpected error occurred.'}
</p>
)
}

View File

@ -1,15 +1,17 @@
import useEmblaCarousel from 'embla-carousel-react' import useEmblaCarousel from 'embla-carousel-react'
import { useCallback, useEffect, useState } from 'react' import { Suspense, useCallback, useEffect, useState } from 'react'
import { useRouteLoaderData } from 'react-router' import { Await, useRouteLoaderData } from 'react-router'
import { stripHtml } from 'string-strip-html' import { stripHtml } from 'string-strip-html'
import { Button } from '~/components/ui/button' import { ErrorAwait } from '~/components/error/await'
import { CarouselButton } from '~/components/ui/button-slide' import { CarouselButton } from '~/components/ui/button-slide'
import { useNewsContext } from '~/contexts/news' import { useNewsContext } from '~/contexts/news'
import type { loader } from '~/routes/_news' import type { loader } from '~/routes/_news'
import type { TNews } from '~/types/news' import type { TNews } from '~/types/news'
import { getPremiumAttribute } from '~/utils/render' import { getPremiumAttribute } from '~/utils/render'
import { Button } from './button'
export const CarouselHero = (properties: TNews) => { export const CarouselHero = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext() const { setIsSuccessOpen } = useNewsContext()
const loaderData = useRouteLoaderData<typeof loader>('routes/_news') const loaderData = useRouteLoaderData<typeof loader>('routes/_news')
@ -72,8 +74,17 @@ export const CarouselHero = (properties: TNews) => {
ref={emblaReference} ref={emblaReference}
> >
<div className="embla__container hero flex sm:gap-x-8"> <div className="embla__container hero flex sm:gap-x-8">
{items.map( <Suspense fallback={<div>Loading...</div>}>
({ featured_image, title, content, slug, is_premium }, index) => ( <Await
resolve={items}
errorElement={<ErrorAwait />}
>
{(value) =>
value.data.map(
(
{ featured_image, title, content, slug, is_premium },
index,
) => (
<div <div
className="embla__slide hero w-full min-w-0 flex-none" className="embla__slide hero w-full min-w-0 flex-none"
key={index} key={index}
@ -108,7 +119,10 @@ export const CarouselHero = (properties: TNews) => {
</div> </div>
</div> </div>
), ),
)} )
}
</Await>
</Suspense>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,15 +1,16 @@
import useEmblaCarousel from 'embla-carousel-react' import useEmblaCarousel from 'embla-carousel-react'
import { useCallback, useEffect, useState } from 'react' import { Suspense, useCallback, useEffect, useState } from 'react'
import { useRouteLoaderData } from 'react-router' import { Await, useRouteLoaderData } from 'react-router'
import { stripHtml } from 'string-strip-html' import { stripHtml } from 'string-strip-html'
import { Button } from '~/components/ui/button' import { ErrorAwait } from '~/components/error/await'
import { CarouselButton } from '~/components/ui/button-slide' import { CarouselButton } from '~/components/ui/button-slide'
import { useNewsContext } from '~/contexts/news' import { useNewsContext } from '~/contexts/news'
import type { loader } from '~/routes/_news' import type { loader } from '~/routes/_news'
import type { TNews } from '~/types/news' import type { TNews } from '~/types/news'
import { getPremiumAttribute } from '~/utils/render' import { getPremiumAttribute } from '~/utils/render'
import { Button } from './button'
import { Tags } from './tags' import { Tags } from './tags'
export const CarouselSection = (properties: TNews) => { export const CarouselSection = (properties: TNews) => {
@ -79,7 +80,13 @@ export const CarouselSection = (properties: TNews) => {
ref={emblaReference} ref={emblaReference}
> >
<div className="embla__container col-span-3 flex max-h-[586px] sm:gap-x-8"> <div className="embla__container col-span-3 flex max-h-[586px] sm:gap-x-8">
{items.map( <Suspense fallback={<div>Loading...</div>}>
<Await
resolve={items}
errorElement={<ErrorAwait />}
>
{(value) =>
value.data.map(
( (
{ featured_image, title, content, tags, slug, is_premium }, { featured_image, title, content, tags, slug, is_premium },
index, index,
@ -122,7 +129,10 @@ export const CarouselSection = (properties: TNews) => {
</div> </div>
</div> </div>
), ),
)} )
}
</Await>
</Suspense>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,23 +1,19 @@
import { useRouteLoaderData } from 'react-router' import { Suspense } from 'react'
import { Await, useRouteLoaderData } from 'react-router'
import { stripHtml } from 'string-strip-html' import { stripHtml } from 'string-strip-html'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import type { TNewsResponse } from '~/apis/common/get-news' import { ErrorAwait } from '~/components/error/await'
import { CarouselNextIcon } from '~/components/icons/carousel-next' import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous' import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
import { Button } from '~/components/ui/button' import { Button } from '~/components/ui/button'
import { useNewsContext } from '~/contexts/news' import { useNewsContext } from '~/contexts/news'
import type { loader } from '~/routes/_news' import type { loader } from '~/routes/_news'
import type { TNews } from '~/types/news'
import { getPremiumAttribute } from '~/utils/render' import { getPremiumAttribute } from '~/utils/render'
import { Tags } from './tags' import { Tags } from './tags'
type TNews = {
title: string
description: string
items: TNewsResponse[]
}
export const CategorySection = (properties: TNews) => { export const CategorySection = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext() const { setIsSuccessOpen } = useNewsContext()
const loaderData = useRouteLoaderData<typeof loader>('routes/_news') const loaderData = useRouteLoaderData<typeof loader>('routes/_news')
@ -47,7 +43,13 @@ export const CategorySection = (properties: TNews) => {
</div> </div>
<div className="grid sm:grid-cols-3 sm:gap-x-8"> <div className="grid sm:grid-cols-3 sm:gap-x-8">
{items.map( <Suspense fallback={<div>Loading...</div>}>
<Await
resolve={items}
errorElement={<ErrorAwait />}
>
{(value) =>
value.data.map(
( (
{ featured_image, title, content, tags, slug, is_premium }, { featured_image, title, content, tags, slug, is_premium },
index, index,
@ -63,7 +65,9 @@ export const CategorySection = (properties: TNews) => {
src={featured_image} src={featured_image}
alt={title} alt={title}
/> />
<div className={twMerge('flex flex-col justify-between gap-4')}> <div
className={twMerge('flex flex-col justify-between gap-4')}
>
<Tags <Tags
tags={tags} tags={tags}
is_premium={is_premium} is_premium={is_premium}
@ -96,7 +100,10 @@ export const CategorySection = (properties: TNews) => {
</div> </div>
</div> </div>
), ),
)} )
}
</Await>
</Suspense>
</div> </div>
<div className="my-5 mt-5 flex flex-row-reverse"> <div className="my-5 mt-5 flex flex-row-reverse">

View File

@ -2,10 +2,14 @@ import xior, { merge } from 'xior'
const baseURL = import.meta.env.VITE_API_URL const baseURL = import.meta.env.VITE_API_URL
export type THttpServer = { accessToken?: string } export type THttpServer = {
accessToken?: string
ipAddress?: string | null
userAgent?: string | null
}
export const HttpServer = (parameters?: THttpServer) => { export const HttpServer = (parameters?: THttpServer) => {
const { accessToken } = parameters || {} const { accessToken, ipAddress, userAgent } = parameters || {}
const instance = xior.create({ const instance = xior.create({
baseURL, baseURL,
}) })
@ -16,6 +20,8 @@ export const HttpServer = (parameters?: THttpServer) => {
return merge(config, { return merge(config, {
headers: { headers: {
...(accessToken && { Authorization: `Bearer ${accessToken}` }), ...(accessToken && { Authorization: `Bearer ${accessToken}` }),
...(ipAddress && { 'X-Ip-Address': ipAddress }),
...(userAgent && { 'X-User-Agent': userAgent }),
}, },
}) })
}) })

View File

@ -17,7 +17,7 @@ export const NewsCategoriesPage = () => {
<CategorySection <CategorySection
title={name || ''} title={name || ''}
description={description || ''} description={description || ''}
items={newsData || []} items={newsData || Promise.resolve({ data: [] })}
/> />
</Card> </Card>
</div> </div>

View File

@ -21,7 +21,7 @@ export const NewsDetailPage = () => {
const berita: TNews = { const berita: TNews = {
title: loaderData?.beritaCategory?.name || '', title: loaderData?.beritaCategory?.name || '',
description: loaderData?.beritaCategory?.description || '', description: loaderData?.beritaCategory?.description || '',
items: loaderData?.beritaNews || [], items: loaderData?.beritaData || Promise.resolve({ data: [] }),
} }
const currentUrl = globalThis.location const currentUrl = globalThis.location
const { title, content, featured_image, author, live_at, tags } = const { title, content, featured_image, author, live_at, tags } =

View File

@ -12,17 +12,17 @@ export const NewsPage = () => {
const spotlight: TNews = { const spotlight: TNews = {
title: loaderData?.spotlightCategory?.name || '', title: loaderData?.spotlightCategory?.name || '',
description: loaderData?.spotlightCategory?.description || '', description: loaderData?.spotlightCategory?.description || '',
items: loaderData?.spotlightNews || [], items: loaderData?.spotlightData || Promise.resolve({ data: [] }),
} }
const berita: TNews = { const berita: TNews = {
title: loaderData?.beritaCategory?.name || '', title: loaderData?.beritaCategory?.name || '',
description: loaderData?.beritaCategory?.description || '', description: loaderData?.beritaCategory?.description || '',
items: loaderData?.beritaNews || [], items: loaderData?.beritaData || Promise.resolve({ data: [] }),
} }
const kajian: TNews = { const kajian: TNews = {
title: loaderData?.kajianCategory?.name || '', title: loaderData?.kajianCategory?.name || '',
description: loaderData?.kajianCategory?.description || '', description: loaderData?.kajianCategory?.description || '',
items: loaderData?.kajianNews || [], items: loaderData?.kajianData || Promise.resolve({ data: [] }),
} }
return ( return (

View File

@ -25,31 +25,25 @@ export const loader = async ({}: Route.LoaderArgs) => {
(category) => category.code === kajianCode, (category) => category.code === kajianCode,
) )
let { data: spotlightNews } = await getNews({ const spotlightData = getNews({
categories: [spotlightCode], categories: [spotlightCode],
active: true,
}) })
spotlightNews = spotlightNews.filter( const beritaData = getNews({
(news) => new Date(news.live_at) <= new Date(),
)
let { data: beritaNews } = await getNews({
categories: [beritaCode], categories: [beritaCode],
active: true,
}) })
beritaNews = beritaNews.filter((news) => new Date(news.live_at) <= new Date()) const kajianData = getNews({
let { data: kajianNews } = await getNews({
categories: [kajianCode], categories: [kajianCode],
active: true,
}) })
kajianNews = kajianNews.filter((news) => new Date(news.live_at) <= new Date())
return { return {
spotlightCategory, spotlightCategory,
spotlightCode,
beritaCategory, beritaCategory,
beritaCode,
kajianCategory, kajianCategory,
kajianCode, spotlightData,
spotlightNews, beritaData,
beritaNews, kajianData,
kajianNews,
} }
} }

View File

@ -11,8 +11,7 @@ export const loader = async ({ params }: Route.LoaderArgs) => {
const { data: categoriesData } = await getCategories() const { data: categoriesData } = await getCategories()
const { code } = params const { code } = params
const categoryData = categoriesData.find((category) => category.code === code) const categoryData = categoriesData.find((category) => category.code === code)
let { data: newsData } = await getNews({ categories: [code] }) const newsData = getNews({ categories: [code], active: true })
newsData = newsData.filter((news) => new Date(news.live_at) <= new Date())
return { categoryData, newsData } return { categoryData, newsData }
} }

View File

@ -1,4 +1,5 @@
import { isRouteErrorResponse } from 'react-router' import { isRouteErrorResponse } from 'react-router'
import { getClientIPAddress } from 'remix-utils/get-client-ip-address'
import { stripHtml } from 'string-strip-html' import { stripHtml } from 'string-strip-html'
import { getCategories } from '~/apis/common/get-categories' import { getCategories } from '~/apis/common/get-categories'
@ -12,6 +13,8 @@ import { NewsDetailPage } from '~/pages/news-detail'
import type { Route } from './+types/_news.detail.$slug' import type { Route } from './+types/_news.detail.$slug'
export const loader = async ({ request, params }: Route.LoaderArgs) => { export const loader = async ({ request, params }: Route.LoaderArgs) => {
const userAgent = request.headers.get('user-agent')
const ipAddress = getClientIPAddress(request) || 'localhost'
const { userToken: accessToken } = await handleCookie(request) const { userToken: accessToken } = await handleCookie(request)
let userData let userData
if (accessToken) { if (accessToken) {
@ -19,7 +22,12 @@ export const loader = async ({ request, params }: Route.LoaderArgs) => {
userData = data userData = data
} }
const { slug } = params const { slug } = params
let { data: newsDetailData } = await getNewsBySlug({ slug, accessToken }) let { data: newsDetailData } = await getNewsBySlug({
slug,
accessToken,
userAgent,
ipAddress,
})
const shouldSubscribe = const shouldSubscribe =
(!accessToken || userData?.subscribe?.subscribe_plan?.code === 'basic') && (!accessToken || userData?.subscribe?.subscribe_plan?.code === 'basic') &&
newsDetailData?.is_premium newsDetailData?.is_premium
@ -34,13 +42,12 @@ export const loader = async ({ request, params }: Route.LoaderArgs) => {
const beritaCategory = categoriesData.find( const beritaCategory = categoriesData.find(
(category) => category.code === beritaCode, (category) => category.code === beritaCode,
) )
let { data: beritaNews } = await getNews({ categories: [beritaCode] }) const beritaData = getNews({ categories: [beritaCode], active: true })
beritaNews = beritaNews.filter((news) => new Date(news.live_at) <= new Date())
return { return {
newsDetailData, newsDetailData,
beritaCategory, beritaCategory,
beritaNews, beritaData,
shouldSubscribe, shouldSubscribe,
} }
} }

View File

@ -1,4 +1,5 @@
import { data } from 'react-router' import { data } from 'react-router'
import { getClientIPAddress } from 'remix-utils/get-client-ip-address'
import { XiorError } from 'xior' import { XiorError } from 'xior'
import { createLogAdsRequest } from '~/apis/news/create-log-ads' import { createLogAdsRequest } from '~/apis/news/create-log-ads'
@ -7,12 +8,16 @@ import { handleCookie } from '~/libs/cookies'
import type { Route } from './+types/actions.log.ads.$id' import type { Route } from './+types/actions.log.ads.$id'
export const action = async ({ request, params }: Route.ActionArgs) => { export const action = async ({ request, params }: Route.ActionArgs) => {
const userAgent = request.headers.get('user-agent')
const ipAddress = getClientIPAddress(request) || 'localhost'
const { userToken: accessToken } = await handleCookie(request) const { userToken: accessToken } = await handleCookie(request)
const { id } = params const { id } = params
try { try {
const { data: logsData } = await createLogAdsRequest({ const { data: logsData } = await createLogAdsRequest({
id, id,
accessToken, accessToken,
userAgent,
ipAddress,
}) })
return data( return data(

View File

@ -1,7 +1,7 @@
import type { TNewsResponse } from '~/apis/common/get-news' import type { TNewsResponseData } from '~/apis/common/get-news'
export type TNews = { export type TNews = {
title: string title: string
description: string description: string
items: TNewsResponse[] items: Promise<TNewsResponseData>
} }

View File

@ -1,4 +1,4 @@
import { decodeJwt } from 'jose' import { JWT } from '@edgefirst-dev/jwt'
import { import {
staffTokenCookieConfig, staffTokenCookieConfig,
@ -12,10 +12,10 @@ type TTokenCookie = {
export const generateUserTokenCookie = (parameters: TTokenCookie) => { export const generateUserTokenCookie = (parameters: TTokenCookie) => {
const { accessToken } = parameters const { accessToken } = parameters
const decodedToken = decodeJwt(accessToken) const decodedToken = JWT.decode(accessToken)
const decodedTokenExp = decodedToken.exp const decodedTokenExp = decodedToken.exp
const expirationDate = decodedTokenExp const expirationDate = decodedTokenExp
? new Date(decodedTokenExp * 1000) ? new Date(Number(decodedTokenExp) * 1000)
: undefined : undefined
return userTokenCookieConfig.serialize(accessToken, { return userTokenCookieConfig.serialize(accessToken, {
@ -26,10 +26,10 @@ export const generateUserTokenCookie = (parameters: TTokenCookie) => {
export const generateStaffTokenCookie = (parameters: TTokenCookie) => { export const generateStaffTokenCookie = (parameters: TTokenCookie) => {
const { accessToken } = parameters const { accessToken } = parameters
const decodedToken = decodeJwt(accessToken) const decodedToken = JWT.decode(accessToken)
const decodedTokenExp = decodedToken.exp const decodedTokenExp = decodedToken.exp
const expirationDate = decodedTokenExp const expirationDate = decodedTokenExp
? new Date(decodedTokenExp * 1000) ? new Date(Number(decodedTokenExp) * 1000)
: undefined : undefined
return staffTokenCookieConfig.serialize(accessToken, { return staffTokenCookieConfig.serialize(accessToken, {

View File

@ -14,10 +14,15 @@
"validate": "pnpm lint && pnpm typecheck && pnpm knip" "validate": "pnpm lint && pnpm typecheck && pnpm knip"
}, },
"dependencies": { "dependencies": {
"@edgefirst-dev/batcher": "^1.0.1",
"@edgefirst-dev/jwt": "^1.2.0",
"@edgefirst-dev/server-timing": "^0.0.1",
"@headlessui/react": "^2.2.0", "@headlessui/react": "^2.2.0",
"@heroicons/react": "^2.2.0", "@heroicons/react": "^2.2.0",
"@hookform/resolvers": "^4.1.1", "@hookform/resolvers": "^4.1.1",
"@monaco-editor/react": "^4.7.0", "@monaco-editor/react": "^4.7.0",
"@oslojs/crypto": "^1.0.1",
"@oslojs/encoding": "^1.1.0",
"@react-router/fs-routes": "^7.1.3", "@react-router/fs-routes": "^7.1.3",
"@react-router/node": "^7.1.3", "@react-router/node": "^7.1.3",
"@react-router/serve": "^7.1.3", "@react-router/serve": "^7.1.3",
@ -37,8 +42,9 @@
"embla-carousel-autoplay": "^8.5.2", "embla-carousel-autoplay": "^8.5.2",
"embla-carousel-react": "^8.5.2", "embla-carousel-react": "^8.5.2",
"html-react-parser": "^5.2.2", "html-react-parser": "^5.2.2",
"intl-parse-accept-language": "^1.0.0",
"is-ip": "^5.0.1",
"isbot": "^5.1.17", "isbot": "^5.1.17",
"jose": "^6.0.8",
"react": "^19.0.0", "react": "^19.0.0",
"react-chartjs-2": "^5.3.0", "react-chartjs-2": "^5.3.0",
"react-colorful": "^5.6.1", "react-colorful": "^5.6.1",
@ -49,6 +55,7 @@
"react-router": "^7.1.3", "react-router": "^7.1.3",
"react-share": "^5.2.2", "react-share": "^5.2.2",
"remix-hook-form": "^6.1.3", "remix-hook-form": "^6.1.3",
"remix-utils": "^8.5.0",
"string-strip-html": "^13.4.12", "string-strip-html": "^13.4.12",
"tailwind-merge": "^3.0.1", "tailwind-merge": "^3.0.1",
"xior": "^0.6.3", "xior": "^0.6.3",

225
pnpm-lock.yaml generated
View File

@ -8,6 +8,15 @@ importers:
.: .:
dependencies: dependencies:
'@edgefirst-dev/batcher':
specifier: ^1.0.1
version: 1.0.1
'@edgefirst-dev/jwt':
specifier: ^1.2.0
version: 1.2.0
'@edgefirst-dev/server-timing':
specifier: ^0.0.1
version: 0.0.1
'@headlessui/react': '@headlessui/react':
specifier: ^2.2.0 specifier: ^2.2.0
version: 2.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 2.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -20,6 +29,12 @@ importers:
'@monaco-editor/react': '@monaco-editor/react':
specifier: ^4.7.0 specifier: ^4.7.0
version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@oslojs/crypto':
specifier: ^1.0.1
version: 1.0.1
'@oslojs/encoding':
specifier: ^1.1.0
version: 1.1.0
'@react-router/fs-routes': '@react-router/fs-routes':
specifier: ^7.1.3 specifier: ^7.1.3
version: 7.1.3(@react-router/dev@7.1.3(@react-router/serve@7.1.3(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(typescript@5.7.3))(@types/node@20.17.16)(babel-plugin-macros@3.1.0)(lightningcss@1.29.1)(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(typescript@5.7.3)(vite@5.4.14(@types/node@20.17.16)(lightningcss@1.29.1)))(typescript@5.7.3) version: 7.1.3(@react-router/dev@7.1.3(@react-router/serve@7.1.3(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(typescript@5.7.3))(@types/node@20.17.16)(babel-plugin-macros@3.1.0)(lightningcss@1.29.1)(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(typescript@5.7.3)(vite@5.4.14(@types/node@20.17.16)(lightningcss@1.29.1)))(typescript@5.7.3)
@ -77,12 +92,15 @@ importers:
html-react-parser: html-react-parser:
specifier: ^5.2.2 specifier: ^5.2.2
version: 5.2.2(@types/react@19.0.8)(react@19.0.0) version: 5.2.2(@types/react@19.0.8)(react@19.0.0)
intl-parse-accept-language:
specifier: ^1.0.0
version: 1.0.0
is-ip:
specifier: ^5.0.1
version: 5.0.1
isbot: isbot:
specifier: ^5.1.17 specifier: ^5.1.17
version: 5.1.22 version: 5.1.22
jose:
specifier: ^6.0.8
version: 6.0.8
react: react:
specifier: ^19.0.0 specifier: ^19.0.0
version: 19.0.0 version: 19.0.0
@ -113,6 +131,9 @@ importers:
remix-hook-form: remix-hook-form:
specifier: ^6.1.3 specifier: ^6.1.3
version: 6.1.3(react-dom@19.0.0(react@19.0.0))(react-hook-form@7.54.2(react@19.0.0))(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) version: 6.1.3(react-dom@19.0.0(react@19.0.0))(react-hook-form@7.54.2(react@19.0.0))(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
remix-utils:
specifier: ^8.5.0
version: 8.5.0(@edgefirst-dev/batcher@1.0.1)(@edgefirst-dev/jwt@1.2.0)(@edgefirst-dev/server-timing@0.0.1)(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(intl-parse-accept-language@1.0.0)(is-ip@5.0.1)(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(zod@3.24.2)
string-strip-html: string-strip-html:
specifier: ^13.4.12 specifier: ^13.4.12
version: 13.4.12 version: 13.4.12
@ -448,6 +469,22 @@ packages:
resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==}
engines: {node: '>=v18'} engines: {node: '>=v18'}
'@edgefirst-dev/batcher@1.0.1':
resolution: {integrity: sha512-9AsnqLSIbO0mK7Du6lRp3v7hCJuercNo7t16leZeMCnI3QReZDE2IEtbLpDwrIrcOS7xt1vgdonKYU9GQqittw==}
engines: {node: '>=20.0.0'}
'@edgefirst-dev/data@0.0.4':
resolution: {integrity: sha512-VLhlvEPDJ0Sd0pE6sAYTQkIqZCXVonaWlgRJIQQHzfjTXCadF77qqHj5NxaPSc4wCul0DJO/0MnejVqJAXUiRg==}
engines: {node: '>=20.0.0'}
'@edgefirst-dev/jwt@1.2.0':
resolution: {integrity: sha512-MnNceBAmJYhoctIAGYivh0/sSsKYXfEPfwGZ8tsoX96+vSRuoeLrBi4p2L9NHCjqxMafd4KMKk+93SfX3sW7dQ==}
engines: {node: '>=20.0.0'}
'@edgefirst-dev/server-timing@0.0.1':
resolution: {integrity: sha512-WlvF/dhgM7CE9SOb3Ji6Wj4PsIk21CHvXzRVdQwmeS1eVEVimWqagiYuV5e8/7Owt7SDFpeVnuF0q2CtONch0g==}
engines: {node: '>=20.0.0'}
'@emotion/babel-plugin@11.13.5': '@emotion/babel-plugin@11.13.5':
resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==}
@ -747,6 +784,12 @@ packages:
'@kurkle/color@0.3.4': '@kurkle/color@0.3.4':
resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
'@mjackson/file-storage@0.6.1':
resolution: {integrity: sha512-H3GEVpmfmNryNoYloddIOba5OAwckfVGMvutPeI94Shbv/R+NVh89gIYa8SK3Vfa+ky9PitclP+5XQ6/zlSdQQ==}
'@mjackson/lazy-file@3.3.1':
resolution: {integrity: sha512-BxpNT1KmLx0OLYfgQESx/AKGD2czwfZXh9c0SaDUQY2DRAaVYtAvSQE5EkpATFdQQKqfL+iXVoaQ/SN+w7/CDA==}
'@mjackson/node-fetch-server@0.2.0': '@mjackson/node-fetch-server@0.2.0':
resolution: {integrity: sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==} resolution: {integrity: sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==}
@ -803,6 +846,18 @@ packages:
'@one-ini/wasm@0.1.1': '@one-ini/wasm@0.1.1':
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
'@oslojs/asn1@1.0.0':
resolution: {integrity: sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==}
'@oslojs/binary@1.0.0':
resolution: {integrity: sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==}
'@oslojs/crypto@1.0.1':
resolution: {integrity: sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==}
'@oslojs/encoding@1.1.0':
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'} engines: {node: '>=14'}
@ -2001,6 +2056,10 @@ packages:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
clone-regexp@3.0.0:
resolution: {integrity: sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==}
engines: {node: '>=12'}
clone@1.0.4: clone@1.0.4:
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
engines: {node: '>=0.8'} engines: {node: '>=0.8'}
@ -2081,6 +2140,10 @@ packages:
engines: {node: '>=16'} engines: {node: '>=16'}
hasBin: true hasBin: true
convert-hrtime@5.0.0:
resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==}
engines: {node: '>=12'}
convert-source-map@1.9.0: convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
@ -2712,6 +2775,10 @@ packages:
function-bind@1.1.2: function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
function-timeout@0.1.1:
resolution: {integrity: sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==}
engines: {node: '>=14.16'}
function.prototype.name@1.1.8: function.prototype.name@1.1.8:
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -2937,6 +3004,14 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
intl-parse-accept-language@1.0.0:
resolution: {integrity: sha512-YFMSV91JNBOSjw1cOfw2tup6hDP7mkz+2AUV7W1L1AM6ntgI75qC1ZeFpjPGMrWp+upmBRTX2fJWQ8c7jsUWpA==}
engines: {node: '>=14'}
ip-regex@5.0.0:
resolution: {integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
ipaddr.js@1.9.1: ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
@ -3018,6 +3093,10 @@ packages:
resolution: {integrity: sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==} resolution: {integrity: sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
is-ip@5.0.1:
resolution: {integrity: sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==}
engines: {node: '>=14.16'}
is-map@2.0.3: is-map@2.0.3:
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -3042,6 +3121,10 @@ packages:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
is-regexp@3.1.0:
resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==}
engines: {node: '>=12'}
is-set@2.0.3: is-set@2.0.3:
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -3463,6 +3546,10 @@ packages:
motion-utils@11.18.1: motion-utils@11.18.1:
resolution: {integrity: sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==} resolution: {integrity: sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==}
mrmime@2.0.1:
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
engines: {node: '>=10'}
ms@2.0.0: ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
@ -4096,6 +4183,42 @@ packages:
react-hook-form: ^7.51.0 react-hook-form: ^7.51.0
react-router: '>=7.0.0' react-router: '>=7.0.0'
remix-utils@8.5.0:
resolution: {integrity: sha512-Wf9OGSJveBaVHKptbEgxc+DwKRUUGOH+aiaBlsrAA2b4F+gNtCkvaZzA7Tp+1esBElRcRvMZQq/0aSSWFMP18A==}
engines: {node: '>=20.0.0'}
peerDependencies:
'@edgefirst-dev/batcher': ^1.0.0
'@edgefirst-dev/jwt': ^1.2.0
'@edgefirst-dev/server-timing': ^0.0.1
'@oslojs/crypto': ^1.0.1
'@oslojs/encoding': ^1.1.0
intl-parse-accept-language: ^1.0.0
is-ip: ^5.0.1
react: ^18.0.0 || ^19.0.0
react-router: ^7.0.0
zod: ^3.22.4
peerDependenciesMeta:
'@edgefirst-dev/batcher':
optional: true
'@edgefirst-dev/jwt':
optional: true
'@edgefirst-dev/server-timing':
optional: true
'@oslojs/crypto':
optional: true
'@oslojs/encoding':
optional: true
intl-parse-accept-language:
optional: true
is-ip:
optional: true
react:
optional: true
react-router:
optional: true
zod:
optional: true
require-directory@2.1.1: require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -4403,6 +4526,10 @@ packages:
summary@2.1.0: summary@2.1.0:
resolution: {integrity: sha512-nMIjMrd5Z2nuB2RZCKJfFMjgS3fygbeyGk9PxPPaJR1RIcyN9yn4A63Isovzm3ZtQuEkLBVgMdPup8UeLH7aQw==} resolution: {integrity: sha512-nMIjMrd5Z2nuB2RZCKJfFMjgS3fygbeyGk9PxPPaJR1RIcyN9yn4A63Isovzm3ZtQuEkLBVgMdPup8UeLH7aQw==}
super-regex@0.2.0:
resolution: {integrity: sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==}
engines: {node: '>=14.16'}
supports-color@7.2.0: supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -4437,6 +4564,10 @@ packages:
through@2.3.8: through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
time-span@5.1.0:
resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==}
engines: {node: '>=12'}
tiny-invariant@1.3.3: tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
@ -4507,6 +4638,10 @@ packages:
resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
engines: {node: '>=8'} engines: {node: '>=8'}
type-fest@4.37.0:
resolution: {integrity: sha512-S/5/0kFftkq27FPNye0XM1e2NsnoD/3FS+pBmbjmmtLT6I+i344KoOf7pvXreaFsDamWeaJX55nczA1m5PsBDg==}
engines: {node: '>=16'}
type-is@1.6.18: type-is@1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
@ -5105,6 +5240,21 @@ snapshots:
'@types/conventional-commits-parser': 5.0.1 '@types/conventional-commits-parser': 5.0.1
chalk: 5.4.1 chalk: 5.4.1
'@edgefirst-dev/batcher@1.0.1':
dependencies:
type-fest: 4.37.0
'@edgefirst-dev/data@0.0.4': {}
'@edgefirst-dev/jwt@1.2.0':
dependencies:
'@edgefirst-dev/data': 0.0.4
'@mjackson/file-storage': 0.6.1
jose: 6.0.8
type-fest: 4.37.0
'@edgefirst-dev/server-timing@0.0.1': {}
'@emotion/babel-plugin@11.13.5': '@emotion/babel-plugin@11.13.5':
dependencies: dependencies:
'@babel/helper-module-imports': 7.25.9 '@babel/helper-module-imports': 7.25.9
@ -5391,6 +5541,14 @@ snapshots:
'@kurkle/color@0.3.4': {} '@kurkle/color@0.3.4': {}
'@mjackson/file-storage@0.6.1':
dependencies:
'@mjackson/lazy-file': 3.3.1
'@mjackson/lazy-file@3.3.1':
dependencies:
mrmime: 2.0.1
'@mjackson/node-fetch-server@0.2.0': {} '@mjackson/node-fetch-server@0.2.0': {}
'@monaco-editor/loader@1.5.0': '@monaco-editor/loader@1.5.0':
@ -5461,6 +5619,19 @@ snapshots:
'@one-ini/wasm@0.1.1': {} '@one-ini/wasm@0.1.1': {}
'@oslojs/asn1@1.0.0':
dependencies:
'@oslojs/binary': 1.0.0
'@oslojs/binary@1.0.0': {}
'@oslojs/crypto@1.0.1':
dependencies:
'@oslojs/asn1': 1.0.0
'@oslojs/binary': 1.0.0
'@oslojs/encoding@1.1.0': {}
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
optional: true optional: true
@ -6698,6 +6869,10 @@ snapshots:
strip-ansi: 6.0.1 strip-ansi: 6.0.1
wrap-ansi: 7.0.0 wrap-ansi: 7.0.0
clone-regexp@3.0.0:
dependencies:
is-regexp: 3.1.0
clone@1.0.4: clone@1.0.4:
optional: true optional: true
@ -6779,6 +6954,8 @@ snapshots:
meow: 12.1.1 meow: 12.1.1
split2: 4.2.0 split2: 4.2.0
convert-hrtime@5.0.0: {}
convert-source-map@1.9.0: {} convert-source-map@1.9.0: {}
convert-source-map@2.0.0: {} convert-source-map@2.0.0: {}
@ -7576,6 +7753,8 @@ snapshots:
function-bind@1.1.2: {} function-bind@1.1.2: {}
function-timeout@0.1.1: {}
function.prototype.name@1.1.8: function.prototype.name@1.1.8:
dependencies: dependencies:
call-bind: 1.0.8 call-bind: 1.0.8
@ -7805,6 +7984,10 @@ snapshots:
hasown: 2.0.2 hasown: 2.0.2
side-channel: 1.1.0 side-channel: 1.1.0
intl-parse-accept-language@1.0.0: {}
ip-regex@5.0.0: {}
ipaddr.js@1.9.1: {} ipaddr.js@1.9.1: {}
is-array-buffer@3.0.5: is-array-buffer@3.0.5:
@ -7886,6 +8069,11 @@ snapshots:
is-gzip@1.0.0: {} is-gzip@1.0.0: {}
is-ip@5.0.1:
dependencies:
ip-regex: 5.0.0
super-regex: 0.2.0
is-map@2.0.3: {} is-map@2.0.3: {}
is-number-object@1.1.1: is-number-object@1.1.1:
@ -7906,6 +8094,8 @@ snapshots:
has-tostringtag: 1.0.2 has-tostringtag: 1.0.2
hasown: 2.0.2 hasown: 2.0.2
is-regexp@3.1.0: {}
is-set@2.0.3: {} is-set@2.0.3: {}
is-shared-array-buffer@1.0.4: is-shared-array-buffer@1.0.4:
@ -8291,6 +8481,8 @@ snapshots:
motion-utils@11.18.1: {} motion-utils@11.18.1: {}
mrmime@2.0.1: {}
ms@2.0.0: {} ms@2.0.0: {}
ms@2.1.3: {} ms@2.1.3: {}
@ -8936,6 +9128,21 @@ snapshots:
react-hook-form: 7.54.2(react@19.0.0) react-hook-form: 7.54.2(react@19.0.0)
react-router: 7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react-router: 7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
remix-utils@8.5.0(@edgefirst-dev/batcher@1.0.1)(@edgefirst-dev/jwt@1.2.0)(@edgefirst-dev/server-timing@0.0.1)(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(intl-parse-accept-language@1.0.0)(is-ip@5.0.1)(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(zod@3.24.2):
dependencies:
type-fest: 4.37.0
optionalDependencies:
'@edgefirst-dev/batcher': 1.0.1
'@edgefirst-dev/jwt': 1.2.0
'@edgefirst-dev/server-timing': 0.0.1
'@oslojs/crypto': 1.0.1
'@oslojs/encoding': 1.1.0
intl-parse-accept-language: 1.0.0
is-ip: 5.0.1
react: 19.0.0
react-router: 7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
zod: 3.24.2
require-directory@2.1.1: {} require-directory@2.1.1: {}
require-from-string@2.0.2: {} require-from-string@2.0.2: {}
@ -9300,6 +9507,12 @@ snapshots:
summary@2.1.0: {} summary@2.1.0: {}
super-regex@0.2.0:
dependencies:
clone-regexp: 3.0.0
function-timeout: 0.1.1
time-span: 5.1.0
supports-color@7.2.0: supports-color@7.2.0:
dependencies: dependencies:
has-flag: 4.0.0 has-flag: 4.0.0
@ -9325,6 +9538,10 @@ snapshots:
through@2.3.8: {} through@2.3.8: {}
time-span@5.1.0:
dependencies:
convert-hrtime: 5.0.0
tiny-invariant@1.3.3: {} tiny-invariant@1.3.3: {}
tiny-lru@11.2.11: {} tiny-lru@11.2.11: {}
@ -9377,6 +9594,8 @@ snapshots:
type-fest@0.8.1: {} type-fest@0.8.1: {}
type-fest@4.37.0: {}
type-is@1.6.18: type-is@1.6.18:
dependencies: dependencies:
media-typer: 0.3.0 media-typer: 0.3.0