Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions apps/frontend/src/composables/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,16 @@ export interface GeneratedState extends GloballyUsedState {
}

/**
* Composable for accessing the globally used generated state.
* This includes both fetched data and runtime-defined constants.
* Built once per module load rather than via `useState`, because every `useState` value is
* serialized into the SSR payload of every page. This is build-time constant data that the
* client already has via the static import above, so putting it in the payload would ship a
* second copy for no benefit.
*
* Consequence: on the server this object is shared by every request in the isolate, so it must
* stay immutable there. Runtime refreshes are client-only and go through `setGameVersions`.
*/
export const useGeneratedState = () =>
useState<GeneratedState>('generatedState', () => ({
const generatedState = shallowRef<GeneratedState>(
Object.freeze({
// Cast JSON data to typed API responses
categories: (categories ?? []) as Labrinth.Tags.v2.Category[],
loaders: (loaders ?? []) as Labrinth.Tags.v2.Loader[],
Expand Down Expand Up @@ -147,4 +152,24 @@ export const useGeneratedState = () =>
errors,

buildYear: new Date().getFullYear(),
}))
}) as GeneratedState,
)

/**
* Composable for accessing the globally used generated state.
* This includes both fetched data and runtime-defined constants.
*/
export const useGeneratedState = () => generatedState

/**
* Replaces the build-time game versions with a freshly fetched list. Client-only: mutating this
* on the server would leak across every request sharing the isolate.
*/
export function setGameVersions(versions: Labrinth.Tags.v2.GameVersion[]) {
if (import.meta.server) return

generatedState.value = Object.freeze({
...generatedState.value,
gameVersions: versions,
}) as GeneratedState
}
19 changes: 17 additions & 2 deletions apps/frontend/src/pages/discover/[type]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,19 @@ function parseSearchParams(requestParams: string): Labrinth.Search.SearchParams
}
}

// Search returns expanded dependency data that no card renders and that isn't part of
// ResultSearchProject. On modpacks it is ~90% of the response, and everything cached here
// is serialized into the SSR payload, so drop it before it reaches the query cache.
function stripUnrenderedFields(
hits: Labrinth.Search.v3.ResultSearchProject[],
): Labrinth.Search.v3.ResultSearchProject[] {
return hits.map((hit) => {
const { dependencies, dependency_project_ids, compatible_dependency_project_ids, ...rendered } =
hit as Labrinth.Search.v3.ResultSearchProject & Record<string, unknown>
return rendered as Labrinth.Search.v3.ResultSearchProject
})
}

async function fetchSearch(requestParams: string) {
debug('search() called', {
requestParams: requestParams.substring(0, 100),
Expand All @@ -241,17 +254,19 @@ async function fetchSearch(requestParams: string) {

debug('search() response', { total_hits: raw.total_hits, hitCount: raw.hits?.length })

const hits = stripUnrenderedFields(raw.hits ?? [])

if (isServerType.value) {
return {
projectHits: [],
serverHits: raw.hits,
serverHits: hits,
total_hits: raw.total_hits,
per_page: raw.hits_per_page,
}
}

return {
projectHits: raw.hits,
projectHits: hits,
serverHits: [],
total_hits: raw.total_hits,
per_page: raw.hits_per_page,
Expand Down
19 changes: 19 additions & 0 deletions apps/frontend/src/plugins/update-game-versions.client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Labrinth } from '@modrinth/api-client'

import { setGameVersions } from '~/composables/generated'

export default defineNuxtPlugin((nuxtApp) => {
// Deferred until after hydration: the server renders with the build-time game versions, so
// swapping them in during setup would make the first client render disagree with the markup.
nuxtApp.hook('app:suspense:resolve', async () => {
try {
const gameVersions = await $fetch<Labrinth.Tags.v2.GameVersion[]>('/api/tags/game-versions')

if (gameVersions && gameVersions.length > 0) {
setGameVersions(gameVersions)
}
} catch (error) {
console.error('[Game Version Updater] Failed to fetch:', error)
}
})
})
17 changes: 0 additions & 17 deletions apps/frontend/src/plugins/update-game-versions.ts

This file was deleted.

1 change: 1 addition & 0 deletions apps/frontend/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"routes": ["modrinth.com/*"],
"preview_urls": true,
"workers_dev": true,
"upload_source_maps": true,
"limits": {
"cpu_ms": 5000
},
Expand Down
Loading