Skip to content

Cloudhal 0.5.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 05:34
· 71 commits to main since this release
Immutable release. Only release title and notes can be modified.
01a685b

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

Changes

  • Typed custom fields, step 1 (RFC-0041 / ADR-0043). Site.collections.<key>.fields takes an ordered list of 1–30 field definitions in nine types (text, richText, number, boolean, date, datetime, url, image, select), discriminated by type, each with per-locale labels, localized and (except boolean) requiredToPublish. The new @cloudhal/core-ts/fields subpath (Worker-safe, eval-free) checks definitions (reserved keys, unique keys and options, 30 per collection, 200 per site, 256 KB of collection settings, the plain-edit guard), checks values through ADR-0030's validateProps (only changed values; unknown keys refused once a collection has definitions; 256 KB per entry), checks requiredToPublish, emits the JSON Schema agents receive (strict-mode safe), and resolves values across translations (resolveFields). Nothing reads it yet: the store, API, MCP, Studio and renderer adopt it in later steps, so nothing changes for existing sites.

  • Typed custom fields, step 3 (RFC-0041 §6 / ADR-0043): field operations.

    • runFieldOperation / continueFieldOperation / revertFieldOperation / fieldOperationStatus in @cloudhal/core-ts run rename_field, change_field_type, set_field_localized and remove_field: a dry run (entries touched, values moved, converted, cleared, removed, unchanged and unconvertible, per locale, with samples), a before … snapshot whose id is the operation id, the new definition with its migration marker (phase A), entries rewritten in guarded batches of 200 that resume after an interruption (phase B), the marker dropped once nothing is pending (phase C) and an after snapshot. Only the draft graph changes. A type change follows a closed conversion table (convertFieldValue, exported from @cloudhal/core-ts/fields) and refuses values it cannot convert (field_values_unconvertible) unless onInvalid: "clear". One operation per collection at a time (operation_in_progress). Reverting restores the earlier definition and the entries nobody edited since, and converts edited ones back.
    • Readers stay consistent mid-run: resolveFields converts values still in the old type, and a field being removed reads as gone; a person's entry write during an operation goes through the marker, so edits and the operation converge.
    • Translations: changing only non-localized field values of an original no longer marks its translations stale (their recorded source hash follows, in the same batch); operations keep fresh translations fresh.
    • API: POST /api/sites/:id/collections/:key/fields/operations (dryRun, ?continue=<id>), GET …/:operationId, POST …/:operationId/revert (site:settings and content:write), and the same under /api/admin with the deployment token.
    • CLI: cloudhal fields rename|retype|localize|remove|continue|revert [--dry-run] [--remote]; the dry run also lists theme files that read the field directly.
  • Typed custom fields, step 6 (RFC-0041 §9 / ADR-0043): renderer and themes. Additive only: entry.fields still means exactly what is stored, and themes copied before this release build and render as before (a frozen 0.4.0 theme joins the compat test).

    • GraphSource.entryFields(entry, options?) on live and snapshot sources (and entryFields(source, entry, options?) from @cloudhal/astro-renderer/fields for any source): an entry's typed fields as a theme draws them, by key in definition order. Values are resolved by the language rules (a translation's empty localized field reads the original's, fromOriginal; non-localized fields come from the translation group's original; language editions own their values), labelled in the page's language and made safe: rich text as HTML from the article body's markdown pipeline (raw HTML escaped, safeHref on links), URLs through safeHref (dropped when unsafe), images as <img> attributes with the asset's alt in the page's language, everything else as formatted text. Also renderFields, hasShownFields, and resolveFields, formatFieldValue, fieldLabel, fieldHelp, optionLabel re-exported.
    • formatFieldValue(value, def, { locale, defaultLocale?, timeZone?, words? }) in @cloudhal/core-ts/fields: a value as plain text with Intl (numbers, dates that never shift a day, instants in their own zone, option labels as a list, yes/no words).
    • EntryFields.astro in @cloudhal/astro-renderer/components: a plain, unstyled <dl class="ch-fields"> of the fields that have a value, with data-field / data-type hooks for themes; nothing at all when none has one.
    • Blog starter: articles of collections with fields show a "Details" list under the cover (English, Turkish and Japanese words). Entries of collections without definitions render byte for byte as before.
    • New guide: docs/guides/fields.md (defining fields in Studio, MCP, the API and the CLI; operations; languages; theme API; security).
  • Typed custom fields, step 2 (RFC-0041 / ADR-0043): the store and the API enforce field definitions.

    • Site writes (updateSite, createSite, PUT /api/sites/:id/collections/:key, dry run included) check definitions: reserved (field_key_reserved) and duplicate keys or options, minimum above maximum, 30 fields per collection and 200 per site (too_many_fields), 256 KB of collection settings (too_large). A plain edit never loses a value: changing a field's type, localized or multiple, or dropping a key or a select option that an entry still holds, is refused with 409 field_change_needs_operation, naming the field operation to use. Other site writes do not re-run the checks.
    • Entry writes check only the field values that changed: 422 invalid_fields, unknown_field (a new key without a definition, once the collection has definitions; stored unknown keys are kept), field_not_localized (a translation's own copy of a non-localized value) and required_field (review or published without a requiredToPublish value; drafts save freely). fields is capped at 256 KB per entry in every collection when it changes (too_large). Image values must be image assets of the site, and image and rich-text asset references are recorded in asset_refs under fields.<key>, so a used file cannot be deleted. Every refusal lists its problems with paths in details.issues (fields/startsAt/at).
    • API inputs remove a key with null in a collection that has definitions. Collections without definitions keep fields as any JSON, exactly as before.
    • Search: text and richText fields marked searchable add their words to the entry's search text at the next publish (off by default; sites without searchable fields index byte for byte as before).
    • refuseFieldIssues and fieldRefusalCode are exported from @cloudhal/core-ts/fields.

Patch Changes

  • Typed custom fields, step 6 (RFC-0041 §9 / ADR-0043): renderer and themes. Additive only: entry.fields still means exactly what is stored, and themes copied before this release build and render as before (a frozen 0.4.0 theme joins the compat test).

    • GraphSource.entryFields(entry, options?) on live and snapshot sources (and entryFields(source, entry, options?) from @cloudhal/astro-renderer/fields for any source): an entry's typed fields as a theme draws them, by key in definition order. Values are resolved by the language rules (a translation's empty localized field reads the original's, fromOriginal; non-localized fields come from the translation group's original; language editions own their values), labelled in the page's language and made safe: rich text as HTML from the article body's markdown pipeline (raw HTML escaped, safeHref on links), URLs through safeHref (dropped when unsafe), images as <img> attributes with the asset's alt in the page's language, everything else as formatted text. Also renderFields, hasShownFields, and resolveFields, formatFieldValue, fieldLabel, fieldHelp, optionLabel re-exported.
    • formatFieldValue(value, def, { locale, defaultLocale?, timeZone?, words? }) in @cloudhal/core-ts/fields: a value as plain text with Intl (numbers, dates that never shift a day, instants in their own zone, option labels as a list, yes/no words).
    • EntryFields.astro in @cloudhal/astro-renderer/components: a plain, unstyled <dl class="ch-fields"> of the fields that have a value, with data-field / data-type hooks for themes; nothing at all when none has one.
    • Blog starter: articles of collections with fields show a "Details" list under the cover (English, Turkish and Japanese words). Entries of collections without definitions render byte for byte as before.
    • New guide: docs/guides/fields.md (defining fields in Studio, MCP, the API and the CLI; operations; languages; theme API; security).

