Skip to content

Cloudhal 0.6.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 13:26
· 49 commits to main since this release
Immutable release. Only release title and notes can be modified.
689ff48

All @cloudhal/* packages and create-cloudhal at 0.6.0.

Changes

  • Collections stage 3 contract and pure core (RFC-0042 step 1, ADR-0044). CollectionSettings gains optional listFields (up to 4 field keys), expiry ({ field, action: "unlist" | "hide", afterMinutes? }) and structuredData ({ type: "Event" | "JobPosting" | "Product" | "Recipe", properties }, each property sourced by kind: field, entry, site or a plain-text text constant); contentList sections gain where (up to 4 FieldConditions) and orderBy (FieldOrder), both new in common.schema.json. New @cloudhal/core-ts/structured-data: the closed preset table, checkStructuredData (save-time mapping check) and structuredDataFor (an entry's JSON-LD, none when a required value is missing, capped at 32 KB). @cloudhal/core-ts/fields adds the listing evaluator (checkFieldQuery, parseFieldQuery, matchesFieldQuery, compareByField) and the derived expiry gate (fieldGateInstant, expiryInstant, effectiveUntil, dayStartIn). Nothing reads them yet: no behaviour change.
  • Collections stage 3 in the renderer (RFC-0042 step 3, ADR-0044). A collection's expiry gives every entry a derived gate (min(availability.until, field date + grace); a date ends at the next day's start in the site's zone), applied by both published sources at render time: unlist keeps the entry's page but leaves entries(), allEntries(), term filters and archives, related posts, feeds, the sitemap, llms.txt and search (search rows' gateUntil); hide answers 404 like an expired availability. The gate index adds every derived instant and every value instant of date/datetime list fields, so the edge cache expires there without a purge; previews ignore derived gates and say "Ended on …". New theme APIs: GraphSource.queryEntries and queryEntries(source, q) (./queries) list a collection by its list fields (where, orderBy, sort, filter, limit, offset, includeExpired) with one evaluator on live and snapshot sources; contentListEntries; GraphSource.routableEntries (entries with a public URL, unlisted included); withStructuredData and entryStructuredData (./seo) replace the generic BlogPosting with the collection's JSON-LD preset when the entry has every required value. ContentList.astro honours where/orderBy, also for theme copies that pass a source's list. entryHead is unchanged, so old themes keep today's output; the blog starter calls withStructuredData and contentListEntries. The site Worker's GET /__cloudhal/features answers { "gates": 2 }, and writers (store, API, MCP, bundle import) refuse a new or changed expiry with 409 gates_unsupported until it does (GateSupport.enforced(siteId, version), EXPIRY_GATES_VERSION). A live entry whose published change adds a derived gate or moves it earlier is never low risk in auto mode (expiringEntries, auto-check code expires, for publishes and releases). Sites without the new settings render as before.
  • Collections stage 3 in the store and the API (RFC-0042 step 2, ADR-0044). updateSite checks the new collection settings when they change: listFields and expiry.field name fields of the collection (unknown_field), the expiry field is a date or datetime field (invalid_expiry), and a structured-data preset passes checkStructuredData (structured_data_invalid; warnings are not refused). Unlisting, dropping or retyping a list field, or dropping a select option, that contentList sections still filter or sort by is refused as list_field_in_use naming the pages, kept true by a guard on the site CAS. Page writes check changed contentList where/orderBy against the collection's list fields (invalid_query, with sections/<i>/… paths). Field operations follow: rename_field renames the field in listFields, expiry, structured-data sources and sections' queries in phase A's batch (and back on revert); remove_field refuses while any of them names the field; change_field_type refuses when the new type would break them. New queryContentEntries (and applyFieldQuery) filters and orders a collection's entries by list fields with the shared evaluator over resolved values. GET /api/sites/:id/entries takes where (JSON), orderBy (JSON), limit (1–100) and cursor; with limit it answers { entries, total, nextCursor? }, without it the plain array as before. New error codes invalid_query, structured_data_invalid, invalid_expiry (422) and list_field_in_use (409). Sites without the new settings behave as before.

Patch Changes

  • Typed fields follow-ups (RFC-0041). One asset check for field values, fieldAssetIssues in @cloudhal/core-ts/media (with fieldAssetRefs and newFieldAssetRefs), now used by both the store's entry write and MCP's create_entry/update_entry precheck; same codes and messages. Machine translation now follows §5: get_translation_source returns only the localized text and rich-text field values to translate (fields) and names the shared ones (sharedFields); save_translation takes translated fields, refuses shared ones in precheck with field_not_localized (paths in details.issues), and no longer copies the source's field values into a new translation in the same site. New helpers in @cloudhal/core-ts/fields: localizedFieldValues, translatableFieldValues, sharedFieldKeys, sharedFieldIssues. Studio's "write by hand" draft copies only localized field values.
  • Per-collection feeds (RFC-0042 step 6, ADR-0044). Each collection with a permalink prefix has an RSS feed at <prefix>/rss.xml (and /<locale><prefix>/rss.xml or the locale's domain): its newest listed entries, so expiry and time gates apply as in the site feed, with the same cache tag and edge-cache cap; feed: false answers 404. The cloudhal() integration serves it from a middleware on every target, only where the theme's own routes answered 404, so a theme's own route wins and old theme copies gain the feeds; static builds write them because staticPaths().feeds now lists every collection feed path for the starter's [...path]/rss.xml route. New theme API collectionFeedLink(site, collection, origin, locale?) (./seo, ./collection-feeds) gives the <link rel="alternate">; the blog starter adds it on entry pages and on pages that list a collection. Previews now list contentList sections with where/orderBy through the same evaluator as the site (renderSectionHtml's new optional listing argument, passed by renderPagePreview), so an entry without the field's value no longer shows under "upcoming". New guide: docs/guides/structured-data.md.
  • Collections stage 3 in the renderer (RFC-0042 step 3, ADR-0044). A collection's expiry gives every entry a derived gate (min(availability.until, field date + grace); a date ends at the next day's start in the site's zone), applied by both published sources at render time: unlist keeps the entry's page but leaves entries(), allEntries(), term filters and archives, related posts, feeds, the sitemap, llms.txt and search (search rows' gateUntil); hide answers 404 like an expired availability. The gate index adds every derived instant and every value instant of date/datetime list fields, so the edge cache expires there without a purge; previews ignore derived gates and say "Ended on …". New theme APIs: GraphSource.queryEntries and queryEntries(source, q) (./queries) list a collection by its list fields (where, orderBy, sort, filter, limit, offset, includeExpired) with one evaluator on live and snapshot sources; contentListEntries; GraphSource.routableEntries (entries with a public URL, unlisted included); withStructuredData and entryStructuredData (./seo) replace the generic BlogPosting with the collection's JSON-LD preset when the entry has every required value. ContentList.astro honours where/orderBy, also for theme copies that pass a source's list. entryHead is unchanged, so old themes keep today's output; the blog starter calls withStructuredData and contentListEntries. The site Worker's GET /__cloudhal/features answers { "gates": 2 }, and writers (store, API, MCP, bundle import) refuse a new or changed expiry with 409 gates_unsupported until it does (GateSupport.enforced(siteId, version), EXPIRY_GATES_VERSION). A live entry whose published change adds a derived gate or moves it earlier is never low risk in auto mode (expiringEntries, auto-check code expires, for publishes and releases). Sites without the new settings render as before.

Patch Changes

  • Collections stage 3, step 4 (RFC-0042 §7 / ADR-0044): MCP.

    • list_collections returns, per collection, its listFields with each field's type, operators (ops) and whether it sorts, and, for a collection with a JSON-LD preset, structuredData: the type, every path it offers (required ones, "at least one of" groups, defaults, the field types, entry and site properties, constants and vocabulary each takes) and the preset's warnings.
    • update_collection takes listFields, expiry and structuredData (null removes one). The store's dry run checks them in precheck, so unknown_field, invalid_expiry, structured_data_invalid, list_field_in_use and gates_unsupported refuse the call, with paths, before a person is asked. The approval says what changes in words ("Events leave listings, feeds, the sitemap and search (their pages stay) 1 hour after "Ends at" (endsAt); 2 published entries are already past it", "structured data: Event, maps startDate ← field startsAt, …", vocabulary warnings), and the answer lists the preset's warnings. Policy is unchanged (site:write, a person holding site:settings approves in every mode).
    • list_entries takes where, orderBy, limit and cursor, run by core-ts's shared evaluator (queryContentEntries); with limit it answers total and nextCursor. Invalid queries are refused as invalid_query with their paths.
    • create_page / update_page check a changed contentList section's where/orderBy in precheck (invalid_query, sections/0/where/1/value).
    • New read-only tool preview_structured_data (content:read): the JSON-LD an entry's page emits under its collection's preset, built by the renderer's entryStructuredData, with the issues (no preset, a required value missing, over 32 KB) and the preset's warnings. 109 → 110 tools.
    • Hints for invalid_query, structured_data_invalid, invalid_expiry, list_field_in_use and gates_unsupported (expiry and availability alike), and a "Listings, expiry and structured data" paragraph in the server instructions.
    • @cloudhal/astro-renderer: new subpath ./structured-data (entryStructuredData, withStructuredData), so tools use it without the package root.
  • Collections stage 3 in the store and the API (RFC-0042 step 2, ADR-0044). updateSite checks the new collection settings when they change: listFields and expiry.field name fields of the collection (unknown_field), the expiry field is a date or datetime field (invalid_expiry), and a structured-data preset passes checkStructuredData (structured_data_invalid; warnings are not refused). Unlisting, dropping or retyping a list field, or dropping a select option, that contentList sections still filter or sort by is refused as list_field_in_use naming the pages, kept true by a guard on the site CAS. Page writes check changed contentList where/orderBy against the collection's list fields (invalid_query, with sections/<i>/… paths). Field operations follow: rename_field renames the field in listFields, expiry, structured-data sources and sections' queries in phase A's batch (and back on revert); remove_field refuses while any of them names the field; change_field_type refuses when the new type would break them. New queryContentEntries (and applyFieldQuery) filters and orders a collection's entries by list fields with the shared evaluator over resolved values. GET /api/sites/:id/entries takes where (JSON), orderBy (JSON), limit (1–100) and cursor; with limit it answers { entries, total, nextCursor? }, without it the plain array as before. New error codes invalid_query, structured_data_invalid, invalid_expiry (422) and list_field_in_use (409). Sites without the new settings behave as before.

  • Collections stage 3, step 5 (RFC-0042 §7 / ADR-0044): Studio.

    • Collection settings get three cards, each saved on its own: Listing fields (up to four fields, ordered; a field content lists still use names those pages), Expiry (a date or date-and-time field, "Leave lists" or "Hide the page" explained in words, a grace in minutes, and how many published entries are already past it; a site Worker that does not apply expiry yet says to deploy first) and Structured data (Event, Job posting, Product or Recipe; every property offers only the sources that fit it, required ones are marked, problems show while editing, and a chosen entry's JSON-LD is previewed with a link to Google's Rich Results Test).
    • Create listing page makes a draft page at the collection's address with a content list of it (upcoming first for a collection with a date listing field and an expiry) and opens it; when a page already lives there, the action opens that page.
    • Collection lists show listing fields as columns, filter by them and sort by number and date fields. Filters and sort live in the URL and ask the API by field, one page at a time.
    • Page editor: content list sections get conditions and an order by the collection's listing fields; refused conditions show on their row.
    • Words for invalid_query, structured_data_invalid, invalid_expiry, list_field_in_use and gates_unsupported, the preview_structured_data tool and the expiry auto-publish check. Translations are AI drafts awaiting review.
    • @cloudhal/api: new read-only GET /api/sites/:siteId/entries/:entryId/structured-data (content:read), the REST twin of MCP's preview_structured_data: the preset document an entry's page emits, its issues and the preset's warnings.

Patch Changes

Upgrade

npm install @cloudhal/cli@0.6.0 @cloudhal/core-ts@0.6.0 @cloudhal/astro-renderer@0.6.0 @cloudhal/cloudflare@0.6.0
npx cloudhal deploy

Docs: https://cloudhal.dev/docs/start/installation#upgrading