diff --git a/apps/web/src/lib/types.ts b/apps/web/src/lib/types.ts index 66b4d24..1f0b2f5 100644 --- a/apps/web/src/lib/types.ts +++ b/apps/web/src/lib/types.ts @@ -1,7 +1,6 @@ import type { Blob } from '@atcute/lexicons'; import type { AppBskyActorDefs } from '@atcute/bluesky'; -import type { Node } from '@blento/schema'; -import type { ResolveResult } from '@blento/sources'; +import type { ResolvedNode } from '@blento/sources'; export type Item = { id: string; @@ -99,8 +98,9 @@ export type WebsiteData = { sections: SectionRecord[]; - /** The node graph (migrate-on-read). Source of truth going forward; render still uses cards/sections. */ - nodes?: Node[]; + /** The node graph (migrate-on-read). Source of truth going forward; render still uses cards/sections. + * Each node carries its resolved `source` data on `node.loaded` (runtime-only, never persisted). */ + nodes?: ResolvedNode[]; /** True when `nodes` came from stored app.blento.node records (page already migrated), vs rebuilt * on read from legacy card/section. Save uses this to know whether to retire the legacy records. */ @@ -108,10 +108,6 @@ export type WebsiteData = { additionalData: Record; - /** Resolved `node.source` data, keyed by node id (== card id). Runtime-only, never persisted; - * renderers read their node's loaded data here. See `@blento/sources`. */ - loaded?: Record; - updatedAt: number; version?: number; diff --git a/apps/web/src/lib/website/data/load.ts b/apps/web/src/lib/website/data/load.ts index 0ddda64..2776672 100644 --- a/apps/web/src/lib/website/data/load.ts +++ b/apps/web/src/lib/website/data/load.ts @@ -10,7 +10,8 @@ import { type Source, type SourceContext, type CacheAdapter, - type ResolveResult + type ResolveResult, + type ResolvedNode } from '@blento/sources'; import { error } from '@sveltejs/kit'; import type { ActorIdentifier, Did } from '@atcute/lexicons'; @@ -350,8 +351,9 @@ export async function loadData( : buildGraph(sectionRecords, cards, fullPage, { order: 'input' }); // Declarative-source path: stamp/keep node.source, then resolve the graph by node id via - // @blento/sources. Loaded data travels with the node (result.loaded[nodeId]); its cards skip the - // bespoke loadAdditionalData below. Only the source spec is persisted — loaded is runtime-only. + // @blento/sources. Loaded data is attached onto the nodes themselves (node.loaded); sourced cards + // skip the bespoke loadAdditionalData below. Only the source spec is persisted — loaded is + // runtime-only (schema's allowlist serialize drops it). stampNodeSources(graph); const sourcedIds = new Set(graph.filter((n) => n.source).map((n) => n.id)); const loadedPromise = loadNodeSources(graph, { did, cache }); @@ -383,11 +385,15 @@ export async function loadData( }); // Attach the canonical node graph for downstream consumers: the stored records when present, // else the migrate-on-read graph rebuilt from the post-collision-fix state. - result.nodes = nodesFromRecords.length + const nodes: ResolvedNode[] = nodesFromRecords.length ? nodesFromRecords : buildGraph(result.sections, result.cards, fullPage, { order: 'input' }); + // Hang the resolved data on each node (keyed by id) — renderers get source + loaded on one node. + for (const node of nodes) { + if (node.id in loaded) node.loaded = loaded[node.id]; + } + result.nodes = nodes; result.migratedStorage = nodesFromRecords.length > 0; - result.loaded = loaded; return result; } diff --git a/apps/web/src/lib/website/view/ContextProvider.svelte b/apps/web/src/lib/website/view/ContextProvider.svelte index 8761e00..bf306e9 100644 --- a/apps/web/src/lib/website/view/ContextProvider.svelte +++ b/apps/web/src/lib/website/view/ContextProvider.svelte @@ -24,8 +24,14 @@ // svelte-ignore state_referenced_locally setAdditionalUserData(data.additionalData); + // Loaded data lives on each node (node.loaded); expose an id→loaded lookup for the Item-based + // render path. // svelte-ignore state_referenced_locally - setLoadedData(data.loaded ?? {}); + setLoadedData( + Object.fromEntries( + (data.nodes ?? []).filter((n) => n.loaded != null).map((n) => [n.id, n.loaded!]) + ) + ); setCanEdit( () =>