@localess/client 3.0.0-dev.20260313114825 → 3.0.0-dev.20260323211059

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 CHANGED
@@ -303,8 +303,7 @@ Base type for all content schema data objects.
303
303
  ```ts
304
304
  interface ContentDataSchema {
305
305
  _id: string;
306
- _schema?: string;
307
- schema: string;
306
+ _schema: string;
308
307
  }
309
308
 
310
309
  interface ContentData extends ContentDataSchema {
@@ -374,6 +373,18 @@ A key-value map of translation keys to translated string values.
374
373
 
375
374
  ---
376
375
 
376
+ ## AI Coding Agents
377
+
378
+ This package ships a [`SKILL.md`](./SKILL.md) file that provides AI coding agents (GitHub Copilot, Claude Code, Cursor, and others) with accurate, up-to-date APIs, patterns, and best practices. Most agents automatically read `SKILL.md` when starting a session.
379
+
380
+ When you change the public API of this package, update `SKILL.md` alongside your code:
381
+
382
+ - **New option or parameter** → add it to the relevant options table and usage example
383
+ - **Changed behaviour** → update the description and any affected code snippets
384
+ - **Deprecated API** → mark it clearly and point to the replacement
385
+
386
+ ---
387
+
377
388
  ## License
378
389
 
379
390
  [MIT](../../LICENSE)
package/SKILL.md ADDED
@@ -0,0 +1,282 @@
1
+ # SKILL: @localess/client
2
+
3
+ ## Overview
4
+
5
+ `@localess/client` is the **core JavaScript/TypeScript SDK** for the Localess headless CMS. It is a **server-side-only** library — never use it in browser/client-side code because it requires an API token that must remain secret.
6
+
7
+ **Zero production dependencies.** Requires Node.js >= 20.0.0.
8
+
9
+ ---
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @localess/client
15
+ ```
16
+
17
+ ---
18
+
19
+ ## Initialization
20
+
21
+ ```typescript
22
+ import { localessClient } from "@localess/client";
23
+
24
+ const client = localessClient({
25
+ origin: 'https://my-localess.web.app', // Full URL with protocol (required)
26
+ spaceId: 'YOUR_SPACE_ID', // From Space settings (required)
27
+ token: 'YOUR_API_TOKEN', // API token — NEVER expose client-side (required)
28
+ version: 'draft', // undefined = published (default), 'draft' for preview
29
+ debug: false, // Logs requests; default: false
30
+ cacheTTL: 300000, // Cache TTL in ms; false to disable; default: 5 min
31
+ });
32
+ ```
33
+
34
+ ---
35
+
36
+ ## API Methods
37
+
38
+ ### Fetch Content by Slug
39
+
40
+ ```typescript
41
+ const content = await client.getContentBySlug<Page>('home', {
42
+ locale: 'en',
43
+ resolveReference: true, // Inline referenced content
44
+ resolveLink: true, // Inline linked content
45
+ version: 'draft', // Override client default per-request
46
+ });
47
+ // content.data is typed as Page
48
+ ```
49
+
50
+ ### Fetch Content by ID
51
+
52
+ ```typescript
53
+ const content = await client.getContentById<Article>('content-id-here', {
54
+ locale: 'en',
55
+ resolveReference: true,
56
+ });
57
+ ```
58
+
59
+ ### Fetch Navigation Links
60
+
61
+ ```typescript
62
+ const links = await client.getLinks({
63
+ kind: 'DOCUMENT', // 'DOCUMENT' | 'FOLDER'
64
+ parentSlug: 'blog', // Filter to children of a slug
65
+ excludeChildren: false, // Exclude nested sub-slugs
66
+ });
67
+ // links: { [id: string]: ContentMetadata }
68
+ ```
69
+
70
+ ### Fetch Translations
71
+
72
+ ```typescript
73
+ const t = await client.getTranslations('en');
74
+ // t: { [key: string]: string }
75
+ // Usage: t['common.submit'] => 'Submit'
76
+ ```
77
+
78
+ ### Asset URL
79
+
80
+ ```typescript
81
+ import { localessClient } from "@localess/client";
82
+
83
+ const url = client.assetLink(content.data.image);
84
+ // Returns: https://my-localess.web.app/api/v1/spaces/{spaceId}/assets/{uri}
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Content Fetch Parameters
90
+
91
+ | Parameter | Type | Default | Description |
92
+ |--------------------|-----------|---------------|----------------------------------------|
93
+ | `version` | `'draft' \| undefined` | `undefined` | `'draft'` for preview, omit for published |
94
+ | `locale` | `string` | — | ISO 639-1 code: `'en'`, `'de'`, etc. |
95
+ | `resolveReference` | `boolean` | `false` | Inline referenced content objects |
96
+ | `resolveLink` | `boolean` | `false` | Inline linked content metadata |
97
+
98
+ ---
99
+
100
+ ## Caching
101
+
102
+ - Default: TTL-based, **5 minutes** (300,000 ms)
103
+ - Cache key = full request URL (includes all parameters)
104
+ - Adjust for content update frequency:
105
+
106
+ ```typescript
107
+ localessClient({ cacheTTL: 60000 }) // 1 minute — frequently updated
108
+ localessClient({ cacheTTL: 3600000 }) // 1 hour — rarely updated
109
+ localessClient({ cacheTTL: false }) // Disabled — always fresh (draft mode)
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Visual Editor Integration
115
+
116
+ ### Inject Sync Script
117
+
118
+ ```typescript
119
+ import { loadLocalessSync } from "@localess/client";
120
+
121
+ // Call in browser context (e.g., inside useEffect or layout script)
122
+ loadLocalessSync('https://my-localess.web.app');
123
+ // Injects the Localess sync script into <head>; no-op if not in iframe
124
+ ```
125
+
126
+ ### Mark Elements as Editable
127
+
128
+ ```typescript
129
+ import { localessEditable, localessEditableField } from "@localess/client";
130
+
131
+ // Root element of a content block
132
+ <section {...localessEditable(data)}>
133
+ {/* Adds: data-ll-id (from _id), data-ll-schema (from _schema) */}
134
+
135
+ {/* Individual editable field */}
136
+ <h1 {...localessEditableField<Page>('title')}>
137
+ {/* Adds: data-ll-field="title" */}
138
+ {data.title}
139
+ </h1>
140
+ </section>
141
+ ```
142
+
143
+ > **Note:** `localessEditable` reads `content._id` and `content._schema`.
144
+
145
+ ### Listen to Editor Events
146
+
147
+ ```typescript
148
+ // Only available when app is loaded inside the Localess Visual Editor iframe
149
+ if (window.localess) {
150
+ window.localess.on(['input', 'change'], (event) => {
151
+ if (event.type === 'input' || event.type === 'change') {
152
+ setPageData(event.data); // Real-time preview update
153
+ }
154
+ });
155
+ // No .off() method — subscribe once on mount
156
+ }
157
+ ```
158
+
159
+ **Event types:**
160
+
161
+ | Event | When |
162
+ |---------------|---------------------------------------------|
163
+ | `input` | User is typing in a field (real-time) |
164
+ | `change` | Field value confirmed |
165
+ | `save` | Content saved |
166
+ | `publish` | Content published |
167
+ | `pong` | Editor heartbeat response |
168
+ | `enterSchema` | User enters a schema element |
169
+ | `hoverSchema` | User hovers over a schema element |
170
+
171
+ ---
172
+
173
+ ## Key Data Types
174
+
175
+ ```typescript
176
+ // Content response wrapper
177
+ interface Content<T extends ContentData> extends ContentMetadata {
178
+ data?: T;
179
+ links?: Links;
180
+ references?: References;
181
+ }
182
+
183
+ // Base schema fields every content data object has
184
+ interface ContentDataSchema {
185
+ _id: string;
186
+ _schema: string;
187
+ }
188
+
189
+ // Asset reference
190
+ interface ContentAsset {
191
+ kind: 'ASSET';
192
+ uri: string;
193
+ }
194
+
195
+ // Internal or external link
196
+ interface ContentLink {
197
+ kind: 'LINK';
198
+ type: 'url' | 'content';
199
+ target: '_blank' | '_self';
200
+ uri: string;
201
+ }
202
+
203
+ // Rich text (Tiptap JSON format)
204
+ interface ContentRichText {
205
+ type?: string;
206
+ content?: ContentRichText[];
207
+ }
208
+
209
+ // Reference to another content item
210
+ interface ContentReference {
211
+ kind: 'REFERENCE';
212
+ uri: string;
213
+ }
214
+
215
+ // Navigation links map
216
+ interface Links {
217
+ [contentId: string]: ContentMetadata;
218
+ }
219
+
220
+ // Translations flat map
221
+ interface Translations {
222
+ [key: string]: string;
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Environment Safety Utilities
229
+
230
+ ```typescript
231
+ import { isBrowser, isServer, isIframe } from "@localess/client";
232
+
233
+ isBrowser() // true if window is defined
234
+ isServer() // true if window is undefined
235
+ isIframe() // true if running inside an iframe (browser only)
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Best Practices
241
+
242
+ 1. **Never import `@localess/client` in browser bundles.** Use it only in server-side code: Next.js Server Components, API routes, `getServerSideProps`, Remix loaders, etc.
243
+
244
+ 2. **Store credentials in environment variables**, not hardcoded:
245
+ ```
246
+ LOCALESS_ORIGIN=https://my-localess.web.app
247
+ LOCALESS_SPACE_ID=your-space-id
248
+ LOCALESS_TOKEN=your-api-token
249
+ ```
250
+
251
+ 3. **Create one client instance** and reuse it — the cache is instance-bound.
252
+
253
+ 4. **Use generated types** from `@localess/cli` (`localess types generate`) for full type safety:
254
+ ```typescript
255
+ import type { Page } from './.localess/localess';
256
+ const content = await client.getContentBySlug<Page>('home');
257
+ ```
258
+
259
+ 5. **Use `version: 'draft'` and `cacheTTL: false` for preview/editing** environments.
260
+
261
+ 6. **Use `resolveReference: true`** only when you need inline reference data — it increases payload size.
262
+
263
+ ---
264
+
265
+ ## Exports Reference
266
+
267
+ ```typescript
268
+ export { localessClient } // Client factory
269
+ export { localessEditable, localessEditableField } // Visual editor helpers
270
+ export { llEditable, llEditableField } // Deprecated aliases
271
+ export { loadLocalessSync } // Sync script injector
272
+ export { isBrowser, isServer, isIframe } // Environment utilities
273
+ export type {
274
+ LocalessClient, LocalessClientOptions,
275
+ ContentFetchParams, LinksFetchParams,
276
+ Content, ContentData, ContentDataSchema, ContentDataField,
277
+ ContentMetadata, ContentAsset, ContentLink,
278
+ ContentRichText, ContentReference,
279
+ Links, References, Translations,
280
+ LocalessSync, EventToApp, EventCallback, EventToAppType,
281
+ }
282
+ ```
package/dist/index.d.mts CHANGED
@@ -74,11 +74,7 @@ interface ContentDataSchema {
74
74
  /**
75
75
  * Unique identifier for the Schema object.
76
76
  */
77
- _schema?: string;
78
- /**
79
- * Unique identifier for the Schema object.
80
- */
81
- schema: string;
77
+ _schema: string;
82
78
  }
83
79
  /**
84
80
  * ContentData defined Object to connect all possible root Schemas.
@@ -233,7 +229,7 @@ type ContentFetchParams = {
233
229
  * Content version to fetch, leave empty for 'published' or 'draft' for the latest draft.
234
230
  * Overrides the version set in the client options.
235
231
  */
236
- version?: 'draft' | string;
232
+ version?: 'draft';
237
233
  /**
238
234
  * Locale identifier (ISO 639-1) to fetch content in, leave empty for default locale.
239
235
  *
@@ -293,16 +289,6 @@ declare function localessEditable(content: ContentDataSchema): {
293
289
  'data-ll-id': string;
294
290
  'data-ll-schema': string;
295
291
  };
296
- /**
297
- * Adds Localess editable attributes to a content item.
298
- * @param content
299
- * @returns An object containing data-ll-id and data-ll-schema attributes.
300
- * @deprecated use localessEditable instead
301
- */
302
- declare function llEditable(content: ContentDataSchema): {
303
- 'data-ll-id': string;
304
- 'data-ll-schema': string;
305
- };
306
292
  /**
307
293
  * Adds Localess editable field attribute to a specific field.
308
294
  * Added type safety to ensure fieldName is a valid key of the content data excluding base schema fields.
@@ -312,15 +298,6 @@ declare function llEditable(content: ContentDataSchema): {
312
298
  declare function localessEditableField<T extends ContentData = ContentData>(fieldName: Exclude<keyof T, keyof ContentDataSchema>): {
313
299
  'data-ll-field': Exclude<keyof T, keyof ContentDataSchema>;
314
300
  };
315
- /**
316
- * Adds Localess editable field attribute to a specific field.
317
- * @param fieldName
318
- * @returns An object containing data-ll-field attribute.
319
- * @deprecated use localessEditableField instead
320
- */
321
- declare function llEditableField(fieldName: string): {
322
- 'data-ll-field': string;
323
- };
324
301
 
325
302
  /**
326
303
  * Inject Localess Sync Script in Header
@@ -356,4 +333,4 @@ declare global {
356
333
  }
357
334
  }
358
335
 
359
- export { type Content, type ContentAsset, type ContentData, type ContentDataField, type ContentDataSchema, type ContentFetchParams, type ContentLink, type ContentMetadata, type ContentReference, type ContentRichText, type EventCallback, type EventToApp, type EventToAppType, type Links, type LinksFetchParams, type Locale, type LocalessClient, type LocalessClientOptions, type LocalessSync, type References, type Translations, isBrowser, isIframe, isServer, llEditable, llEditableField, loadLocalessSync, localessClient, localessEditable, localessEditableField };
336
+ export { type Content, type ContentAsset, type ContentData, type ContentDataField, type ContentDataSchema, type ContentFetchParams, type ContentLink, type ContentMetadata, type ContentReference, type ContentRichText, type EventCallback, type EventToApp, type EventToAppType, type Links, type LinksFetchParams, type Locale, type LocalessClient, type LocalessClientOptions, type LocalessSync, type References, type Translations, isBrowser, isIframe, isServer, loadLocalessSync, localessClient, localessEditable, localessEditableField };
package/dist/index.d.ts CHANGED
@@ -74,11 +74,7 @@ interface ContentDataSchema {
74
74
  /**
75
75
  * Unique identifier for the Schema object.
76
76
  */
77
- _schema?: string;
78
- /**
79
- * Unique identifier for the Schema object.
80
- */
81
- schema: string;
77
+ _schema: string;
82
78
  }
83
79
  /**
84
80
  * ContentData defined Object to connect all possible root Schemas.
@@ -233,7 +229,7 @@ type ContentFetchParams = {
233
229
  * Content version to fetch, leave empty for 'published' or 'draft' for the latest draft.
234
230
  * Overrides the version set in the client options.
235
231
  */
236
- version?: 'draft' | string;
232
+ version?: 'draft';
237
233
  /**
238
234
  * Locale identifier (ISO 639-1) to fetch content in, leave empty for default locale.
239
235
  *
@@ -293,16 +289,6 @@ declare function localessEditable(content: ContentDataSchema): {
293
289
  'data-ll-id': string;
294
290
  'data-ll-schema': string;
295
291
  };
296
- /**
297
- * Adds Localess editable attributes to a content item.
298
- * @param content
299
- * @returns An object containing data-ll-id and data-ll-schema attributes.
300
- * @deprecated use localessEditable instead
301
- */
302
- declare function llEditable(content: ContentDataSchema): {
303
- 'data-ll-id': string;
304
- 'data-ll-schema': string;
305
- };
306
292
  /**
307
293
  * Adds Localess editable field attribute to a specific field.
308
294
  * Added type safety to ensure fieldName is a valid key of the content data excluding base schema fields.
@@ -312,15 +298,6 @@ declare function llEditable(content: ContentDataSchema): {
312
298
  declare function localessEditableField<T extends ContentData = ContentData>(fieldName: Exclude<keyof T, keyof ContentDataSchema>): {
313
299
  'data-ll-field': Exclude<keyof T, keyof ContentDataSchema>;
314
300
  };
315
- /**
316
- * Adds Localess editable field attribute to a specific field.
317
- * @param fieldName
318
- * @returns An object containing data-ll-field attribute.
319
- * @deprecated use localessEditableField instead
320
- */
321
- declare function llEditableField(fieldName: string): {
322
- 'data-ll-field': string;
323
- };
324
301
 
