Skip to content

chore(release): version packages#85

Merged
ABB65 merged 1 commit into
mainfrom
changeset-release/main
Jul 16, 2026
Merged

chore(release): version packages#85
ABB65 merged 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@contentrain/[email protected]

Major Changes

  • 173326c: feat(mcp)!: enforce the field constraints the schema already accepted

    A project reported that items, accept and maxSize are accepted on a field but
    never enforced — emails: ["not-an-email"] and accept: "image/jpeg" against a
    .webp both produced zero errors. The report was right, and the surface was larger
    than the three properties it named: 4 of 27 field types had any semantic
    validation
    , three constraints were read by nothing, and none of it blocked a write.

    A constraint that isn't a constraint is worse than no constraint — the author stops
    looking.

    content_save now validates before committing and refuses to write. It ran
    plan → commit → validate → report, so an invalid value landed in git, was
    auto-merged, and the caller learned about it from a string in next_steps while
    status still said "committed". Validation now runs on the pending changes and
    blocks on errors, returning isError and no commit. Warnings still pass — they are
    heuristics, and a legitimate value can sit outside an approximate pattern. Only the
    entries being saved are fatal: a pre-existing bad entry elsewhere in the model does
    not hold up an unrelated save.

    Array items share the scalar rule set. They ran through a parallel type switch
    that knew 10 of the 27 types and checked only typeof, so min/max/pattern/
    options never reached an item, and items given as a FieldDef with a non-object
    type ({type:'array', items:{type:'string', max:50}}) matched no branch at all —
    silently unvalidated, while the type emitter rendered it as real. Items now recurse
    through the same validator, which also closes the integer split where 3.7 was
    rejected inside an array but accepted as a scalar.

    17 types were pure typeof checks. slug now uses the SLUG_PATTERN the
    codebase already owned — every shipped template declares slug: { type: 'slug' },
    so "Hello World!!" used to validate clean. date/datetime are parsed (the same
    check schedule.ts already did for meta), percent is range-checked, and color/
    phone warn. Mechanical rules are errors; heuristics are warnings. email/url
    keep their existing warning severity. rating is deliberately untouched — its scale
    is never declared, so any range would be invented.

    unique works on documents. It was gated on a context only the collection
    validator passed, so it was a no-op exactly where every shipped template declares it.
    On singletons it is now rejected at model_save: the model holds one record per
    locale, so there is nothing to compare against.

    The dead constraints, handled honestly. accept is enforced by extension-sniff
    and says that is what it is. default is coherence-checked at model_save (right
    type, within its own options) but not written into content. maxSize cannot be
    enforced by MCP
    — it holds a path, never the bytes — so model_save now says so and
    points at the provider, which owns the policy at ingest. The docs claimed all three
    worked; they no longer do.

    model_save rejects what it will not enforce. options on a non-select, items
    on a non-array, accept/maxSize on a non-media field, min > max, and an
    uncompilable pattern are now errors instead of silent no-ops. Nested fields/
    items schemas are validated recursively — they were typed z.unknown() and never
    checked. The field schema is .strict(): a typo'd constraint (requird: true) used
    to be stripped without a word.

    BREAKING CHANGE:

    • content_save rejects content it previously committed. Run contentrain_validate
      before upgrading to see what would now be blocked.
    • model_save rejects models it previously accepted (unknown keys, min > max,
      options on a non-select, unique on a singleton).
    • validateModelDefinition returns { errors, warnings } instead of string[].
    • Array-item type errors carry validateFieldValue's message ("Type mismatch:
      expected string, got number") instead of "must be a string". The field path is
      unchanged.
    • Nested object errors are qualified by their parent (seo.title, not title) —
      a bare name was ambiguous with a top-level field.

    @contentrain/types gains validateSemanticType, validateAccept and
    isMediaType; validateFieldValue now applies semantic and accept rules.

    Studio picks all of this up automatically — its content-validation.ts delegates to
    this validator.

Patch Changes

@contentrain/[email protected]

Minor Changes

  • 173326c: feat(mcp)!: enforce the field constraints the schema already accepted

    A project reported that items, accept and maxSize are accepted on a field but
    never enforced — emails: ["not-an-email"] and accept: "image/jpeg" against a
    .webp both produced zero errors. The report was right, and the surface was larger
    than the three properties it named: 4 of 27 field types had any semantic
    validation
    , three constraints were read by nothing, and none of it blocked a write.

    A constraint that isn't a constraint is worse than no constraint — the author stops
    looking.

    content_save now validates before committing and refuses to write. It ran
    plan → commit → validate → report, so an invalid value landed in git, was
    auto-merged, and the caller learned about it from a string in next_steps while
    status still said "committed". Validation now runs on the pending changes and
    blocks on errors, returning isError and no commit. Warnings still pass — they are
    heuristics, and a legitimate value can sit outside an approximate pattern. Only the
    entries being saved are fatal: a pre-existing bad entry elsewhere in the model does
    not hold up an unrelated save.

    Array items share the scalar rule set. They ran through a parallel type switch
    that knew 10 of the 27 types and checked only typeof, so min/max/pattern/
    options never reached an item, and items given as a FieldDef with a non-object
    type ({type:'array', items:{type:'string', max:50}}) matched no branch at all —
    silently unvalidated, while the type emitter rendered it as real. Items now recurse
    through the same validator, which also closes the integer split where 3.7 was
    rejected inside an array but accepted as a scalar.

    17 types were pure typeof checks. slug now uses the SLUG_PATTERN the
    codebase already owned — every shipped template declares slug: { type: 'slug' },
    so "Hello World!!" used to validate clean. date/datetime are parsed (the same
    check schedule.ts already did for meta), percent is range-checked, and color/
    phone warn. Mechanical rules are errors; heuristics are warnings. email/url
    keep their existing warning severity. rating is deliberately untouched — its scale
    is never declared, so any range would be invented.

    unique works on documents. It was gated on a context only the collection
    validator passed, so it was a no-op exactly where every shipped template declares it.
    On singletons it is now rejected at model_save: the model holds one record per
    locale, so there is nothing to compare against.

    The dead constraints, handled honestly. accept is enforced by extension-sniff
    and says that is what it is. default is coherence-checked at model_save (right
    type, within its own options) but not written into content. maxSize cannot be
    enforced by MCP
    — it holds a path, never the bytes — so model_save now says so and
    points at the provider, which owns the policy at ingest. The docs claimed all three
    worked; they no longer do.

    model_save rejects what it will not enforce. options on a non-select, items
    on a non-array, accept/maxSize on a non-media field, min > max, and an
    uncompilable pattern are now errors instead of silent no-ops. Nested fields/
    items schemas are validated recursively — they were typed z.unknown() and never
    checked. The field schema is .strict(): a typo'd constraint (requird: true) used
    to be stripped without a word.

    BREAKING CHANGE:

    • content_save rejects content it previously committed. Run contentrain_validate
      before upgrading to see what would now be blocked.
    • model_save rejects models it previously accepted (unknown keys, min > max,
      options on a non-select, unique on a singleton).
    • validateModelDefinition returns { errors, warnings } instead of string[].
    • Array-item type errors carry validateFieldValue's message ("Type mismatch:
      expected string, got number") instead of "must be a string". The field path is
      unchanged.
    • Nested object errors are qualified by their parent (seo.title, not title) —
      a bare name was ambiguous with a top-level field.

    @contentrain/types gains validateSemanticType, validateAccept and
    isMediaType; validateFieldValue now applies semantic and accept rules.

    Studio picks all of this up automatically — its content-validation.ts delegates to
    this validator.

[email protected]

Patch Changes

@contentrain/[email protected]

Patch Changes

@netlify

netlify Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploy Preview for contentrain-ai ready!

Name Link
🔨 Latest commit 416f421
🔍 Latest deploy log https://app.netlify.com/projects/contentrain-ai/deploys/6a58ab2f5c67a80007db8a9f
😎 Deploy Preview https://deploy-preview-85--contentrain-ai.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@ABB65
ABB65 merged commit da9d5c6 into main Jul 16, 2026
4 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant