@localess/client 3.0.0-dev.20260313085125 → 3.0.0-dev.20260313114825
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.
- package/README.md +355 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,55 +8,372 @@
|
|
|
8
8
|
|
|
9
9
|
# Localess JavaScript / TypeScript Client SDK
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The `@localess/client` package is the core JavaScript/TypeScript SDK for the [Localess](https://github.com/Lessify/localess) headless CMS platform. It provides a type-safe API client for fetching content, translations, and assets, along with Visual Editor integration utilities.
|
|
12
12
|
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
>
|
|
13
|
+
> **⚠️ Security Notice:**
|
|
14
|
+
> This SDK is designed for **server-side use only**. It requires a Localess API Token that must be kept secret.
|
|
15
|
+
> Never use this package in browser/client-side code, as it would expose your API token to the public.
|
|
16
|
+
> In React applications, always fetch data server-side (e.g., Next.js Server Components, API routes, or server-side rendering).
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Node.js >= 20.0.0
|
|
16
21
|
|
|
17
22
|
## Installation
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
npm install @localess/client
|
|
22
|
-
|
|
24
|
+
```bash
|
|
25
|
+
# npm
|
|
26
|
+
npm install @localess/client
|
|
27
|
+
|
|
28
|
+
# yarn
|
|
29
|
+
yarn add @localess/client
|
|
30
|
+
|
|
31
|
+
# pnpm
|
|
32
|
+
pnpm add @localess/client
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Getting Started
|
|
38
|
+
|
|
39
|
+
### Initializing the Client
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { localessClient } from "@localess/client";
|
|
43
|
+
|
|
44
|
+
const client = localessClient({
|
|
45
|
+
origin: 'https://my-localess.web.app', // Fully qualified domain with protocol
|
|
46
|
+
spaceId: 'YOUR_SPACE_ID', // Found in Localess Space settings
|
|
47
|
+
token: 'YOUR_API_TOKEN', // Found in Localess Space settings (keep secret!)
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Client Options
|
|
52
|
+
|
|
53
|
+
| Option | Type | Required | Default | Description |
|
|
54
|
+
|--------|------|----------|---------|-------------|
|
|
55
|
+
| `origin` | `string` | ✅ | — | Fully qualified domain with protocol (e.g., `https://my-localess.web.app`) |
|
|
56
|
+
| `spaceId` | `string` | ✅ | — | Localess Space ID, found in Space settings |
|
|
57
|
+
| `token` | `string` | ✅ | — | Localess API token, found in Space settings |
|
|
58
|
+
| `version` | `'draft' \| string` | ❌ | `'published'` | Default content version to fetch |
|
|
59
|
+
| `debug` | `boolean` | ❌ | `false` | Enable debug logging |
|
|
60
|
+
| `cacheTTL` | `number \| false` | ❌ | `300000` | Cache TTL in milliseconds (5 minutes). Set `false` to disable caching |
|
|
61
|
+
|
|
62
|
+
---
|
|
23
63
|
|
|
24
|
-
|
|
25
|
-
````bash
|
|
26
|
-
yarn add @localess/client@latest
|
|
27
|
-
````
|
|
64
|
+
## Fetching Content
|
|
28
65
|
|
|
29
|
-
|
|
66
|
+
### `getContentBySlug<T>(slug, params?)`
|
|
30
67
|
|
|
31
|
-
|
|
32
|
-
import {localessClient} from "@localess/client";
|
|
68
|
+
Fetch a content document by its slug path. Supports generic typing for full type safety.
|
|
33
69
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
70
|
+
```ts
|
|
71
|
+
// Basic usage
|
|
72
|
+
const content = await client.getContentBySlug('docs/overview');
|
|
73
|
+
|
|
74
|
+
// With type safety (requires generated types from @localess/cli)
|
|
75
|
+
import type { Page } from './.localess/localess';
|
|
76
|
+
|
|
77
|
+
const content = await client.getContentBySlug<Page>('home', {
|
|
78
|
+
locale: 'en',
|
|
79
|
+
resolveReference: true,
|
|
80
|
+
resolveLink: true,
|
|
41
81
|
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `getContentById<T>(id, params?)`
|
|
85
|
+
|
|
86
|
+
Fetch a content document by its unique ID. Accepts the same parameters as `getContentBySlug`.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const content = await client.getContentById<Page>('FRnIT7CUABoRCdSVVGGs', {
|
|
90
|
+
locale: 'de',
|
|
91
|
+
version: 'draft',
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Content Fetch Parameters
|
|
96
|
+
|
|
97
|
+
| Parameter | Type | Default | Description |
|
|
98
|
+
|-----------|------|---------|-------------|
|
|
99
|
+
| `version` | `'draft' \| string` | Client default | Override the client's default content version |
|
|
100
|
+
| `locale` | `string` | — | ISO 639-1 locale code (e.g., `'en'`, `'de'`) |
|
|
101
|
+
| `resolveReference` | `boolean` | `false` | Resolve content references inline |
|
|
102
|
+
| `resolveLink` | `boolean` | `false` | Resolve content links inline |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Fetching Content Links
|
|
107
|
+
|
|
108
|
+
### `getLinks(params?)`
|
|
109
|
+
|
|
110
|
+
Fetch all content links from the space, optionally filtered by type or parent.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// Fetch all links
|
|
114
|
+
const links = await client.getLinks();
|
|
115
|
+
|
|
116
|
+
// Fetch only documents under a specific parent
|
|
117
|
+
const legalLinks = await client.getLinks({
|
|
118
|
+
kind: 'DOCUMENT',
|
|
119
|
+
parentSlug: 'legal',
|
|
120
|
+
excludeChildren: false,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
| Parameter | Type | Description |
|
|
125
|
+
|-----------|------|-------------|
|
|
126
|
+
| `kind` | `'DOCUMENT' \| 'FOLDER'` | Filter results by content kind |
|
|
127
|
+
| `parentSlug` | `string` | Filter by parent slug (e.g., `'legal/policy'`) |
|
|
128
|
+
| `excludeChildren` | `boolean` | When `true`, excludes nested sub-slugs from results |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Fetching Translations
|
|
133
|
+
|
|
134
|
+
### `getTranslations(locale)`
|
|
135
|
+
|
|
136
|
+
Fetch all translations for a given locale. Returns a flat key-value map.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
const translations = await client.getTranslations('en');
|
|
140
|
+
// { "common.submit": "Submit", "nav.home": "Home", ... }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Assets
|
|
146
|
+
|
|
147
|
+
### `assetLink(asset)`
|
|
148
|
+
|
|
149
|
+
Generate a fully qualified URL for a content asset.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { localessClient } from "@localess/client";
|
|
153
|
+
|
|
154
|
+
const client = localessClient({ origin, spaceId, token });
|
|
155
|
+
|
|
156
|
+
// From a ContentAsset object
|
|
157
|
+
const url = client.assetLink(content.data.image);
|
|
158
|
+
|
|
159
|
+
// From a URI string
|
|
160
|
+
const url = client.assetLink('/spaces/abc/assets/photo.jpg');
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Visual Editor Integration
|
|
166
|
+
|
|
167
|
+
### `loadLocalessSync(origin, force?)`
|
|
168
|
+
|
|
169
|
+
Injects the Localess Visual Editor sync script into the document `<head>`. This enables live-editing capabilities when your site is opened inside the Localess Visual Editor.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { loadLocalessSync } from "@localess/client";
|
|
173
|
+
|
|
174
|
+
loadLocalessSync('https://my-localess.web.app');
|
|
175
|
+
|
|
176
|
+
// Force injection even when not running inside an iframe
|
|
177
|
+
loadLocalessSync('https://my-localess.web.app', true);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### `syncScriptUrl()`
|
|
181
|
+
|
|
182
|
+
Returns the URL of the Localess sync script, useful for manual script injection.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const scriptUrl = client.syncScriptUrl();
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Marking Editable Content
|
|
189
|
+
|
|
190
|
+
Use these helpers to add Localess editable attributes to your HTML elements, enabling element selection and highlighting in the Visual Editor.
|
|
191
|
+
|
|
192
|
+
#### `localessEditable(content)`
|
|
193
|
+
|
|
194
|
+
Marks a content block as editable.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { localessEditable } from "@localess/client";
|
|
198
|
+
|
|
199
|
+
// Returns: { 'data-ll-id': '...', 'data-ll-schema': '...' }
|
|
200
|
+
<section {...localessEditable(content.data)}>...</section>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### `localessEditableField<T>(fieldName)`
|
|
204
|
+
|
|
205
|
+
Marks a specific field within a content block as editable, with type-safe field name inference.
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import { localessEditableField } from "@localess/client";
|
|
209
|
+
|
|
210
|
+
// Returns: { 'data-ll-field': 'title' }
|
|
211
|
+
<h1 {...localessEditableField<MyPage>('title')}>...</h1>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
> **Deprecated:** `llEditable()` and `llEditableField()` are deprecated aliases. Use `localessEditable()` and `localessEditableField()` instead.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Listening to Visual Editor Events
|
|
219
|
+
|
|
220
|
+
When your application is loaded inside the Localess Visual Editor, you can subscribe to editing events via `window.localess`.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
if (window.localess) {
|
|
224
|
+
// Subscribe to a single event
|
|
225
|
+
window.localess.on('change', (event) => {
|
|
226
|
+
if (event.type === 'change') {
|
|
227
|
+
setPageData(event.data);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Subscribe to multiple events
|
|
232
|
+
window.localess.on(['input', 'change'], (event) => {
|
|
233
|
+
if (event.type === 'input' || event.type === 'change') {
|
|
234
|
+
setPageData(event.data);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Available Event Types
|
|
241
|
+
|
|
242
|
+
| Event | Payload | Description |
|
|
243
|
+
|-------|---------|-------------|
|
|
244
|
+
| `input` | `{ type: 'input', data: any }` | Fired while a field is being edited (real-time) |
|
|
245
|
+
| `change` | `{ type: 'change', data: any }` | Fired after a field value is confirmed |
|
|
246
|
+
| `save` | `{ type: 'save' }` | Fired when content is saved |
|
|
247
|
+
| `publish` | `{ type: 'publish' }` | Fired when content is published |
|
|
248
|
+
| `pong` | `{ type: 'pong' }` | Heartbeat response from the editor |
|
|
249
|
+
| `enterSchema` | `{ type: 'enterSchema', id, schema, field? }` | Fired when hovering over a schema element |
|
|
250
|
+
| `hoverSchema` | `{ type: 'hoverSchema', id, schema, field? }` | Fired when entering a schema element |
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Caching
|
|
255
|
+
|
|
256
|
+
All API responses are cached by default using a TTL (time-to-live) cache. You can configure caching when initializing the client.
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
// Default: 5-minute TTL cache
|
|
260
|
+
const client = localessClient({ origin, spaceId, token });
|
|
261
|
+
|
|
262
|
+
// Custom TTL (e.g., 10 minutes)
|
|
263
|
+
const client = localessClient({ origin, spaceId, token, cacheTTL: 600000 });
|
|
264
|
+
|
|
265
|
+
// Disable caching entirely
|
|
266
|
+
const client = localessClient({ origin, spaceId, token, cacheTTL: false });
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Type Reference
|
|
272
|
+
|
|
273
|
+
### `Content<T>`
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
interface Content<T extends ContentData> extends ContentMetadata {
|
|
277
|
+
data?: T;
|
|
278
|
+
links?: Links; // Populated when resolveLink: true
|
|
279
|
+
references?: References; // Populated when resolveReference: true
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### `ContentMetadata`
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
interface ContentMetadata {
|
|
287
|
+
id: string;
|
|
288
|
+
name: string;
|
|
289
|
+
kind: 'FOLDER' | 'DOCUMENT';
|
|
290
|
+
slug: string;
|
|
291
|
+
fullSlug: string;
|
|
292
|
+
parentSlug: string;
|
|
293
|
+
publishedAt?: string;
|
|
294
|
+
createdAt: string;
|
|
295
|
+
updatedAt: string;
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### `ContentData`
|
|
300
|
+
|
|
301
|
+
Base type for all content schema data objects.
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
interface ContentDataSchema {
|
|
305
|
+
_id: string;
|
|
306
|
+
_schema?: string;
|
|
307
|
+
schema: string;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
interface ContentData extends ContentDataSchema {
|
|
311
|
+
[field: string]: ContentDataField | undefined;
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### `ContentAsset`
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
interface ContentAsset {
|
|
319
|
+
kind: 'ASSET';
|
|
320
|
+
uri: string;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### `ContentLink`
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
interface ContentLink {
|
|
328
|
+
kind: 'LINK';
|
|
329
|
+
type: 'url' | 'content';
|
|
330
|
+
target: '_blank' | '_self';
|
|
331
|
+
uri: string;
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### `ContentReference`
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
interface ContentReference {
|
|
339
|
+
kind: 'REFERENCE';
|
|
340
|
+
uri: string;
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### `ContentRichText`
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
interface ContentRichText {
|
|
348
|
+
type?: string;
|
|
349
|
+
content?: ContentRichText[];
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### `Links`
|
|
354
|
+
|
|
355
|
+
A key-value map of content IDs to `ContentMetadata` objects.
|
|
356
|
+
|
|
357
|
+
### `References`
|
|
358
|
+
|
|
359
|
+
A key-value map of reference IDs to `Content` objects.
|
|
360
|
+
|
|
361
|
+
### `Translations`
|
|
362
|
+
|
|
363
|
+
A key-value map of translation keys to translated string values.
|
|
364
|
+
|
|
365
|
+
---
|
|
42
366
|
|
|
43
|
-
|
|
44
|
-
llClient.getLinks()
|
|
45
|
-
// Fetch content by SLUG
|
|
46
|
-
llClient.getContentBySlug('docs/overview')
|
|
47
|
-
// Fetch content by ID
|
|
48
|
-
llClient.getContentById('FRnIT7CUABoRCdSVVGGs')
|
|
49
|
-
// Fetch translations by locale
|
|
50
|
-
llClient.getTranslations('en')
|
|
51
|
-
````
|
|
367
|
+
## Utility Functions
|
|
52
368
|
|
|
53
|
-
|
|
369
|
+
| Function | Returns | Description |
|
|
370
|
+
|----------|---------|-------------|
|
|
371
|
+
| `isBrowser()` | `boolean` | Returns `true` if code is running in a browser environment |
|
|
372
|
+
| `isServer()` | `boolean` | Returns `true` if code is running in a server/Node.js environment |
|
|
373
|
+
| `isIframe()` | `boolean` | Returns `true` if the page is rendered inside an iframe |
|
|
54
374
|
|
|
55
|
-
|
|
375
|
+
---
|
|
56
376
|
|
|
57
|
-
|
|
58
|
-
import {loadLocalessSync} from "@localess/client";
|
|
377
|
+
## License
|
|
59
378
|
|
|
60
|
-
|
|
61
|
-
loadLocalessSync('https://my-localess.web.app')
|
|
62
|
-
````
|
|
379
|
+
[MIT](../../LICENSE)
|