325
302
  /**
326
303
  * Inject Localess Sync Script in Header
@@ -356,4 +333,4 @@ declare global {
356
333
  }
357
334
  }
358
335
 
359
- export { type Content, type ContentAsset, type ContentData, type ContentDataField, type ContentDataSchema, type ContentFetchParams, type ContentLink, type ContentMetadata, type ContentReference, type ContentRichText, type EventCallback, type EventToApp, type EventToAppType, type Links, type LinksFetchParams, type Locale, type LocalessClient, type LocalessClientOptions, type LocalessSync, type References, type Translations, isBrowser, isIframe, isServer, llEditable, llEditableField, loadLocalessSync, localessClient, localessEditable, localessEditableField };
336
+ export { type Content, type ContentAsset, type ContentData, type ContentDataField, type ContentDataSchema, type ContentFetchParams, type ContentLink, type ContentMetadata, type ContentReference, type ContentRichText, type EventCallback, type EventToApp, type EventToAppType, type Links, type LinksFetchParams, type Locale, type LocalessClient, type LocalessClientOptions, type LocalessSync, type References, type Translations, isBrowser, isIframe, isServer, loadLocalessSync, localessClient, localessEditable, localessEditableField };
package/dist/index.js CHANGED
@@ -23,8 +23,6 @@ __export(index_exports, {
23
23
  isBrowser: () => isBrowser,
24
24
  isIframe: () => isIframe,
25
25
  isServer: () => isServer,
26
- llEditable: () => llEditable,
27
- llEditableField: () => llEditableField,
28
26
  loadLocalessSync: () => loadLocalessSync,
29
27
  localessClient: () => localessClient,
30
28
  localessEditable: () => localessEditable,
@@ -253,13 +251,7 @@ function localessClient(options) {
253
251
  function localessEditable(content) {
254
252
  return {
255
253
  "data-ll-id": content._id,
256
- "data-ll-schema": content._schema || content.schema
257
- };
258
- }
259
- function llEditable(content) {
260
- return {
261
- "data-ll-id": content._id,
262
- "data-ll-schema": content._schema || content.schema
254
+ "data-ll-schema": content._schema
263
255
  };
264
256
  }
265
257
  function localessEditableField(fieldName) {
@@ -267,11 +259,6 @@ function localessEditableField(fieldName) {
267
259
  "data-ll-field": fieldName
268
260
  };
269
261
  }
270
- function llEditableField(fieldName) {
271
- return {
272
- "data-ll-field": fieldName
273
- };
274
- }
275
262
 
276
263
  // src/sync.ts
277
264
  var JS_SYNC_ID = "localess-js-sync";
@@ -296,8 +283,6 @@ function loadLocalessSync(origin, force = false) {
296
283
  isBrowser,
297
284
  isIframe,
298
285
  isServer,
299
- llEditable,
300
- llEditableField,
301
286
  loadLocalessSync,
302
287
  localessClient,
303
288
  localessEditable,
package/dist/index.mjs CHANGED
@@ -219,13 +219,7 @@ function localessClient(options) {
219
219
  function localessEditable(content) {
220
220
  return {
221
221
  "data-ll-id": content._id,
222
- "data-ll-schema": content._schema || content.schema
223
- };
224
- }
225
- function llEditable(content) {
226
- return {
227
- "data-ll-id": content._id,
228
- "data-ll-schema": content._schema || content.schema
222
+ "data-ll-schema": content._schema
229
223
  };
230
224
  }
231
225
  function localessEditableField(fieldName) {
@@ -233,11 +227,6 @@ function localessEditableField(fieldName) {
233
227
  "data-ll-field": fieldName
234
228
  };
235
229
  }
236
- function llEditableField(fieldName) {
237
- return {
238
- "data-ll-field": fieldName
239
- };
240
- }
241
230
 
242
231
  // src/sync.ts
243
232
  var JS_SYNC_ID = "localess-js-sync";
@@ -261,8 +250,6 @@ export {
261
250
  isBrowser,
262
251
  isIframe,
263
252
  isServer,
264
- llEditable,
265
- llEditableField,
266
253
  loadLocalessSync,
267
254
  localessClient,
268
255
  localessEditable,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@localess/client",
3
- "version": "3.0.0-dev.20260313114825",
3
+ "version": "3.0.0-dev.20260323211059",
4
4
  "description": "Universal JavaScript/TypeScript SDK for Localess's API.",
5
5
  "keywords": [
6
6
  "localess",
@@ -14,7 +14,8 @@
14
14
  "homepage": "https://github.com/Lessify/localess-js",
15
15
  "sideEffects": false,
16
16
  "files": [
17
- "dist"
17
+ "dist",
18
+ "SKILL.md"
18
19
  ],
19
20
  "main": "dist/index.js",
20
21
  "module": "dist/index.mjs",