Created
December 30, 2025 12:37
-
-
Save igor9silva/75514ad56e5bad708836c565f344a409 to your computer and use it in GitHub Desktop.
Type-safe utility for accessing streaming promises from SvelteKit server load functions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { page } from '$app/state'; | |
| /** | |
| * Type-safe utility for accessing streaming promises from SvelteKit server load functions. | |
| * | |
| * Solves two key problems: | |
| * 1. **Prop drilling**: Components can access page.data.streaming directly without passing promises through parent components | |
| * 2. **Type safety**: Infers exact promise types instead of unions, avoiding type guards everywhere | |
| * | |
| * @example | |
| * in +page.server.ts: | |
| * export const load = async ({ locals: { supabase } }) => { | |
| * | |
| * const chats = supabase.from('chats').select('*'); | |
| * const creatorMood: Record<string, Promise<TMoodData | null>> = {}; | |
| * | |
| * creatorMood['creator-123'] = supabase.rpc('get_creator_mood', { id: 'creator-123' }); | |
| * | |
| * return { | |
| * streaming: { chats, creatorMood } | |
| * }; | |
| * }; | |
| * | |
| * in component (no prop drilling!): | |
| * import { getStreamingPromise } from '$lib/utils/streaming'; | |
| * import type { PageData } from './$types'; | |
| * | |
| * const moodPromise = getStreamingPromise<PageData>()('creatorMood', 'creator-123'); | |
| * ^? Promise<TMoodData | null> | |
| * | |
| * {#await moodPromise} | |
| * Loading... | |
| * {:then data} | |
| * {data.mood_name} | |
| * {/await} | |
| */ | |
| /** | |
| * Creates a type-safe accessor for streaming promises from server load function page data. | |
| * | |
| * This lets TypeScript narrow the return type to the exact promise type (for the given streaming key) | |
| * instead of a union of all possible streaming keys, avoiding the need for type guards. | |
| * | |
| * @template TPageData - The PageData type from +page.server.ts (imported via ./$types) | |
| * @returns A function that accepts a streaming key and optional ID (for records) | |
| */ | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intended | |
| export function getStreamingPromise<TPageData extends { streaming?: Record<string, any> }>() { | |
| // | |
| // the returned function uses 'const TKey' to preserve the literal type of the key | |
| // without 'const', TypeScript would widen 'creatorMood' to string, losing type information | |
| return function <const TKey extends keyof NonNullable<TPageData['streaming']>>( | |
| streamingKey: TKey, | |
| id?: string, | |
| ): StreamingReturnType<TPageData, TKey> { | |
| return getStreamingPromiseImpl<TPageData, TKey>(streamingKey, id); | |
| }; | |
| } | |
| /** | |
| * Extracts the resolved value type from a streaming promise. | |
| * | |
| * Handles two patterns: | |
| * 1. Direct promises: `streaming.chats -> Promise<TCustomerChat[]>` | |
| * 2. Nested records: `streaming.creatorMood -> Record<creatorId, Promise<TMoodData>>` | |
| * | |
| * The conditional type checks if the value at TKey is a Record of promises (pattern 2) | |
| * or a direct Promise (pattern 1), then infers the inner type V. | |
| * | |
| * @template TPageData - The page data type containing the streaming object | |
| * @template TKey - The literal key type from the streaming object | |
| */ | |
| type StreamingReturnType< | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intended | |
| TPageData extends { streaming?: Record<string, any> }, | |
| TKey extends keyof NonNullable<TPageData['streaming']>, | |
| > = Promise< | |
| TKey extends string | |
| ? NonNullable<TPageData['streaming']>[TKey] extends Record<string, Promise<infer V>> | |
| ? V | |
| : NonNullable<TPageData['streaming']>[TKey] extends Promise<infer V> | |
| ? V | |
| : never | |
| : never | |
| >; | |
| /** | |
| * Internal implementation that accesses page.data and extracts the promise. | |
| * | |
| * Accesses SvelteKit's page store directly, allowing components at any nesting level | |
| * to fetch their data without receiving it as props from parents. | |
| * | |
| * Always returns a promise (even rejected ones) so components can use {#await} blocks without null checks. | |
| * | |
| * @template TPageData - The page data type | |
| * @template TKey - The literal streaming key type | |
| * @param streamingKey - The key in the streaming object | |
| * @param id - Optional ID for nested record patterns (e.g., creatorId) | |
| * @returns Promise of the data type, or rejected promise if not available | |
| */ | |
| function getStreamingPromiseImpl< | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intended | |
| TPageData extends { streaming?: Record<string, any> }, | |
| const TKey extends keyof NonNullable<TPageData['streaming']>, | |
| >(streamingKey: TKey, id?: string): StreamingReturnType<TPageData, TKey> { | |
| // | |
| // access the current page's data from SvelteKit's page store | |
| // we cast to TPageData to get type safety on the streaming object | |
| const pageData = page.data as unknown as TPageData; | |
| const streaming = pageData.streaming?.[streamingKey as string]; | |
| // if the streaming key doesn't exist, return a rejected promise | |
| // this ensures components always receive a promise (never null/undefined) | |
| // so they can use {#await} blocks without null checks | |
| if (!streaming) { | |
| return Promise.reject( | |
| new Error(`${String(streamingKey)} data not available`), | |
| ) as StreamingReturnType<TPageData, TKey>; | |
| } | |
| // handle nested record pattern: streaming.creatorMood[creatorId] | |
| // this is used when we have multiple promises keyed by ID (e.g., one per creator) | |
| if (id !== undefined) { | |
| // | |
| if (typeof streaming === 'object' && streaming !== null && id in streaming) { | |
| // | |
| const promise = (streaming as Record<string, Promise<unknown>>)[id]; | |
| if (promise) { | |
| return promise as StreamingReturnType<TPageData, TKey>; | |
| } | |
| } | |
| // if the ID doesn't exist in the record, return a rejected promise | |
| return Promise.reject( | |
| new Error(`${String(streamingKey)} data not available for ${id}`), | |
| ) as StreamingReturnType<TPageData, TKey>; | |
| } | |
| // when there's a single promise for the entire key, return it directly | |
| return streaming as StreamingReturnType<TPageData, TKey>; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment