Repository navigation
fix(blocks): infer the sole allowed type when _type is omitted - #3825
khoinguyenpham04 wants to merge 3 commits into
Conversation
🦋 Changeset detectedLatest commit: 37a8804 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
docs | 37a8804 | Oct 08 2026, 01:30 PM |
There was a problem hiding this comment.
This PR fixes the right problem in the right places. Inferring the single allowed block type on ordinary writes is a small, backwards-compatible change that matches the documented intent of single-type blocks fields. The server normalization in block-values.ts and the client-side Markdown-to-Portable-Text conversion in client/portable-text.ts both handle the omitted _type case, and the safeguards described in the PR (multi-type fields, restores, retired/different stored types, explicit invalid types) are all preserved by the implementation.
I checked:
packages/core/src/schema/block-values.ts—normalizeBlockArraynow infers the sole allowed type before validation, while still requiring an explicit_typefor restores, multiple allowed types, and existing blocks whose stored type differs.packages/core/src/client/portable-text.ts—convertNestedBlockPortableTextinfers the sole allowed type on writes so nested Markdown conversion works when_typeis omitted.packages/core/src/client/index.ts— the field-schema cache now retainsvalidation/blockTypes, so both preloaded and on-demand schema fetches have the metadata needed for client-side conversion.- Tests — new regression coverage spans SQLite/PostgreSQL create/update, identity preservation, restore refusal, retired-type refusal, nested Markdown via MCP, and both cold/preloaded client caches.
- Docs and changeset — the
field-types.mdxaddition accurately describes the new behavior, and the changeset explains the released behavior for package consumers.
No blocking issues found. The change is clean, well-tested, and consistent with EmDash conventions.
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-test
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-loader
@emdash-cms/registry-moderation
@emdash-cms/registry-verification
upgrade-emdash
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
74c0eef to
ac9976c
Compare
ac9976c to
66b9014
Compare
66b9014 to
6d8d813
Compare
Seed application is the only write path that uses restoreBlocks, so readers could not map "restoring block values" to anything. Also note that adding a second allowed type breaks writes that omit _type.
Inference runs before any SQL and is covered on SQLite and PostgreSQL by the integration suite; the D1 variant only re-indented the existing test.
What does this PR do?
Fixes content writes to a blocks field with exactly one allowed type rejecting a missing
_type. Ordinary writes infer the allowed type, including nested Markdown conversion over MCP and the TypeScript client. Both client schema-cache paths retain the metadata needed for conversion.The change preserves the existing safeguards: explicit null/empty/wrong types remain errors, fields with multiple allowed types require an explicit type, restores require an explicit type, and a stored block of a different type cannot silently switch to the newly allowed type. Keys, versions, defaults, and replacement intent retain their existing behavior.
Nine new regression cases failed before this fix; 125 controls passed. Added coverage includes actual SQLite/PostgreSQL content and MCP writes, persisted identity, restore/retired-type refusal, nested Markdown, cold/preloaded client caches, and inferred D1 writes. The field reference and package changeset describe the accepted input.
This is the second layer of the two-PR stack, based on PR #3824 (
noah/mcp-blocks/omitted-fields). Merge #3824 first. Reported directly; no linked issue number.Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.Translation and screenshot items are not applicable: this changes no admin UI or user-visible UI strings. The changeset applies to the published
emdashpackage.AI-generated code disclosure
gpt-6.1-sol); independent adversarial review with GPT-5.6 Sol (gpt-5.6-sol,xhigh).Screenshots / test output
Not applicable: no UI changes.
pnpm format, rootpnpm build,pnpm lint:quick,pnpm lint, andpnpm typecheckpass.pnpm --silent lint:json | jq '.diagnostics | length':0.pnpm --dir docs build, before the read-side follow-up: passes.xhigh) review of this layer and the combined stack: clean, with no findings rejected or nits left.