Repository navigation
feat(astro-renderer,core-ts): resolve reference and repeater fields (RFC-0045 step 3a) - #211
Merged
Merged
Conversation
…RFC-0045 step 3a)
- entryFields: reference cards (ReferencedEntry) by pickMember among the members the
view shows, depth 1 by default and 2 at most, breadth-first over a visited set of
groups (one groupMembers call per level), within the request's 200-target budget
(PlaceholderBudget.references, one console.warn); repeater items rendered like fields;
unresolved targets leave no id, title or empty item
- GraphSource.groupMembers / referencedBy / conversionEntries on both sources: the
snapshot answers from gate-independent indexes filtered per view (no store reads);
the live source reads entry_refs by (site_id, target_id) expanded by the shared rule
and resolves each candidate like the snapshot index
- core-ts: getGroupMembers and referrerCandidates (shared suite on SQLite, D1 and DO
SQLite), the conversion-map builder, resolveFields' optional conversion context
- EntryFields.astro: links for references, a nested dl per repeater item
- site Worker features: { gates: 2, fields: 2 }
- tests: live vs snapshot cards and referencedBy (editions, translations' own localized
values), gate instants, depth, cycles and budget, the route sweep extension, and the
frozen theme-v0.8.0 copy in old-theme-compat
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Madraka <[email protected]>
- reference cards carry their graph `path`; EntryFields links with it, so the locale middleware localizes it once (no /tr/tr/…, no /en/en/… under prefix-all); `url` stays the canonical - referencedBy: a total order (date, then id) in both sources, ORDER BY id in the candidate reads; a translation resolves with its original only while the view shows it (what its page reads through the gated entryById), without it otherwise - referrer records resolve through the conversion maps (snapshot index and live source) - live source: conversion maps memoized per collection (2 s) and loaded only from the collections the conversion reads; groupMembers and referencedBy pass the rows they read as #split's originals pool (no whole-site scan with expiry rules) - tests: links through localizeLinks under both routings with a fallback card, tied dates and limit, the gated-original case, the live shared = 2 path (both suites) - README: themes calling entryFields pass one placeholderBudget() per request; RFC: "Amended in 3a", and the step-4 live referencedBy note in watch list item 3 Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Madraka <[email protected]>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
RFC-0045 rollout step 3a: the renderer resolves reference and repeater fields (§4, §8;
docs/rfcs/0045-collections-stage-4-references-and-repeaters.md§9 step 3). The purge planner (3b) follows in a second PR. Both types stay refused on every production write path; the tests enable them withSiteGraphStoreOptions.fieldTypes, as 2a and 2b did.What changes
@cloudhal/astro-rendererreferences.ts(new):referenceCards: breadth-first over the groups a page's values name, with onegroupMemberscall per level and a visited set of group ids, so cycles cost nothing.pickMemberchooses among the members the view shows.PlaceholderBudget.references;placeholderBudget()includes it, and a theme's own{ left }object gets 200 on first use). Past the budget the rest resolve to nothing, with oneconsole.warnper request.ReferenceIndex: members and referrers by group over the snapshot's published entries. It is gate-independent, built lazily once per snapshot and isolate, and filtered per view:reachablefor resolution and originals,listedfor "used by".referrerRecordsandreferrersInare shared by both sources.reference-conversions.ts(new): the §7 conversion maps.fields.ts:RenderedField.references(ReferencedEntrycards) andRenderedField.items(repeater items rendered like fields; empty items left out);EntryFieldsOptions.depth.valuekeeps only the resolved group ids, and there is no title and no empty item.source.ts/live-source.ts:groupMembers,referencedByandconversionEntrieson both sources.getGroupMembersonce per level. ForreferencedByit reads theentry_refsrows by(site_id, target_id), expanded by thesharedrule (referrerCandidates). It then resolves each candidate with its original from the same pool, exactly as the snapshot index does, so both answer alike by construction.source.tsis 670 lines.EntryFields.astro: a reference renders as a link, withlangon a fallback-locale card and aul.ch-referenceswhen there are several. A repeater renders as anol.ch-itemsof nesteddls.GET /__cloudhal/featuresanswers{ "gates": 2, "fields": 2 }. Nothing passes the capability to a store until step 7.@cloudhal/core-tsreferences/reads.ts:getGroupMembersandreferrerCandidates. Both filter bysite_idand use no correlated outer references.references/conversion-maps.ts:conversionEntries,conversionTargets,conversionCollectionsandfoldText, one builder for every converter. Archived entries count nothing, because a converted value is a newly chosen target.resolveFields(site, entry, original, { entries })takes an optional fourth argument, andConvertContext.entriesis optional. Both changes are additive.Tests
astro-renderer/test/references.test.ts:default-with-notice;T+1h−1,T+1handT+1h+1,nextGate, and the ungated preview;groupMemberscalls, a cycle ending in a field-less card;groupMembers;en/tr/de× depth 1 and 2 gives identicalentryFields;referencedByqueries gives identical results;groupMembersis identical;sponsorowned by the Turkish translation), inherited values and a draft holder.references-sweep.test.ts, the route sweep extension (§8): a published referrer names a draft, an archived, an embargoed, anavailability.untiland ahide-expired target, plus one always shown. The sweep runs atT−1,T+1h−1,T+1handT+1h+1over every starter route, with entry routes now drawing fields at depth 2 and their "used by" list (sweep.ts, shared with the gate sweeps).membersInskip the gate filter fails this sweep and two cases inreferences.test.ts.core-ts/src/testing-reference-reads.ts, indescribeSiteGraphStore:getGroupMembersandreferrerCandidateson SQLite, Miniflare D1 and DO SQLite (6/6 green).cli/test/old-theme-compat.test.ts: the frozen copyfixtures/theme-v0.8.0(the v0.8.0 starter, unchanged since).placereference and alineuprepeater.EntryFields.astro; older themes show neither.Eventpreset, since it callswithStructuredData.cli/test/cloudflare-gates.test.ts: the features answer now includesfields: 2.Watch list
root_key; resolution then decides.Notes and deviations
pickMemberdoes not depend on order.Locally: lint, size check, typecheck (20/20) and every package's tests pass. Two
pnpm checkruns overlapped by accident, and in both, Studio's UI tests timed out at 20 to 90 s under the load. This PR changes nothing inapps/studioorpackages/ui, and the Studio suite run alone passes: 83 files, 1,339 tests. CI is the clean full run.Review fixes (
bc0fa663)path(the graph path), andEntryFields.astrolinks with it, solocalizeLinksadds the locale once; before, a Turkish page linked/tr/tr/….urlstays the canonical. Tests run the links throughlinkLocalizerandlocalizeLinksunder both routings, with a fallback card. Makingpaththe localized URL again fails them with/tr/tr/….referencedBy. It sorts by date, then id, and the candidate reads useORDER BY id. A test on a world with equal dates everywhere checks that both sources agree on order and thatlimitkeeps the same entries.referrerRecords/referrersIn. The test covers an embargoed original before and after it opens, on both sources:set_field_localizedtrue→false phase A and phase B, written directly);cloudhal devkeeps oneLiveSourceper process, so maps kept for its lifetime would never see edits.groupMembersandreferencedBypass the rows they read as#split's originals pool.placeholderBudget()per request inmedia.budget;cloudflare-entrydoesn't callentryFields. The README tells themes that callentryFieldsthemselves to do the same.shared = 2path is tested. A localized sponsor ontalkis inherited by a translation with none of its own, and not by one with its own value. It is in both the renderer's live-versus-snapshot comparison and the core suite (SQLite, D1, DO SQLite).The RFC gained an "Amended in 3a" note (
ReferencedEntry.path, andreferencedByfollowing each page).Local checks. No single
pnpm checkwas green, because of load on this machine:--continuetest run passed 17 of 19 packages;packages/commentsfailed twice in the D1 tests withfetch failedduring a migration, then passed in the--continuerun and on its own.🤖 Generated with Claude Code