@localess/schema 4.0.1-dev.20260915135536 → 4.0.1-dev.20260915190418

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +208 -0
  2. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,208 @@
1
+ <br/>
2
+ <br/>
3
+ <img src="https://github.com/Lessify/localess/wiki/img/logo-adaptive.svg" alt="logo">
4
+ <br/>
5
+ <br/>
6
+
7
+ ----
8
+
9
+ # @localess/schema
10
+
11
+ Define your [Localess](https://github.com/Lessify/localess) content schemas **in TypeScript**, keep them in version control, and get the content types of every schema inferred for free.
12
+
13
+ Instead of clicking a content model together in the Studio UI and then hand-writing matching interfaces that drift, you write the schema once. `@localess/cli` pushes it to your space, and `InferContent` derives the exact shape `getContentBySlug()` will return — including nested blocks and enum unions.
14
+
15
+ **Zero external dependencies** — `@localess/model` is the only entry in `dependencies`. See [ADR 008](../../docs/decisions/008-schema-package.md).
16
+
17
+ ## Requirements
18
+
19
+ - Node.js >= 24.0.0
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ # npm
25
+ npm install @localess/schema
26
+ npm install --save-dev @localess/cli
27
+
28
+ # yarn
29
+ yarn add @localess/schema && yarn add -D @localess/cli
30
+
31
+ # pnpm
32
+ pnpm add @localess/schema && pnpm add -D @localess/cli
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Defining a schema
38
+
39
+ Straight from the [schema playground](../../playgrounds/schema). An enum, a reusable block, and a root document that nests it:
40
+
41
+ ```ts
42
+ // schemas/button-type.ts
43
+ import { defineEnum } from '@localess/schema';
44
+
45
+ export const ButtonType = defineEnum({
46
+ id: 'ButtonType',
47
+ displayName: 'Button Type',
48
+ description: 'It will define the visual part of a button.',
49
+ labels: ['button'],
50
+ values: [
51
+ { name: 'Primary', value: 'primary' },
52
+ { name: 'Secondary', value: 'secondary' },
53
+ ],
54
+ });
55
+ ```
56
+
57
+ ```ts
58
+ // schemas/button.ts
59
+ import { defineField, defineSchema } from '@localess/schema';
60
+ import { ButtonType } from './button-type';
61
+
62
+ export const Button = defineSchema({
63
+ id: 'Button',
64
+ type: 'NODE',
65
+ displayName: 'Button',
66
+ description: 'A button',
67
+ previewField: 'label',
68
+ fields: [
69
+ defineField({
70
+ name: 'label',
71
+ kind: 'TEXT',
72
+ displayName: 'Label',
73
+ required: true,
74
+ translatable: true,
75
+ defaultValue: 'CTA',
76
+ minLength: 3,
77
+ maxLength: 30,
78
+ }),
79
+ defineField({ name: 'type', kind: 'OPTION', displayName: 'Type', required: true, source: ButtonType }),
80
+ ],
81
+ });
82
+ ```
83
+
84
+ ```ts
85
+ // schemas/page.ts
86
+ import { defineField, defineSchema } from '@localess/schema';
87
+ import { Button } from './button';
88
+
89
+ export const Page = defineSchema({
90
+ id: 'Page',
91
+ type: 'ROOT',
92
+ displayName: 'Page',
93
+ fields: [
94
+ defineField({ name: 'title', kind: 'TEXT', displayName: 'Title', translatable: true }),
95
+ defineField({ name: 'description', kind: 'TEXTAREA', displayName: 'Description', translatable: true }),
96
+ defineField({ name: 'buttons', kind: 'SCHEMAS', displayName: 'Buttons', schemas: [Button] }),
97
+ defineField({ name: 'content', kind: 'RICH_TEXT', displayName: 'Content', translatable: true }),
98
+ ],
99
+ });
100
+ ```
101
+
102
+ Collect them into a config — this is the file the CLI reads:
103
+
104
+ ```ts
105
+ // schemas/index.ts
106
+ import { defineConfig } from '@localess/schema';
107
+ import { Button } from './button';
108
+ import { ButtonType } from './button-type';
109
+ import { Page } from './page';
110
+
111
+ export const config = defineConfig({ schemas: [Button, ButtonType, Page] });
112
+ ```
113
+
114
+ `type: 'ROOT'` marks a schema that can be a document of its own; `type: 'NODE'` is a block only usable inside another schema.
115
+
116
+ ---
117
+
118
+ ## Inferring content types
119
+
120
+ This is the payoff — no hand-written interfaces:
121
+
122
+ ```ts
123
+ import type { InferContent, InferContentData } from '@localess/schema';
124
+ import { Page } from './schemas/page';
125
+ import { config } from './schemas';
126
+
127
+ /** Unions the content type of every ROOT schema — type a page-fetching function with this. */
128
+ export type ContentData = InferContentData<typeof config>;
129
+
130
+ /** Resolves a single schema, `SCHEMAS`/`SCHEMA` references included. */
131
+ export type PageContent = InferContent<typeof Page, typeof config>;
132
+ ```
133
+
134
+ `PageContent` now has `title: string`, `buttons: ButtonContent[]`, and `type: 'primary' | 'secondary'` on each button — the enum narrowed to a literal union, resolved through the nesting. Change `minLength` in the schema and nothing breaks; rename a field and every call site fails to compile.
135
+
136
+ Use it with the client:
137
+
138
+ ```ts
139
+ const content = await client.getContentBySlug<PageContent>('home', { locale: 'en' });
140
+ content.data.buttons[0].type; // 'primary' | 'secondary'
141
+ ```
142
+
143
+ `InferEnum<typeof ButtonType>` gives you the value union on its own.
144
+
145
+ ---
146
+
147
+ ## Validating before you push
148
+
149
+ `validate(config)` checks ids, field constraints and cross-schema references locally — no network:
150
+
151
+ ```ts
152
+ import { validate } from '@localess/schema';
153
+ import { config } from './schemas';
154
+
155
+ const result = validate(config);
156
+ // → { ok: true, issues: [] }
157
+
158
+ if (!result.ok) {
159
+ for (const issue of result.issues) {
160
+ console.error(`${issue.severity} ${issue.code} at ${issue.path}: ${issue.message}`);
161
+ }
162
+ }
163
+ ```
164
+
165
+ Each issue carries `severity` (`'error' | 'warning'`), a stable `code` such as `schema/invalid-id`, the `path` to the offending schema or field, and a human-readable `message`.
166
+
167
+ `toSchemaExport(config)` produces the wire format the API accepts, if you'd rather push it yourself than use the CLI.
168
+
169
+ ---
170
+
171
+ ## The CLI workflow
172
+
173
+ [`@localess/cli`](../cli) drives the round trip. From the playground's `package.json`:
174
+
175
+ ```jsonc
176
+ {
177
+ "scripts": {
178
+ "schema:validate": "localess schema validate ./src/schemas/index.ts",
179
+ "schema:diff": "localess schema diff ./src/schemas/index.ts",
180
+ "schema:push": "localess schema push ./src/schemas/index.ts",
181
+ "schema:pull": "localess schema pull --path ./src/schemas/pulled",
182
+ "localess:types": "localess type generate --path ./src/localess.types.ts"
183
+ }
184
+ }
185
+ ```
186
+
187
+ `diff` shows what a push would change before it changes it. `pull` goes the other way, generating these definitions from a space you already modelled in the UI — useful for adopting schemas-as-code on an existing project.
188
+
189
+ ---
190
+
191
+ ## Field kinds
192
+
193
+ `TEXT`, `TEXTAREA`, `MARKDOWN`, `RICH_TEXT`, `NUMBER`, `COLOR`, `DATE`, `DATETIME`, `BOOLEAN`, `OPTION`, `OPTIONS`, `LINK`, `REFERENCE`, `REFERENCES`, `ASSET`, `ASSETS`, `SCHEMA`, `SCHEMAS`.
194
+
195
+ Each kind accepts its own options — `minLength`/`maxLength` on text, `source` on `OPTION`, `schemas` on `SCHEMA`/`SCHEMAS`, `fileTypes` on assets — and `defineField` is typed per kind, so an option that doesn't belong is a compile error rather than a runtime surprise.
196
+
197
+ ---
198
+
199
+ ## Related
200
+
201
+ - [`@localess/cli`](../cli) — push, pull, diff, validate, and type generation
202
+ - [`@localess/model`](../model) — the wire types this package emits
203
+ - [docs/schema.md](../../docs/schema.md) — full reference for every field kind and inference rule
204
+ - [CONTRIBUTING.md](./CONTRIBUTING.md) — adding a field kind
205
+
206
+ ## License
207
+
208
+ See the [Localess](https://github.com/Lessify/localess) repository.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@localess/schema",
3
- "version": "4.0.1-dev.20260915135536",
3
+ "version": "4.0.1-dev.20260915190418",
4
4
  "description": "Programmatic schema definitions for Localess with TypeScript content type inference.",
5
5
  "keywords": [
6
6
  "localess",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "license": "MIT",
45
45
  "dependencies": {
46
- "@localess/model": "4.0.1-dev.20260915135536"
46
+ "@localess/model": "4.0.1-dev.20260915190418"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^24",