Localess

Schema

Define content schemas in TypeScript and sync them with your Localess space.

Define your schemas as TypeScript in your own repository, then validate, compare, and push them to a Localess space. Schemas become reviewable code with a history, and a new environment can be provisioned from a git clone plus one push instead of being clicked together by hand.

This is the inverse of type generate, which reads schemas out of the CMS. Pick one direction: either the CMS is the source of truth and your types follow it, or your code is the source of truth and the CMS follows it.

Requirements

Schema definitions are authored with the @localess/schema package:

npm install @localess/schema@latest -D

See Schemas as Code for the authoring API — defineSchema, defineEnum, defineField, defineConfig, and type inference. This page covers only the commands that move those definitions between your repository and a space.

Your API token must have Development Tools permission enabled in Localess Space settings — the same permission type generate needs. There is no separate schema permission.

The entry file

Every command that reads your definitions takes an entry file: a TypeScript or JavaScript file exporting the result of defineConfig(). The convention is schemas/index.ts.

// schemas/index.ts
import { defineConfig } from '@localess/schema';

import { Button } from './button';
import { HeroSection } from './hero-section';
import { Page } from './page';

export const config = defineConfig({
  schemas: [Page, HeroSection, Button],
});

The entry is loaded with its default export preferred, falling back to named exports — the first value shaped like a defineConfig() result wins.

Validate

Check your definitions for problems. Fully offline: no login, no network, no space.

localess schema validate <entry> [options]
FlagDefaultDescription
--format <format>textOutput format: text or json
-v, --verbosefalsePrint verbose debug output

Each issue prints as ERROR|WARNING <code> <path> — <message>. The command exits 1 if any error-severity issue is found, or if no config could be located in the entry file. --format json prints the full validation result instead, for tooling to consume.

localess schema validate ./schemas/index.ts
localess schema validate ./schemas/index.ts --format json

Because it needs nothing but the files, validate is the right thing to run on every commit — including on pull requests from forks, where credentials aren't available.

Pull

Generate schema definition files from an existing space. This is how you adopt schema-as-code on a space that was built through the UI.

localess schema pull [options]
FlagDefaultDescription
-p, --path <path>schemasDirectory to write schema definition files into
-v, --verbosefalsePrint verbose debug output

Writes one file per schema, named in kebab-case (HeroBlockhero-block.ts), plus an index.ts exporting a defineConfig({ schemas: [...] }) over all of them. References between pulled schemas become real import statements, so renaming a schema is a type error rather than a silent mismatch. Fields are emitted wrapped in defineField(...), which is the form that type-checks each field against its own kind:

// src/schemas/button.ts
// Generated by `localess schema pull`. Do not edit — the next pull overwrites this file.
import { defineField, defineSchema } from '@localess/schema';

import { ButtonType } from './button-type';

export const Button = defineSchema({
  id: 'Button',
  type: 'NODE',
  displayName: 'Button',
  previewField: 'label',
  fields: [
    defineField({ name: 'label', kind: 'TEXT', required: true, translatable: true, maxLength: 30 }),
    defineField({ name: 'type', kind: 'OPTION', required: true, source: ButtonType }),
  ],
});

The command is repeatable. That header comment is the marker — only files carrying it are overwritten or deleted, so a hand-written file that happens to share a name is reported and left alone. Output is deterministic for a given space, so re-running produces no spurious diff.

localess schema pull
localess schema pull --path src/schemas

If you maintain schemas by hand and only want to inspect what's on the server, pull into a scratch directory rather than over your own files:

localess schema pull --path ./src/schemas/pulled

Diff

Compare your code-defined schemas against the space, read-only. Exits 1 on any drift, which makes it a CI gate.

localess schema diff <entry> [options]
FlagDefaultDescription
-a, --allfalseAlso print unchanged schemas
-v, --verbosefalsePrint verbose debug output

Schemas are grouped into Create / Update / Stale sections, the same report translation diff produces. Comparison is by key-sorted JSON of each schema's exported form, which is what the server itself uses to detect changes — so the preview matches what a push would do.

Unlike push, diff does not run validation first. Run validate alongside it if you want both checks.

localess schema diff ./schemas/index.ts
localess schema diff ./schemas/index.ts --all

Push

Validate, diff, then write to the space.

localess schema push <entry> [options]
FlagDefaultDescription
--dry-runfalseReport what would change without writing
--deletefalseAlso delete schemas present on the server but absent from code
-a, --allfalseAlso print unchanged schemas in the preview
-y, --yesfalseSkip the deletion confirmation prompt
-v, --verbosefalsePrint verbose debug output

If validation reports any error, the command aborts with exit 1 and pushes nothing.

Upsert vs. sync

ModeFlagBehaviour
Upsert (default)Creates and updates. Schemas on the server that aren't in your code are left alone and listed in a warning
Sync--deleteAlso deletes server schemas absent from your code. Prompts with the exact list first, unless -y, --dry-run, or nothing is stale

Upsert is the default because deleting a schema in a space deletes the content shaped by it. Reach for --delete deliberately.

# Preview
localess schema push ./schemas/index.ts --dry-run

# Create and update only
localess schema push ./schemas/index.ts

# Also remove schemas that no longer exist in code
localess schema push ./schemas/index.ts --delete

After writing, the command prints the server's created / updated / deleted / unchanged counts, then reconciles them against the preview it showed beforehand. Any schema whose predicted outcome doesn't match what actually happened is reported as a Prediction mismatch warning — usually the fingerprint of a concurrent change between the preview and the write. The warning doesn't fail the command.

In CI

validate needs no credentials, so it can run anywhere. diff needs credentials and fails the build on drift:

- run: npx @localess/cli schema validate ./schemas/index.ts
- run: npx @localess/cli schema diff ./schemas/index.ts
  env:
    LOCALESS_ORIGIN: ${{ secrets.LOCALESS_ORIGIN }}
    LOCALESS_SPACE: ${{ secrets.LOCALESS_SPACE }}
    LOCALESS_TOKEN: ${{ secrets.LOCALESS_TOKEN }}

See Login for how credentials are resolved in a pipeline.

On this page