@AGENTS.md
This is a monorepo containing the official JavaScript/TypeScript SDKs for the Localess headless CMS. Twelve packages:
@localess/model— Shared domain-model types (Content, ContentAsset, ContentLink, ContentReference, ContentRichText, Locale, Space, Translations, and related shapes). Zero dependencies.@localess/client— Core server-side-only SDK. Zero external dependencies (depends on@localess/model).@localess/richtext— Framework-neutral rich text model, renderer, and HTML/Markdown parsers. Zero dependencies (not even@localess/client) beyond@localess/model.@localess/live-preview— Framework-neutral Visual Editor bridge (sync script loading,data-ll-*attributes, editor events, shared sync controller). Zero dependencies beyond@localess/model(see ADR 013).@localess/schema— Programmatic schema definitions with TypeScript content type inference. Zero dependencies (not even@localess/client) beyond@localess/model.@localess/react— React integration (components, hooks, rich text, Visual Editor sync).@localess/angular— Angular integration (components, directives, pipes, Visual Editor sync).@localess/vue— Vue integration (components, composables, Vite plugin).@localess/svelte— Svelte integration (components, actions, context).@localess/astro— Astro integration (integration, components, live preview).@localess/nuxt— Nuxt module wrapping@localess/vue(config-driven setup, component auto-registration, public/secret token split, server client).@localess/cli— CLI for translations, type generation, and schema pull/push. Depends on@localess/client,@localess/model, and@localess/schema.
Dependency graph: three roots, @localess/model, @localess/richtext, and @localess/schema, which depend on nothing outside that tier (@localess/model on nothing at all; @localess/richtext and @localess/schema on @localess/model alone — see ADR 007, ADR 008, ADR 009). @localess/live-preview depends on @localess/model. @localess/client depends on @localess/model and @localess/live-preview (deprecated re-exports only — see ADR 013). @localess/react, @localess/angular, @localess/vue, @localess/svelte, and @localess/astro depend on @localess/client, @localess/model, @localess/richtext, and @localess/live-preview; @localess/cli depends on @localess/client, @localess/model, and @localess/schema. @localess/nuxt depends on @localess/vue — the single sanctioned framework-to-framework edge (see ADR 011). Otherwise dependent packages never depend on each other.
# Build all packages (model, live-preview, richtext, client, schema, react, vue, svelte, cli, angular, astro, nuxt)
npm run build
# Build individual packages
npm run build:model # must be built before running client/richtext/schema tests
npm run build:richtext # must be built before running framework package tests
npm run build:live-preview # must be built before build:client
npm run build:client
npm run build:schema # must be built before running @localess/cli tests
npm run build:react
npm run build:vue
npm run build:svelte
npm run build:cli
npm run build:angular
npm run build:astro
npm run build:nuxt # requires build:vue first
# Run Angular playgrounds (requires build:angular first)
npm run start:angular-ssr # Angular CLI, SSR
npm run start:angular-static # Angular CLI, prerendered
npm run start:analog # AnalogJS (Vite + Nitro), SSR
npm run start:analog-static # AnalogJS, prerendered
# Run all package tests (vitest everywhere, including @localess/angular via Angular CLI's unit-test builder)
npm test
# Run a single package's tests
npm test --workspace=@localess/angular
# Run a single test file
npx vitest run packages/cli/src/commands/login/login.test.tsRequirements: Node.js >= 24.0.0, npm >= 10.
-
Never commit. Never run
git commitor any command that creates a commit. Make file changes and stop — the developer reviews all changes and commits themselves when ready. -
@localess/clientis server-side only, with one exception. Never suggest using it in browser/client-side code, and a secret token must never be exposed client-side. The one exception: Localess now also issues public tokens (read-only, published content and translations only) that are safe to use client-side — currently supported in@localess/react(its client-sideLocalessDocumentfallback for static export, wherelocalessInit()is called again client-side with a public token),@localess/angular(its unifiedprovideLocaless({ token, ... }), where the token's type is determined by the app's rendering mode),@localess/vue(theLocalessplugin'stokenin CSR apps),@localess/svelte(localessInit({ token })in CSR apps), and@localess/nuxt(itstokenoption is public and goes toruntimeConfig.public; the secret goes inserverToken— see ADR 011).@localess/cliand@localess/astrohave not been reworked for this yet — treat their token as secret-only until they are. Seedocs/decisions/001-server-side-only.md. -
@localess/modelhas zero dependencies of any kind;@localess/richtext,@localess/schema, and@localess/live-previewdepend on@localess/modelalone;@localess/clientdepends on@localess/modeland@localess/live-preview.packages/model/package.jsonhas nodependencieskey at all and must stay that way.packages/richtext/package.json,packages/schema/package.json, andpackages/live-preview/package.jsoneach have exactly onedependenciesentry,@localess/model;packages/client/package.jsonhas exactly two,@localess/modeland@localess/live-preview(ADR 013) — never add anything else todependenciesin any of the five. All five remain zero-external-dependency (no npm package outside this monorepo).devDependenciesare fine (e.g. TipTap in richtext, used only by its parity test). Seedocs/decisions/002-zero-production-deps.md,docs/decisions/007-shared-richtext-package.md,docs/decisions/008-schema-package.md, anddocs/decisions/009-shared-model-package.md. -
Package boundaries.
@localess/live-previewdepends on@localess/model.@localess/clientdepends on@localess/modeland@localess/live-preview.@localess/react,@localess/angular,@localess/vue,@localess/svelte, and@localess/astrodepend on@localess/client,@localess/model,@localess/richtext, and@localess/live-preview;@localess/clidepends on@localess/client,@localess/model, and@localess/schema. Dependent packages never depend on each other, and root packages never depend on anything outside the root tier. One exception: a framework package may depend on another framework package when it is a host-framework-specific wrapper around it — a strict superset relationship, not a shared-utility one.@localess/nuxt→@localess/vueis the only such edge today (Nuxt is Vue); anything shared between siblings belongs in a root package instead. Seedocs/decisions/005-package-boundary-discipline.md,docs/decisions/007-shared-richtext-package.md,docs/decisions/008-schema-package.md,docs/decisions/009-shared-model-package.md,docs/decisions/011-nuxt-module-depends-on-vue.md, anddocs/decisions/013-live-preview-package.md. -
Upstream check. When changing
@localess/client's public API (adding/removing/renaming methods or types), check whether@localess/react,@localess/angular,@localess/vue,@localess/svelte,@localess/astro, and@localess/cliconsume the changed surface and update them (and@localess/nuxt, which reaches it through@localess/vue— ADR 011). -
SKILL.md sync. When changing a package's public API, options, or behavior, update the corresponding
packages/<name>/SKILL.md. These files ship inside the npm packages for downstream AI agents. -
Internal-reference-only imports. Within a package's
src/, only three roles may import from@localess/clientdirectly — a models module (every client type the package needs, plusLocalessApiError), a utils module (every plain client function the package needs — notlocalessClient), and a client file (the one place that callslocalessClient(...)and wraps it in the framework's idiom — a singleton insrc/core/client.tsfor react,src/client.tsfor vue,src/lib/client.tsfor svelte, an injectableservices/client.service.tsfor angular, top-levelclient.tsfor cli). Every other internal file — including the public entry point (index.ts/public-api.ts) — imports through those three instead (e.g.from '../models',from '../utils',from '../core/client', neverfrom '@localess/client'). This keeps the client-package boundary at a small, fixed set of files per package, so it can be audited or changed in one place. A package's public entry point may itself be a sanctioned exception when it deliberately re-exports client surface to consumers (e.g.@localess/angular'spublic-api.tsre-exporting all of@localess/client,@localess/react'ssrc/ssr/index.tsre-exportinglocalessClientfor standalone build scripts) — that's a documented pass-through, not a bypass. Currently enforced in@localess/svelte,@localess/react,@localess/angular,@localess/vue,@localess/cli, and@localess/astro(@localess/nuxtnever imports@localess/clientat all — it goes through@localess/vue); each package'sCONTRIBUTING.mdnames its exact sanctioned files.@localess/astroadditionally has a guard test (src/import-boundary.test.ts) that fails if a new direct import appears, or if model types are imported via the client's re-export rather than from@localess/model; it uses two of the three roles, having nolocalessClient(...)call in TypeScript source. The same discipline applies to@localess/richtextimports: each framework package imports it only from its designated richtext file(s) — reactsrc/core/richtext.ts(+ a type-only import insrc/core/components/localess-rich-text.tsx), vuesrc/richtext.ts, sveltesrc/lib/components/LocalessRichText.svelte, astrosrc/richtext.ts, angularsrc/pipes/rich-text.pipe.tsandsrc/components/localess-rich-text.component.ts— with model types re-exported through each package's models barrel.
- TypeScript strict mode with
noImplicitAny: false. Seetsconfig.jsonin each package. @localess/model,@localess/client,@localess/richtext,@localess/live-preview,@localess/schema,@localess/react,@localess/vue,@localess/astro,@localess/clibuild with Vite in library mode (vite.config.mts). Entry pointsrc/index.ts→dist/.@localess/sveltebuilds withsvelte-package(ESM-only), gated bysvelte-check.@localess/nuxtbuilds with@nuxt/module-builder(build.config.ts) → ESM-onlydist/module.mjsplus an unbundleddist/runtime/.@localess/angularbuilds with ng-packagr via Angular CLI (ng-package.json,angular.json). Entry pointsrc/public-api.ts→dist/. Single unified entry point — no/browseror/serversplit.- Dual CJS + ESM output for the Vite library packages:
dist/index.js(CJS),dist/index.mjs(ESM),dist/index.d.ts(types). ESM-only exceptions:@localess/cli(dist/index.mjsonly),@localess/svelte,@localess/nuxt, and@localess/angular. See ADR 004. - No barrel re-exports except in
index.ts/public-api.tsfiles. - Kebab-case file names (e.g.
content-asset.ts,use-localess.ts). - JSDoc on public API only (exported types, functions, parameters). No inline comments explaining what code does.
See each package's CONTRIBUTING.md for step-by-step patterns:
packages/model/CONTRIBUTING.md— adding shared models, changing the schema wire modelpackages/client/CONTRIBUTING.md— adding API methods, models, typespackages/richtext/CONTRIBUTING.md— adding node/mark types, parity rulespackages/live-preview/CONTRIBUTING.md— extending the editor event unionpackages/schema/CONTRIBUTING.md— adding field kinds, extending inference/validationpackages/react/CONTRIBUTING.md— adding components, hooks, utilitiespackages/angular/CONTRIBUTING.md— adding components, directives, pipespackages/vue/CONTRIBUTING.md— adding composables, extending the Vite pluginpackages/svelte/CONTRIBUTING.md— adding stores and actionspackages/astro/CONTRIBUTING.md— adding components, integration options, entrypointspackages/nuxt/CONTRIBUTING.md— adding module options, component registrationpackages/cli/CONTRIBUTING.md— adding commands and subcommands
docs/— full project reference (index, model, client, richtext, live-preview, schema, react, angular, vue, nuxt, svelte, astro, cli, decisions)docs/decisions/— architectural decision records explaining WHY hard constraints exist