# CLI and CMA Improvements: cma:script, Stricter Types, and Dastdown

[date: 2026-05-18T15:41:44.751+02:00]

The last couple of weeks brought a set of converging improvements across the CLI, the JS CMA client, and the structured-text packages.

#### `datocms schema:generate` now exposes runtime ID and REF constants

The schema types generator now emits `Schema.Article.ID` and `Schema.Article.REF` alongside the existing TypeScript types. No more hardcoded item-type ID strings drifting out of sync across environments!

```typescript
// type position: the model's TS shape, as before
const article = await client.items.find<Schema.Article>(id);

// value position: the model's id and ref, generated from your project
await client.items.create({
  item_type: Schema.Article.REF,
  // …
});

if (item.relationships.item_type.data.id === Schema.Article.ID) {
  // …
}
```

#### New `FieldValue<T, K>` helpers

Three new helpers let you derive a field's type directly from an existing record or block for the full read/write cycle:

-   `FieldValue<T, K>` — standard response shape
-   `FieldValueInNestedResponse<T, K>` — what you get when reading with `nested: true`
    
-   `FieldValueInRequest<T, K>` — what `client.items.create` / `update` expects
    

`T` accepts anything item-shaped like a fetched record, a nested block, or an `ItemTypeDefinition` directly. The typical flow this unlocks: read a record with `nested: true`, iterate and mutate its nested blocks, and write the result back. Typing the accumulator takes one line:

```typescript
const page = await client.items.find<LandingPage>(id, { nested: true });
const sections: FieldValueInRequest<typeof page, 'sections'> = [];
```

#### New `isBlockWithItemOfType` predicate

`isBlockWithItemOfType` (and its `isInlineBlockWithItemOfType` counterpart) from `datocms-structured-text-utils` narrows a block node to a specific model shape using its item type ID. Works in `findFirstNode`, `mapNodes`, or standard conditionals, giving you typed access to `.item.attributes` directly without extra casting.

```typescript
// inline guard
if (isBlockWithItemOfType(Schema.CtaBlock.ID, node)) {
  // node.item.attributes is now typed as CtaBlock
}

// curried predicate
const firstCta = findFirstNode(content, isBlockWithItemOfType(Schema.CtaBlock.ID));
```

#### Edit Structured Text as plain text with `datocms-structured-text-dastdown`

The new `datocms-structured-text-dastdown` package gives you a lossless, markdown-flavored serialization for DatoCMS Structured Text documents. Serialize to plain text, edit it however you like (string manipulation, regex, LLM rewrite), and parse back instead of walking the AST.

Best fit: text-heavy content like articles, docs, and chapters where edits are textual and may cross node boundaries. Not for landing pages made of opaque blocks — referenced blocks stay opaque in the serialized form, so you can move or remove them but not edit their internals at this layer.

A typical round-trip would look like:

```typescript
import { parse, serialize } from 'datocms-structured-text-dastdown';

const cur = await client.items.find<Schema.Article>('article-id', { nested: true });

const text = serialize(cur.body);
const edited = text.replace(/Acme Corp/g, '**Acme Inc.**');
const body = parse(edited, cur.body);

await client.items.update<Schema.Article>('article-id', { body });
```

#### `mapNodes` now supports full tree rewrites

`mapNodes` and `mapNodesAsync` from `datocms-structured-text-utils` now support structural transforms, not just 1:1 node mapping. The return value controls what happens:

-   Return a single node: direct replacement
-   Return an array: splat into the parent's children
    
-   Return `null` or `undefined`: remove the node entirely
    

#### New agent-targeted commands

We've also shipped `datocms cma:script`, `datocms schema:inspect`, and enhanced `datocms cma:docs` - commands designed to be used by your agents directly. These are documented in the CLI repo and will evolve as agent tooling matures.

---

To update: `npm i -g datocms` for CLI changes, `npm i @datocms/cma-client-node@latest` for client changes, `npm i datocms-structured-text-dastdown` for the new package.