Patch Changes

  • Typed custom fields, step 4 (RFC-0041 §8 / ADR-0043): MCP.

    • list_collections returns each collection's field definitions (fields) and fieldsSchema, the JSON Schema of an entry's values with labels and help in the asked locale (default: the site's).
    • create_entry / update_entry check field values in precheck with the store's checker (changed keys only, unknown keys, a translation's copy of a non-localized value, requiredToPublish for review and published, image and asset: references), so a refused call never reaches a person; each issue keeps its path (fields/startsAt/at). update_entry takes fields as a merge patch (null removes a key), and its approval names typed changes readably ("Starts at (startsAt) → 5 Nov 2026 19:30 Istanbul", option labels, values quoted and truncated).
    • update_collection takes fields (the whole list) for free definition edits; changes that could lose values are refused with field_change_needs_operation, naming the operation tool.
    • New tools rename_field, change_field_type, set_field_localized, remove_field (site:write + content:write): always a person holding site:settings, in every mode including bypass; the approval shows the dry run (entries touched, values moved, converted or cleared, samples), dryRun: true reports without asking, and a call that would clear values without onInvalid: "clear" is refused before anyone is asked. A run that stops at its budget answers done: false with an operationId; calling the same tool again with it continues the operation after a new approval.
    • Hints for every field error code, and server instructions telling agents to read fieldsSchema before writing entries and to use the operation tools for risky changes.

Patch Changes

  • Typed custom fields, step 5 (RFC-0041 §7 / ADR-0043): Studio.

    • Entry editor › Fields: collections with field definitions get a "Fields" card in the entry editor (loaded only for them), one control per type — text, rich text (the body editor), number, yes/no, date, date and time in its own zone, link, image (the media picker) and choice (one or several). Labels and help follow the Studio language, required fields say "Needed to publish", and the API's issues show on their field. On a translation, shared (non-localized) fields are read-only with the original's value and a link to it; empty per-language fields show the original's value as their placeholder. Values save with the entry.
    • Collection settings › Fields: a builder to add fields (the key follows the label until saved), reorder them and edit labels, help, limits and options, saved together. Renaming, changing a type or the languages of a saved field, and removing one open a dialog that runs the dry run first (entries changed, values moved, converted or cleared, per language, examples, a note about themes), needs confirmation — and an explicit opt-in to clear values that cannot be converted — then runs the operation to the end and offers Revert. A running operation shows its progress and can be continued. Only people who may change site settings edit fields; others see them read-only.
    • Activity and Approvals name the collection and field tools, field operations that convert or remove values ask for confirmation before approval, and every field error code reads as a sentence in the Studio language. New words live in a lazy fields namespace (drafts in every language, awaiting review).

Patch Changes

  • Studio (RFC-0040 §4): a collection's settings get a Comments section (saved in the site's comment settings), the site overview lists recent content across every collection, and entries open at /sites/<id>/collections/<collection>/<entry> — the old /articles/<entry> links redirect there. The command palette goes to each collection.

Upgrade

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

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