@b10cks/client 1.9.0 → 1.11.0

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
@@ -17,7 +17,7 @@ const client = new ApiClient({
17
17
  baseUrl: 'https://api.b10cks.com/api',
18
18
  token: 'your-access-token',
19
19
  version: 'published', // 'published' (default) or 'draft'
20
- rv: 0, // revision token — pass Date.now() to bypass CDN cache
20
+ rv: 0, // revision token — pass Date.now() to bypass CDN cache
21
21
  })
22
22
 
23
23
  // Factory function — equivalent to new B10cksDataApi(client)
@@ -82,31 +82,31 @@ const redirectMap = await dataApi.getRedirects({
82
82
 
83
83
  ### Filter operators
84
84
 
85
- | Operator object | Wire format | Meaning |
86
- |---|---|---|
87
- | `'value'` | `value` | Exact match |
88
- | `{ eq: 'v' }` | `eq:v` | Exact match |
89
- | `{ neq: 'v' }` | `neq:v` | Not equal |
90
- | `{ in: ['a','b'] }` | `in:a,b` | One of |
91
- | `{ '!in': ['a','b'] }` | `!in:a,b` | None of |
92
- | `{ like: 'v' }` | `like:v` | Contains |
93
- | `{ '!like': 'v' }` | `!like:v` | Does not contain |
94
- | `{ '^like': 'v' }` | `^like:v` | Starts with |
95
- | `{ 'like$': 'v' }` | `like$:v` | Ends with |
96
- | `{ gte: 'v' }` | `gte:v` | Greater than or equal |
97
- | `{ gt: 'v' }` | `gt:v` | Greater than |
98
- | `{ lte: 'v' }` | `lte:v` | Less than or equal |
99
- | `{ lt: 'v' }` | `lt:v` | Less than |
100
- | `{ between: ['a','b'] }` | `a...b` | Range (dates) |
101
- | `{ null: true }` | `null:` | Is null |
102
- | `{ '!null': true }` | `!null:` | Is not null |
85
+ | Operator object | Wire format | Meaning |
86
+ | ------------------------ | ----------- | --------------------- |
87
+ | `'value'` | `value` | Exact match |
88
+ | `{ eq: 'v' }` | `eq:v` | Exact match |
89
+ | `{ neq: 'v' }` | `neq:v` | Not equal |
90
+ | `{ in: ['a','b'] }` | `in:a,b` | One of |
91
+ | `{ '!in': ['a','b'] }` | `!in:a,b` | None of |
92
+ | `{ like: 'v' }` | `like:v` | Contains |
93
+ | `{ '!like': 'v' }` | `!like:v` | Does not contain |
94
+ | `{ '^like': 'v' }` | `^like:v` | Starts with |
95
+ | `{ 'like$': 'v' }` | `like$:v` | Ends with |
96
+ | `{ gte: 'v' }` | `gte:v` | Greater than or equal |
97
+ | `{ gt: 'v' }` | `gt:v` | Greater than |
98
+ | `{ lte: 'v' }` | `lte:v` | Less than or equal |
99
+ | `{ lt: 'v' }` | `lt:v` | Less than |
100
+ | `{ between: ['a','b'] }` | `a...b` | Range (dates) |
101
+ | `{ null: true }` | `null:` | Is null |
102
+ | `{ '!null': true }` | `!null:` | Is not null |
103
103
 
104
104
  ### Content sort
105
105
 
106
106
  `sort` accepts a string or a typed array of `ContentSortItem` values. Prefix with `-` for descending order. JSON content fields are supported via `content.{field}`.
107
107
 
108
108
  ```typescript
109
- sort: '-published_at' // single field, descending
109
+ sort: '-published_at' // single field, descending
110
110
  sort: ['updated_at', '-created_at']
111
111
  sort: ['content.title', '-published_at']
112
112
  ```
@@ -128,42 +128,69 @@ const params = serializeFilter({
128
128
 
129
129
  ## Data API Methods
130
130
 
131
- | Method | Description |
132
- |---|---|
133
- | `getContent(slug, params)` | Single content entry by full slug |
134
- | `getContents(params, options)` | List of content entries |
135
- | `getBreadcrumb(slug, params)` | Ancestor trail of an entry, root first |
136
- | `getBreadcrumbResponse(slug, params)` | Same trail, with the response's `meta` block |
137
- | `getBlock(blockId, params)` | Single block by ID |
138
- | `getBlocks(params, options)` | List of blocks |
139
- | `search(params)` | Full-text content search (`q`, `limit`, `offset`, `language`) |
140
- | `lookupRedirect(source)` | POST redirect lookup for a given source path |
141
- | `getRedirects(params, options)` | Redirect map (cached when `allPages: true`) |
142
- | `getDataEntries(source, params, options)` | Entries for a data source slug |
143
- | `getDataSources(params, options)` | List of data sources |
144
- | `getSitemap(params, options)` | Sitemap entries (default sitemap) |
145
- | `getNamedSitemap(name, params, options)` | Entries of a named sitemap from `settings.sitemaps` |
146
- | `getSpace(params)` | Current space info |
147
- | `getConfig(options)` | Config content entry (cached) |
148
- | `syncRevision(fallbackRv)` | Sync local RV from the space |
149
- | `clearCache()` | Clear redirect and config caches |
131
+ | Method | Description |
132
+ | ----------------------------------------- | ------------------------------------------------------------- |
133
+ | `getContent(slug, params)` | Single content entry by full slug |
134
+ | `getContents(params, options)` | List of content entries |
135
+ | `getBreadcrumb(slug, params)` | Ancestor trail of an entry, root first |
136
+ | `getBreadcrumbResponse(slug, params)` | Same trail, with the response's `meta` block |
137
+ | `getBlock(blockId, params)` | Single block by ID |
138
+ | `getBlocks(params, options)` | List of blocks |
139
+ | `search(params)` | Full-text content search (`q`, `limit`, `offset`, `language`) |
140
+ | `lookupRedirect(source)` | POST redirect lookup for a given source path |
141
+ | `getRedirects(params, options)` | Redirect map (cached when `allPages: true`) |
142
+ | `getDataEntries(source, params, options)` | Entries for a data source slug |
143
+ | `getDataSources(params, options)` | List of data sources |
144
+ | `getSitemap(params, options)` | Sitemap entries (default sitemap) |
145
+ | `getNamedSitemap(name, params, options)` | Entries of a named sitemap from `settings.sitemaps` |
146
+ | `getSpace(params)` | Current space info |
147
+ | `getConfig(options)` | Config entry content plus its `id` (cached) |
148
+ | `syncRevision(fallbackRv)` | Sync local RV from the space |
149
+ | `clearCache()` | Clear redirect and config caches |
150
150
 
151
151
  Pass `{ allPages: true }` as the second argument to any collection method to fetch every page automatically.
152
152
 
153
+ Every collection method normalizes the response envelope, so a bare array, a
154
+ `{ data }` wrapper and a `{ data: { data } }` wrapper all resolve to a plain
155
+ array. There is no need to unwrap by hand.
156
+
157
+ ### Common query params
158
+
159
+ `rv` pins a request to a content revision. It defaults to the client's current
160
+ revision, so pass it only to override — `Date.now()` from a server route
161
+ sidesteps a stale delivery cache:
162
+
163
+ ```typescript
164
+ const entries = await dataApi.getContents({ language_iso: 'de', rv: Date.now() })
165
+ ```
166
+
167
+ `getDataEntries` takes `dimension` to read a mutated variant of a data source,
168
+ falling back to the stored base value for keys the dimension does not override:
169
+
170
+ ```typescript
171
+ const strings = await dataApi.getDataEntries('translations', { dimension: 'fr' })
172
+ ```
173
+
174
+ Filtering by `id`, `canonical_id`, `canonical_parent_id`, `parent_id` and
175
+ `include_fallback` goes through `filter` — see [Typed Filters](#typed-filters).
176
+ `filter: { canonical_id: { in: [...] } }` serializes to the same
177
+ `canonical_id=in:a,b` the API expects, so there is no reason to build that
178
+ string yourself.
179
+
153
180
  ## `ApiClient` configuration
154
181
 
155
182
  ```typescript
156
183
  interface B10cksApiClientOptions {
157
- baseUrl: string // Base URL of the b10cks data API (include `/api`)
158
- token: string // Space access token
159
- version?: 'published' | 'draft' // Content version to fetch (default: 'published')
160
- rv?: string | number // Revision token; use Date.now() to bypass CDN cache
161
- fetchClient?: FetchClient // Custom fetch implementation (required in environments without globalThis.fetch)
162
- getRv?: () => string | number // Custom getter for the shared revision token
163
- setRv?: (value: string | number) => void // Custom setter for the shared revision token
164
- timeoutMs?: number // Per-request timeout; aborts and throws ApiError when exceeded (default: none)
165
- retries?: number // Retry attempts for transient GET failures (network/429/5xx) with backoff (default: 0)
166
- maxConcurrency?: number // Max pages fetched concurrently by getAll/allPages (default: 6)
184
+ baseUrl: string // Base URL of the b10cks data API (include `/api`)
185
+ token: string // Space access token
186
+ version?: 'published' | 'draft' // Content version to fetch (default: 'published')
187
+ rv?: string | number // Revision token; use Date.now() to bypass CDN cache
188
+ fetchClient?: FetchClient // Custom fetch implementation (required in environments without globalThis.fetch)
189
+ getRv?: () => string | number // Custom getter for the shared revision token
190
+ setRv?: (value: string | number) => void // Custom setter for the shared revision token
191
+ timeoutMs?: number // Per-request timeout; aborts and throws ApiError when exceeded (default: none)
192
+ retries?: number // Retry attempts for transient GET failures (network/429/5xx) with backoff (default: 0)
193
+ maxConcurrency?: number // Max pages fetched concurrently by getAll/allPages (default: 6)
167
194
  }
168
195
  ```
169
196
 
@@ -210,21 +237,21 @@ const result = await client.post('redirects/lookup', { source: '/old' })
210
237
 
211
238
  ## Supported Endpoints
212
239
 
213
- | Endpoint | Methods |
214
- |---|---|
215
- | `blocks` | GET (list) |
216
- | `blocks/{id}` | GET (single) |
217
- | `breadcrumbs/{slug}` | GET (single) |
218
- | `contents` | GET (list) |
219
- | `contents/{slug}` | GET (single) |
220
- | `datasources` | GET (list) |
221
- | `datasources/{slug}/entries` | GET (list) |
222
- | `redirects` | GET (list) |
223
- | `redirects/lookup` | POST |
224
- | `search` | GET |
225
- | `sitemap` | GET (list) |
226
- | `sitemaps/{name}` | GET (list) |
227
- | `spaces/me` | GET |
240
+ | Endpoint | Methods |
241
+ | ---------------------------- | ------------ |
242
+ | `blocks` | GET (list) |
243
+ | `blocks/{id}` | GET (single) |
244
+ | `breadcrumbs/{slug}` | GET (single) |
245
+ | `contents` | GET (list) |
246
+ | `contents/{slug}` | GET (single) |
247
+ | `datasources` | GET (list) |
248
+ | `datasources/{slug}/entries` | GET (list) |
249
+ | `redirects` | GET (list) |
250
+ | `redirects/lookup` | POST |
251
+ | `search` | GET |
252
+ | `sitemap` | GET (list) |
253
+ | `sitemaps/{name}` | GET (list) |
254
+ | `spaces/me` | GET |
228
255
 
229
256
  ## Link resolution
230
257
 
@@ -239,6 +266,20 @@ const resolved = resolveB10cksLink(block.ctaLink)
239
266
  // or: undefined (when link is nullish or type === 'asset')
240
267
  ```
241
268
 
269
+ `url` and `internal` links get their `params` as a query string and their `anchor` as a fragment, in that order: `/about?ref=nav#01kh6h981yh1s5z7s3f80wmrw2`. An href that already has a `#` keeps its fragment.
270
+
271
+ ### Block anchors
272
+
273
+ An internal link `anchor` is the target block's `id`. The page has to render that id on the block's element for the browser to scroll there. The framework SDKs do this for you (`v-editable` in Vue, `B10cksComponent` in React). For blocks rendered without them, spread `blockAnchorAttrs`:
274
+
275
+ ```typescript
276
+ import { blockAnchorAttrs } from '@b10cks/client'
277
+
278
+ blockAnchorAttrs(block) // { id: block.id }, or {} when the block has no id
279
+ ```
280
+
281
+ Block ids are lowercase ULIDs and can start with a digit, which is not a valid CSS id selector. Use `document.getElementById(id)`, or `querySelector('#' + CSS.escape(id))`.
282
+
242
283
  ## Sitemap utilities
243
284
 
244
285
  Framework-agnostic helpers for building multilingual sitemaps from `IBSitemapEntry[]`.
@@ -252,9 +293,9 @@ import {
252
293
  } from '@b10cks/client'
253
294
 
254
295
  // Build a rooted locale-prefixed path from a content entry
255
- buildLocalizedPath('about', 'en') // → '/en/about'
256
- buildLocalizedPath('home', 'en') // → '/en'
257
- buildLocalizedPath('de/uber-uns', 'de') // → '/de/uber-uns' (no double prefix)
296
+ buildLocalizedPath('about', 'en') // → '/en/about'
297
+ buildLocalizedPath('home', 'en') // → '/en'
298
+ buildLocalizedPath('de/uber-uns', 'de') // → '/de/uber-uns' (no double prefix)
258
299
 
259
300
  // Filter: deduplicate, drop noindex, optionally restrict to one locale
260
301
  const filtered = filterSitemapEntries(entries, {
@@ -269,6 +310,48 @@ const xml = renderSitemapXml(filtered, 'https://example.com')
269
310
  const index = renderSitemapIndex(['/sitemap-en.xml', '/sitemap-de.xml'], 'https://example.com')
270
311
  ```
271
312
 
313
+ ### Locale prefixing
314
+
315
+ Entries always carry a `language_iso`, even in a space that only has one
316
+ language, so prefixing on it unconditionally would emit `/en/about` for a page
317
+ served at `/about`. `localePrefix` controls that, and defaults to `auto`:
318
+ prefix only when the entries span more than one language.
319
+
320
+ | Value | Behaviour |
321
+ | ---------------- | ------------------------------------------------------------- |
322
+ | `auto` (default) | Prefix only when the entry set is multilingual |
323
+ | `always` | Prefix every entry |
324
+ | `never` | Use paths as stored, for an app that routes the locale itself |
325
+ | `except-default` | Prefix every locale but `defaultLocale` |
326
+
327
+ ```typescript
328
+ // Nuxt i18n `prefix_except_default`
329
+ const xml = renderSitemapXml(filtered, 'https://example.com', {
330
+ localePrefix: 'except-default',
331
+ defaultLocale: 'en',
332
+ })
333
+ ```
334
+
335
+ `filterSitemapEntries` takes the same options, so its dedupe key matches the
336
+ paths you go on to render.
337
+
338
+ ### Building a sitemap in a server route
339
+
340
+ The data API paginates and unwraps for you, so a nitro route is short. In Nuxt,
341
+ `useB10cksServerApi()` from `@b10cks/nuxt` hands you the same data API:
342
+
343
+ ```typescript
344
+ // server/routes/sitemap.xml.ts
345
+ export default defineEventHandler(async (event) => {
346
+ const api = useB10cksServerApi()
347
+ const entries = await api.getSitemap({}, { allPages: true })
348
+ const siteUrl = getRequestURL(event).origin
349
+
350
+ setHeader(event, 'content-type', 'application/xml')
351
+ return renderSitemapXml(filterSitemapEntries(entries, { siteUrl }), siteUrl)
352
+ })
353
+ ```
354
+
272
355
  ## Breadcrumbs
273
356
 
274
357
  `getBreadcrumb(slug, params)` returns the ancestor trail of an entry, ordered from the tree root down to the entry itself. The entry is addressed by full slug or by content id.
@@ -280,18 +363,18 @@ trail.map((level) => [level.name, level.path])
280
363
  // → [['Startseite', '/de/startseite'], ['Produkte', '/de/startseite/produkte'], …]
281
364
  ```
282
365
 
283
- Every level is resolved through its own i18n family, so an ancestor without a translation is served from the fallback language and flagged with `is_fallback` — `resolved_language_iso` says which language it actually came from, while `path` always carries the *requested* locale segment.
366
+ Every level is resolved through its own i18n family, so an ancestor without a translation is served from the fallback language and flagged with `is_fallback` — `resolved_language_iso` says which language it actually came from, while `path` always carries the _requested_ locale segment.
284
367
 
285
368
  Unpublished ancestors are omitted from the trail rather than blanked, so the position in the array is not the position in the tree: read `depth` for that, and pass `ancestors: 'all'` when structural levels are never published by design.
286
369
 
287
- | Parameter | Default | Description |
288
- |---|---|---|
289
- | `language` / `language_iso` | space default | Language every level is resolved for |
290
- | `vid` | `published` | `published` or `draft` — a version id is not accepted |
291
- | `include_self` | `true` | Include the requested entry as the last level |
292
- | `ancestors` | `published` | `all` also returns unpublished ancestors |
293
- | `translations` | `false` | Add published sibling translations per level |
294
- | `include_content` | `false` | Add the resolved `content` payload per level; honors `take`/`except` |
370
+ | Parameter | Default | Description |
371
+ | --------------------------- | ------------- | -------------------------------------------------------------------- |
372
+ | `language` / `language_iso` | space default | Language every level is resolved for |
373
+ | `vid` | `published` | `published` or `draft` — a version id is not accepted |
374
+ | `include_self` | `true` | Include the requested entry as the last level |
375
+ | `ancestors` | `published` | `all` also returns unpublished ancestors |
376
+ | `translations` | `false` | Add published sibling translations per level |
377
+ | `include_content` | `false` | Add the resolved `content` payload per level; honors `take`/`except` |
295
378
 
296
379
  Use `getBreadcrumbResponse` when you need the `meta` block (resolved language, its fallback, the space's i18n mode, and the root/current ids).
297
380
 
@@ -318,25 +401,27 @@ import { previewBridge } from '@b10cks/client'
318
401
  // Call once on the client when your app boots (the framework packages do this for you).
319
402
  previewBridge.init({ allowedOrigins: ['https://app.b10cks.com'] })
320
403
 
321
- previewBridge.isInPreviewMode() // true only inside the editor iframe
322
- previewBridge.selectItem(blockId) // tell the editor to select a block
323
- previewBridge.selectField(blockId, path) // deep-select a nested field (e.g. rich text)
404
+ previewBridge.isInPreviewMode() // true only inside the editor iframe
405
+ previewBridge.selectItem(blockId) // tell the editor to select a block
406
+ previewBridge.selectField(blockId, path) // deep-select a nested field (e.g. rich text)
324
407
  previewBridge.updateFieldAt(blockId, ['headline'], 'New text') // stream an inline edit
325
408
 
326
409
  // Subscribe to editor → preview events; returns an unsubscribe function.
327
- const off = previewBridge.on('CONTENT_UPDATE', ({ content }) => { /* … */ })
410
+ const off = previewBridge.on('CONTENT_UPDATE', ({ content }) => {
411
+ /* … */
412
+ })
328
413
  ```
329
414
 
330
415
  Event protocol:
331
416
 
332
- | Event | Direction | Payload | Purpose |
333
- |---|---|---|---|
334
- | `SELECT_UPDATE` | both | `{ selectedItem }` | Select a block |
335
- | `HOVER_UPDATE` | editor → preview | `{ selectedItem }` | Hover-highlight a block |
336
- | `FIELD_SELECT` | preview → editor | `{ itemId, path }` | Deep-select a field so the editor opens its own editor |
337
- | `FIELD_UPDATE` | preview → editor | `{ itemId, path?, field?, value }` | Stream an inline edit |
338
- | `CONTENT_UPDATE` | editor → preview | `{ content }` | Replace the whole content tree |
339
- | `CONTENT_PATCH` | editor → preview | `{ path, value }` | Replace a single value at `path` |
417
+ | Event | Direction | Payload | Purpose |
418
+ | ---------------- | ---------------- | ---------------------------------- | ------------------------------------------------------ |
419
+ | `SELECT_UPDATE` | both | `{ selectedItem }` | Select a block |
420
+ | `HOVER_UPDATE` | editor → preview | `{ selectedItem }` | Hover-highlight a block |
421
+ | `FIELD_SELECT` | preview → editor | `{ itemId, path }` | Deep-select a field so the editor opens its own editor |
422
+ | `FIELD_UPDATE` | preview → editor | `{ itemId, path?, field?, value }` | Stream an inline edit |
423
+ | `CONTENT_UPDATE` | editor → preview | `{ content }` | Replace the whole content tree |
424
+ | `CONTENT_PATCH` | editor → preview | `{ path, value }` | Replace a single value at `path` |
340
425
 
341
426
  `path` is a `FieldPath` (`(string | number)[]`) that addresses any value at any depth, including array indices — e.g. `['body', 2, 'headline']`.
342
427
 
@@ -366,7 +451,9 @@ attachEditableField(el, { id: block.id, path: ['body'], mode: 'select' })
366
451
  When a block is selected it is scrolled into view with `block: 'nearest'` (so it never jumps when already visible) and honors the `--b10cks-scroll-offset` CSS variable as `scroll-margin-top`. Set it to your fixed header's height so selection doesn't overshoot beneath it — either in CSS:
367
452
 
368
453
  ```css
369
- :root { --b10cks-scroll-offset: 80px; }
454
+ :root {
455
+ --b10cks-scroll-offset: 80px;
456
+ }
370
457
  ```
371
458
 
372
459
  or from JS (the framework packages expose a `scrollOffset` option that calls this):
@@ -374,16 +461,28 @@ or from JS (the framework packages expose a `scrollOffset` option that calls thi
374
461
  ```typescript
375
462
  import { ensurePreviewStyles, setPreviewScrollOffset } from '@b10cks/client'
376
463
 
377
- ensurePreviewStyles() // inject the outline + scroll-margin styles once
378
- setPreviewScrollOffset(80) // number → px, or pass a string like '5rem'
464
+ ensurePreviewStyles() // inject the outline + scroll-margin styles once
465
+ setPreviewScrollOffset(80) // number → px, or pass a string like '5rem'
379
466
  ```
380
467
 
381
468
  ### `PreviewStore` — reactive live content
382
469
 
383
470
  `PreviewStore` is a framework-agnostic, subscribable holder for the content tree. `bindPreviewStore` feeds `CONTENT_UPDATE`/`CONTENT_PATCH` events into it, so any complex field — including rich text — re-renders from the new snapshot. The framework packages expose this as `usePreviewContent` / `createPreviewContent`.
384
471
 
472
+ The editor sends `CONTENT_UPDATE` scoped to the block that changed, carrying that block's `id`; only an edit of the root block pushes the whole tree (as `{ id: entryId, …entryContent }`). The store therefore merges an update by `id`:
473
+
474
+ - payload without an `id` → treated as the whole tree
475
+ - id equal to the root's id → replaces the root
476
+ - id found in the tree → replaces that node in place, immutably
477
+ - id found nowhere → ignored, so a scoped update can never collapse the page
478
+
479
+ Give the root block the entry's id with `toRootBlock` so root-level edits match it; when the root has no id, an update whose `block` type equals the root's is taken as the root.
480
+
385
481
  ```typescript
386
- import { PreviewStore, bindPreviewStore, setAtPath, getAtPath } from '@b10cks/client'
482
+ import { PreviewStore, bindPreviewStore, setAtPath, getAtPath, toRootBlock } from '@b10cks/client'
483
+
484
+ // `entry.content` has no id of its own — the id lives on the entry.
485
+ const initialContent = toRootBlock(entry) // { ...entry.content, id, block }
387
486
 
388
487
  const store = new PreviewStore(initialContent)
389
488
  const offBridge = bindPreviewStore(store)
@@ -400,22 +499,22 @@ Common types exported from the package root:
400
499
 
401
500
  ```typescript
402
501
  import type {
403
- B10cksLink, // Union type for link fields (url | email | internal | asset)
404
- B10cksLinkResolved, // Return type of resolveB10cksLink
405
- IBContent, // A content entry (generic: IBContent<YourContentShape>)
406
- IBContentBlock, // Base block shape with id and block fields
407
- IBBlock, // A block schema
408
- IBDataEntry, // A data source entry (key/value)
409
- IBDataSource, // A data source definition
410
- IBSitemapEntry, // Sitemap entry with full_slug, language_iso, meta, published_at
411
- IBSeoMeta, // robots and canonical fields
412
- IBSpace, // Space info
413
- IBRedirect, // Redirect rule (source, target, status_code)
414
- IBSearchResult, // Search result extending IBContent with relevance_score
415
- IBSearchResponse, // Wrapper around IBSearchResult[]
416
- IBCollectionResponse,// Paginated list envelope
417
- IBMeta, // Pagination metadata (total, last_page, per_page, …)
418
- IBResponse, // Single-item envelope { data, rv }
502
+ B10cksLink, // Union type for link fields (url | email | internal | asset)
503
+ B10cksLinkResolved, // Return type of resolveB10cksLink
504
+ IBContent, // A content entry (generic: IBContent<YourContentShape>)
505
+ IBContentBlock, // Base block shape with id and block fields
506
+ IBBlock, // A block schema
507
+ IBDataEntry, // A data source entry (key/value)
508
+ IBDataSource, // A data source definition
509
+ IBSitemapEntry, // Sitemap entry with full_slug, language_iso, meta, published_at
510
+ IBSeoMeta, // robots and canonical fields
511
+ IBSpace, // Space info
512
+ IBRedirect, // Redirect rule (source, target, status_code)
513
+ IBSearchResult, // Search result extending IBContent with relevance_score
514
+ IBSearchResponse, // Wrapper around IBSearchResult[]
515
+ IBCollectionResponse, // Paginated list envelope
516
+ IBMeta, // Pagination metadata (total, last_page, per_page, …)
517
+ IBResponse, // Single-item envelope { data, rv }
419
518
  B10cksApiClientOptions,
420
519
  } from '@b10cks/client'
421
520
  ```
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};function n(e,t){if(!t)return e;try{return new URL(e,t).toString()}catch{return e}}function r(e,t={}){let{siteUrl:r,excludeCurrent:i=!1}=t;return{"@context":`https://schema.org`,"@type":`BreadcrumbList`,itemListElement:(i?e.filter(e=>!e.is_current):e).map((e,t)=>({"@type":`ListItem`,position:t+1,name:e.name,item:n(e.path,r)}))}}function i(e){if(!e)return;if(e.type===`email`){let t=new URLSearchParams;e.subject&&t.set(`subject`,e.subject),e.body&&t.set(`body`,e.body),e.cc&&t.set(`cc`,e.cc),e.bcc&&t.set(`bcc`,e.bcc);let n=t.toString();return{href:`mailto:${e.email}${n?`?${n}`:``}`,target:`_self`}}if(e.type===`asset`)return;let t=e.target??`_self`,n=e.url??``;return`anchor`in e&&e.anchor&&(n=`${n.trimEnd()}#${e.anchor}`),{href:n,target:t}}function a(e){if(e!=null){if(typeof e==`boolean`||typeof e==`number`)return String(e);if(typeof e==`string`)return e;if(typeof e==`object`&&!Array.isArray(e)){let t=e;if(`null`in t)return`null:`;if(`!null`in t)return`!null:`;if(`between`in t){let e=t.between;if(!Array.isArray(e)||e.length!==2)return;let[n,r]=e;return`${n}...${r}`}for(let e of[`eq`,`neq`,`like`,`!like`,`^like`,`like$`,`lt`,`gt`,`lte`,`gte`,`in`,`!in`,`empty`,`!empty`])if(e in t){let n=t[e];return Array.isArray(n)?`${e}:${n.join(`,`)}`:`${e}:${String(n)}`}}}}function o(e){let t={};for(let[n,r]of Object.entries(e)){let e=a(r);e!==void 0&&(t[n]=e)}return t}function s(e,t,n){let r=t&&typeof t==`object`?o(t):{},i=Array.isArray(n)?{sort:n.join(`,`)}:n==null?{}:{sort:n};return{...e,...r,...i}}var c=128,l=class{client;redirectsCache=new Map;configCache=new Map;configInflight=new Map;constructor(e){this.client=e}async getResource(e,t={}){let n=await this.client.get(e,t);return this.unwrapResource(n)}async getCollection(e,t={},n={}){if(n.allPages)return this.client.getAll(e,t);let r=await this.client.get(e,t);return this.unwrapCollection(r)}async getContent(e,t={}){return this.getResource(`contents/${e}`,t)}async getContents(e={},t={}){let{filter:n,sort:r,...i}=e;return this.getCollection(`contents`,s(i,n,r),t)}async getBreadcrumb(e,t={}){return(await this.getBreadcrumbResponse(e,t))?.breadcrumb??[]}async getBreadcrumbResponse(e,t={}){let n=await this.client.get(`breadcrumbs/${e.replace(/^\/+/,``)}`,t);return this.unwrapResource(n)}async getBlock(e,t={}){return this.getResource(`blocks/${e}`,t)}async getBlocks(e={},t={}){let{filter:n,...r}=e;return this.getCollection(`blocks`,s(r,n,void 0),t)}async search(e){let t=await this.client.get(`search`,e);if(typeof t==`object`&&t&&`data`in t&&`meta`in t)return t;if(typeof t==`object`&&t&&`data`in t){let e=t.data;if(typeof e==`object`&&e&&`meta`in e&&`data`in e)return e}return t}async getSitemap(e={},t={}){return this.getCollection(`sitemap`,e,t)}async getNamedSitemap(e,t={},n={}){return this.getCollection(`sitemaps/${encodeURIComponent(e)}`,t,n)}async getDataEntries(e,t={},n={}){return this.getCollection(`datasources/${e}/entries`,t,n)}async getDataSources(e={},t={}){return this.getCollection(`datasources`,e,t)}async getSpace(e={}){return this.getResource(`spaces/me`,e)}async lookupRedirect(e){if(!this.client.post)throw Error(`lookupRedirect requires a client that supports POST. Use ApiClient from @b10cks/client.`);return this.client.post(`redirects/lookup`,{source:e})}async getRedirects(e={},t=!1){let{allPages:n=!1,forceRefresh:r=!1}=typeof t==`boolean`?{allPages:!0,forceRefresh:t}:t,i=JSON.stringify({params:e,rv:this.currentRv()});if(n&&!r){let e=this.redirectsCache.get(i);if(e)return e}let{filter:a,...o}=e,c=s(o,a,void 0),l=await this.getCollection(`redirects`,c,{allPages:n}),u=Object.fromEntries(l.map(({source:e,target:t,status_code:n})=>[e,{target:t,status_code:n}]));return n&&this.redirectsCache.set(i,u),u}currentRv(){return typeof this.client.getRv==`function`?this.client.getRv():``}async getConfig({slug:e=`_config`,language_iso:t,language:n,bypassCache:r=!1,...i}={}){let a=t??n,o=JSON.stringify({slug:e,language_iso:a,rv:this.currentRv(),...i});if(!r&&this.configCache.has(o))return this.configCache.get(o);let s=this.configInflight.get(o);if(!r&&s)return s;let c=this.getContent(e,{...i,language_iso:a}).then(e=>{let t=e.content??{};return r||this.setConfigCache(o,t),t}).finally(()=>{this.configInflight.delete(o)});return r||this.configInflight.set(o,c),c}setConfigCache(e,t){if(this.configCache.delete(e),this.configCache.set(e,t),this.configCache.size>c){let e=this.configCache.keys().next().value;e!==void 0&&this.configCache.delete(e)}}async syncRevision(e=426713400){let t=(await this.getSpace()).rv||e;return this.client.setRv(t),t}clearCache(){this.redirectsCache.clear(),this.configCache.clear(),this.configInflight.clear()}unwrapResource(e){return typeof e==`object`&&e&&`data`in e?e.data:e}unwrapCollection(e){if(Array.isArray(e))return e;if(typeof e==`object`&&e&&`data`in e){if(Array.isArray(e.data))return e.data;if(typeof e.data==`object`&&e.data!==null&&`data`in e.data&&Array.isArray(e.data.data))return e.data.data}return[]}};function u(e){return new l(e)}var d=new Set([`CONTENT_UPDATE`,`CONTENT_PATCH`,`SELECT_UPDATE`,`HOVER_UPDATE`,`FIELD_UPDATE`,`FIELD_SELECT`]),f=`B10CKS_BRIDGE_READY`,p=class e{static instance;listeners={};initialized=!1;allowedOrigins=null;parentOrigin=null;constructor(){}static getInstance(){return e.instance||=new e,e.instance}init(e={}){if(this.isInPreviewMode()){if(this.initialized){e.allowedOrigins&&!this.allowedOrigins&&(this.allowedOrigins=e.allowedOrigins,this.parentOrigin&&!this.allowedOrigins.includes(this.parentOrigin)&&(this.parentOrigin=null));return}this.allowedOrigins=e.allowedOrigins??null,window.addEventListener(`message`,this.handleMessage),this.initialized=!0,window.parent.postMessage({type:f},`*`)}}destroy(){this.initialized&&typeof window<`u`&&window.removeEventListener(`message`,this.handleMessage),this.initialized=!1,this.parentOrigin=null;for(let e of Object.keys(this.listeners))this.listeners[e]?.clear()}isInPreviewMode(){return typeof window<`u`&&window.self!==window.top}on(e,t){if(!this.isInPreviewMode())return()=>{};let n=this.listeners[e]||new Set;return this.listeners[e]=n,n.add(t),()=>{n.delete(t)}}selectItem(e){this.post(`SELECT_UPDATE`,{selectedItem:e})}selectField(e,t){this.post(`FIELD_SELECT`,{itemId:e,path:t})}updateField(e,t,n){this.post(`FIELD_UPDATE`,{itemId:e,field:t,value:n})}updateFieldAt(e,t,n){this.post(`FIELD_UPDATE`,{itemId:e,path:t,value:n})}post(e,t){this.isInPreviewMode()&&window.parent.postMessage({type:e,payload:t},this.parentOrigin??`*`)}handleMessage=e=>{if(!this.isOriginTrusted(e.origin)||!e.data||typeof e.data!=`object`)return;let{type:t,payload:n}=e.data;!t||!d.has(t)||!n||(!this.parentOrigin&&e.origin&&e.origin!==`null`&&(this.parentOrigin=e.origin),this.notify(t,n))};isOriginTrusted(e){return this.parentOrigin?e===this.parentOrigin:!this.allowedOrigins||this.allowedOrigins.includes(e)}notify(e,t){let n=this.listeners[e];if(n)for(let e of n)e(t)}},m=p.getInstance(),h=`b10cks-preview-style`,g=`--b10cks-scroll-offset`;function _(){if(typeof document>`u`||document.getElementById(h))return;let e=document.createElement(`style`);e.id=h,e.textContent=`
2
- .b10cks-preview { scroll-margin-top: var(${g}, 0px); }
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};function n(e,t){if(!t)return e;try{return new URL(e,t).toString()}catch{return e}}function r(e,t={}){let{siteUrl:r,excludeCurrent:i=!1}=t;return{"@context":`https://schema.org`,"@type":`BreadcrumbList`,itemListElement:(i?e.filter(e=>!e.is_current):e).map((e,t)=>({"@type":`ListItem`,position:t+1,name:e.name,item:n(e.path,r)}))}}function i(e){if(!e)return null;let t=e.content??{};return{...t,id:e.id,block:t.block??e.block}}function a(e){if(!e)return;if(e.type===`email`){let t=new URLSearchParams;e.subject&&t.set(`subject`,e.subject),e.body&&t.set(`body`,e.body),e.cc&&t.set(`cc`,e.cc),e.bcc&&t.set(`bcc`,e.bcc);let n=t.toString();return{href:`mailto:${e.email}${n?`?${n}`:``}`,target:`_self`}}if(e.type===`asset`)return;let t=e.target??`_self`,n=(e.url??``).trimEnd(),r=e.params?new URLSearchParams(e.params).toString():``;if(r){let e=n.indexOf(`#`),t=e===-1?n:n.slice(0,e),i=e===-1?``:n.slice(e);n=`${t}${t.includes(`?`)?`&`:`?`}${r}${i}`}return e.anchor&&!n.includes(`#`)&&(n=`${n}#${encodeURIComponent(e.anchor)}`),{href:n,target:t}}function o(e){return e?.id?{id:e.id}:{}}function s(e){if(e!=null){if(typeof e==`boolean`||typeof e==`number`)return String(e);if(typeof e==`string`)return e;if(typeof e==`object`&&!Array.isArray(e)){let t=e;if(`null`in t)return`null:`;if(`!null`in t)return`!null:`;if(`between`in t){let e=t.between;if(!Array.isArray(e)||e.length!==2)return;let[n,r]=e;return`${n}...${r}`}for(let e of[`eq`,`neq`,`like`,`!like`,`^like`,`like$`,`lt`,`gt`,`lte`,`gte`,`in`,`!in`,`empty`,`!empty`])if(e in t){let n=t[e];return Array.isArray(n)?`${e}:${n.join(`,`)}`:`${e}:${String(n)}`}}}}function c(e){let t={};for(let[n,r]of Object.entries(e)){let e=s(r);e!==void 0&&(t[n]=e)}return t}function l(e,t,n){let r=t&&typeof t==`object`?c(t):{},i=Array.isArray(n)?{sort:n.join(`,`)}:n==null?{}:{sort:n};return{...e,...r,...i}}var u=128,d=class{client;redirectsCache=new Map;configCache=new Map;configInflight=new Map;constructor(e){this.client=e}async getResource(e,t={}){let n=await this.client.get(e,t);return this.unwrapResource(n)}async getCollection(e,t={},n={}){if(n.allPages)return this.client.getAll(e,t);let r=await this.client.get(e,t);return this.unwrapCollection(r)}async getContent(e,t={}){return this.getResource(`contents/${e}`,t)}async getContents(e={},t={}){let{filter:n,sort:r,...i}=e;return this.getCollection(`contents`,l(i,n,r),t)}async getBreadcrumb(e,t={}){return(await this.getBreadcrumbResponse(e,t))?.breadcrumb??[]}async getBreadcrumbResponse(e,t={}){let n=await this.client.get(`breadcrumbs/${e.replace(/^\/+/,``)}`,t);return this.unwrapResource(n)}async getBlock(e,t={}){return this.getResource(`blocks/${e}`,t)}async getBlocks(e={},t={}){let{filter:n,...r}=e;return this.getCollection(`blocks`,l(r,n,void 0),t)}async search(e){let t=await this.client.get(`search`,e);if(typeof t==`object`&&t&&`data`in t&&`meta`in t)return t;if(typeof t==`object`&&t&&`data`in t){let e=t.data;if(typeof e==`object`&&e&&`meta`in e&&`data`in e)return e}return t}async getSitemap(e={},t={}){return this.getCollection(`sitemap`,e,t)}async getNamedSitemap(e,t={},n={}){return this.getCollection(`sitemaps/${encodeURIComponent(e)}`,t,n)}async getDataEntries(e,t={},n={}){return this.getCollection(`datasources/${e}/entries`,t,n)}async getDataSources(e={},t={}){return this.getCollection(`datasources`,e,t)}async getSpace(e={}){return this.getResource(`spaces/me`,e)}async lookupRedirect(e){if(!this.client.post)throw Error(`lookupRedirect requires a client that supports POST. Use ApiClient from @b10cks/client.`);return this.client.post(`redirects/lookup`,{source:e})}async getRedirects(e={},t=!1){let{allPages:n=!1,forceRefresh:r=!1}=typeof t==`boolean`?{allPages:!0,forceRefresh:t}:t,i=JSON.stringify({params:e,rv:this.currentRv()});if(n&&!r){let e=this.redirectsCache.get(i);if(e)return e}let{filter:a,...o}=e,s=l(o,a,void 0),c=await this.getCollection(`redirects`,s,{allPages:n}),u=Object.fromEntries(c.map(({source:e,target:t,status_code:n})=>[e,{target:t,status_code:n}]));return n&&this.redirectsCache.set(i,u),u}currentRv(){return typeof this.client.getRv==`function`?this.client.getRv():``}async getConfig({slug:e=`_config`,language_iso:t,language:n,bypassCache:r=!1,...i}={}){let a=t??n,o=JSON.stringify({slug:e,language_iso:a,rv:this.currentRv(),...i});if(!r&&this.configCache.has(o))return this.configCache.get(o);let s=this.configInflight.get(o);if(!r&&s)return s;let c=this.getContent(e,{...i,language_iso:a}).then(e=>{let t={...e.content??{},id:e.id};return r||this.setConfigCache(o,t),t}).finally(()=>{this.configInflight.delete(o)});return r||this.configInflight.set(o,c),c}setConfigCache(e,t){if(this.configCache.delete(e),this.configCache.set(e,t),this.configCache.size>u){let e=this.configCache.keys().next().value;e!==void 0&&this.configCache.delete(e)}}async syncRevision(e=426713400){let t=(await this.getSpace()).rv||e;return this.client.setRv(t),t}clearCache(){this.redirectsCache.clear(),this.configCache.clear(),this.configInflight.clear()}unwrapResource(e){return typeof e==`object`&&e&&`data`in e?e.data:e}unwrapCollection(e){if(Array.isArray(e))return e;if(typeof e==`object`&&e&&`data`in e){if(Array.isArray(e.data))return e.data;if(typeof e.data==`object`&&e.data!==null&&`data`in e.data&&Array.isArray(e.data.data))return e.data.data}return[]}};function f(e){return new d(e)}var p=new Set([`CONTENT_UPDATE`,`CONTENT_PATCH`,`SELECT_UPDATE`,`HOVER_UPDATE`,`FIELD_UPDATE`,`FIELD_SELECT`]),m=`B10CKS_BRIDGE_READY`,h=class e{static instance;listeners={};initialized=!1;allowedOrigins=null;parentOrigin=null;constructor(){}static getInstance(){return e.instance||=new e,e.instance}init(e={}){if(this.isInPreviewMode()){if(this.initialized){e.allowedOrigins&&!this.allowedOrigins&&(this.allowedOrigins=e.allowedOrigins,this.parentOrigin&&!this.allowedOrigins.includes(this.parentOrigin)&&(this.parentOrigin=null));return}this.allowedOrigins=e.allowedOrigins??null,window.addEventListener(`message`,this.handleMessage),this.initialized=!0,window.parent.postMessage({type:m},`*`)}}destroy(){this.initialized&&typeof window<`u`&&window.removeEventListener(`message`,this.handleMessage),this.initialized=!1,this.parentOrigin=null;for(let e of Object.keys(this.listeners))this.listeners[e]?.clear()}isInPreviewMode(){return typeof window<`u`&&window.self!==window.top}on(e,t){if(!this.isInPreviewMode())return()=>{};let n=this.listeners[e]||new Set;return this.listeners[e]=n,n.add(t),()=>{n.delete(t)}}selectItem(e){this.post(`SELECT_UPDATE`,{selectedItem:e})}selectField(e,t){this.post(`FIELD_SELECT`,{itemId:e,path:t})}updateField(e,t,n){this.post(`FIELD_UPDATE`,{itemId:e,field:t,value:n})}updateFieldAt(e,t,n){this.post(`FIELD_UPDATE`,{itemId:e,path:t,value:n})}post(e,t){this.isInPreviewMode()&&window.parent.postMessage({type:e,payload:t},this.parentOrigin??`*`)}handleMessage=e=>{if(!this.isOriginTrusted(e.origin)||!e.data||typeof e.data!=`object`)return;let{type:t,payload:n}=e.data;!t||!p.has(t)||!n||(!this.parentOrigin&&e.origin&&e.origin!==`null`&&(this.parentOrigin=e.origin),this.notify(t,n))};isOriginTrusted(e){return this.parentOrigin?e===this.parentOrigin:!this.allowedOrigins||this.allowedOrigins.includes(e)}notify(e,t){let n=this.listeners[e];if(n)for(let e of n)e(t)}},g=h.getInstance(),_=`b10cks-preview-style`,v=`--b10cks-scroll-offset`;function y(){if(typeof document>`u`||document.getElementById(_))return;let e=document.createElement(`style`);e.id=_,e.textContent=`
2
+ .b10cks-preview { scroll-margin-top: var(${v}, 0px); }
3
3
  .b10cks-hover,
4
4
  .b10cks-preview:hover {
5
5
  outline: 2px dashed rgba(59, 130, 246, 0.5);
@@ -9,11 +9,11 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.
9
9
  outline: 2px solid rgb(59, 130, 246) !important;
10
10
  outline-offset: 2px;
11
11
  }
12
- `,document.head.appendChild(e)}function v(e){if(typeof document>`u`)return;let t=typeof e==`number`?`${e}px`:e;document.documentElement.style.setProperty(g,t)}function y(e,t){if(!m.isInPreviewMode()||!t.id)return()=>{};_();let{id:n,onSelectChange:r,onHoverChange:i,scrollOnSelect:a=!0}=t;e.classList.add(`b10cks-preview`);let o=e=>{e.preventDefault(),e.stopPropagation(),m.selectItem(n)},s=({selectedItem:t})=>{let i=t===n;e.classList.toggle(`b10cks-selected`,i),i&&a&&x(e),r?.(i)},c=({selectedItem:t})=>{let r=t===n;e.classList.toggle(`b10cks-hover`,r),i?.(r)};e.addEventListener(`click`,o);let l=m.on(`SELECT_UPDATE`,s),u=m.on(`HOVER_UPDATE`,c);return()=>{e.removeEventListener(`click`,o),l(),u(),e.classList.remove(`b10cks-preview`,`b10cks-selected`,`b10cks-hover`)}}function b(e,t){if(!m.isInPreviewMode()||!t.id)return()=>{};let{id:n,field:r,path:i,mode:a=`inline`}=t;if(a===`select`){_(),e.classList.add(`b10cks-preview`);let t=e=>{e.preventDefault(),e.stopPropagation(),i?m.selectField(n,i):m.selectItem(n)};return e.addEventListener(`click`,t),()=>{e.removeEventListener(`click`,t),e.classList.remove(`b10cks-preview`)}}e.setAttribute(`contenteditable`,`true`);let o=e=>{let t=e.target.innerText;i?m.updateFieldAt(n,i,t):m.updateField(n,r??``,t)};return e.addEventListener(`input`,o),()=>{e.removeEventListener(`input`,o),e.removeAttribute(`contenteditable`)}}function x(e){typeof e.scrollIntoView==`function`&&e.scrollIntoView({block:`nearest`,behavior:`smooth`})}function S(e,t){let n=e;for(let e of t){if(typeof n!=`object`||!n)return;n=n[e]}return n}function C(e,t,n){if(t.length===0)return n;let[r,...i]=t;if(r===void 0)return n;if(typeof r==`number`){let t=Array.isArray(e)?e.slice():[];return t[r]=C(t[r],i,n),t}let a=e&&typeof e==`object`&&!Array.isArray(e)?{...e}:{};return a[r]=C(a[r],i,n),a}var w=class{content;listeners=new Set;constructor(e){this.content=e}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});getSnapshot=()=>this.content;setContent(e){this.content=e,this.emit()}patch(e,t){this.content=C(this.content,e,t),this.emit()}emit(){for(let e of this.listeners)e()}};function T(e){let t=m.on(`CONTENT_UPDATE`,({content:t})=>{e.setContent(t)}),n=m.on(`CONTENT_PATCH`,({path:t,value:n})=>{e.patch(t,n)});return()=>{t(),n()}}function E(e){return(e??``).trim().replace(/^\/+|\/+$/g,``)}function D(e,t){let n=E(e),r=E(t);if(!r)return n&&n!==`home`?`/${n}`:`/`;if(!n||n===`home`)return`/${r}`;let i=`${r}/`;return n===r||n.startsWith(i)?`/${n}`:`/${r}/${n}`}function O(e,t){if(!t)return null;try{return new URL(e,t).toString()}catch{return null}}function k(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&apos;`)}function A(e,t={}){let{siteUrl:n,locale:r}=t,i=new Set;return e.filter(e=>{if(r&&e.language_iso!==r)return!1;let t=e.meta?.robots?.toLowerCase();if(t?.includes(`noindex`)||t?.includes(`none`))return!1;let a=D(e.full_slug,e.language_iso),o=n?O(a,n)??a:a;return i.has(o)?!1:(i.add(o),!0)})}function j(e,t){return[`<?xml version="1.0" encoding="UTF-8"?>`,`<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`,e.map(e=>{let n=D(e.full_slug,e.language_iso),r=t?O(n,t):n;if(!r)return``;let i=[` <url>`,` <loc>${k(r)}</loc>`];return e.published_at&&i.push(` <lastmod>${k(e.published_at)}</lastmod>`),i.push(` </url>`),i.join(`
12
+ `,document.head.appendChild(e)}function b(e){if(typeof document>`u`)return;let t=typeof e==`number`?`${e}px`:e;document.documentElement.style.setProperty(v,t)}function x(e,t){if(!g.isInPreviewMode()||!t.id)return()=>{};y();let{id:n,onSelectChange:r,onHoverChange:i,scrollOnSelect:a=!0}=t;e.classList.add(`b10cks-preview`);let o=e=>{e.preventDefault(),e.stopPropagation(),g.selectItem(n)},s=({selectedItem:t})=>{let i=t===n;e.classList.toggle(`b10cks-selected`,i),i&&a&&C(e),r?.(i)},c=({selectedItem:t})=>{let r=t===n;e.classList.toggle(`b10cks-hover`,r),i?.(r)};e.addEventListener(`click`,o);let l=g.on(`SELECT_UPDATE`,s),u=g.on(`HOVER_UPDATE`,c);return()=>{e.removeEventListener(`click`,o),l(),u(),e.classList.remove(`b10cks-preview`,`b10cks-selected`,`b10cks-hover`)}}function S(e,t){if(!g.isInPreviewMode()||!t.id)return()=>{};let{id:n,field:r,path:i,mode:a=`inline`}=t;if(a===`select`){y(),e.classList.add(`b10cks-preview`);let t=e=>{e.preventDefault(),e.stopPropagation(),i?g.selectField(n,i):g.selectItem(n)};return e.addEventListener(`click`,t),()=>{e.removeEventListener(`click`,t),e.classList.remove(`b10cks-preview`)}}e.setAttribute(`contenteditable`,`true`);let o=e=>{let t=e.target.innerText;i?g.updateFieldAt(n,i,t):g.updateField(n,r??``,t)};return e.addEventListener(`input`,o),()=>{e.removeEventListener(`input`,o),e.removeAttribute(`contenteditable`)}}function C(e){typeof e.scrollIntoView==`function`&&e.scrollIntoView({block:`nearest`,behavior:`smooth`})}function w(e,t){let n=e;for(let e of t){if(typeof n!=`object`||!n)return;n=n[e]}return n}function T(e,t,n){if(t.length===0)return n;let[r,...i]=t;if(r===void 0)return n;if(typeof r==`number`){let t=Array.isArray(e)?e.slice():[];return t[r]=T(t[r],i,n),t}let a=e&&typeof e==`object`&&!Array.isArray(e)?{...e}:{};return a[r]=T(a[r],i,n),a}function E(e,t){let n=(e,r)=>{if(Array.isArray(e)){for(let t=0;t<e.length;t++){let i=n(e[t],[...r,t]);if(i)return i}return null}if(!e||typeof e!=`object`)return null;let i=e;if(r.length>0&&i.id===t)return r;for(let e of Object.keys(i)){let t=i[e];if(t&&typeof t==`object`){let i=n(t,[...r,e]);if(i)return i}}return null};return n(e,[])}function D(e,t){if(!t||typeof t!=`object`)return e;let n=typeof t.id==`string`?t.id:void 0;if(!n||!e||typeof e!=`object`)return t;let r=e,i=typeof r.id==`string`?r.id:void 0;if(i===n)return t;let a=E(e,n);return a?T(e,a,t):!i&&r.block!==void 0&&r.block===t.block?t:e}var O=class{content;listeners=new Set;constructor(e){this.content=e}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});getSnapshot=()=>this.content;setContent(e){this.content=e,this.emit()}applyContentUpdate(e){let t=D(this.content,e);t!==this.content&&(this.content=t,this.emit())}patch(e,t){this.content=T(this.content,e,t),this.emit()}emit(){for(let e of this.listeners)e()}};function k(e){let t=g.on(`CONTENT_UPDATE`,({content:t})=>{e.applyContentUpdate(t)}),n=g.on(`CONTENT_PATCH`,({path:t,value:n})=>{e.patch(t,n)});return()=>{t(),n()}}function A(e){return(e??``).trim().replace(/^\/+|\/+$/g,``)}function j(e,t){let n=A(e),r=A(t);if(!r)return n&&n!==`home`?`/${n}`:`/`;if(!n||n===`home`)return`/${r}`;let i=`${r}/`;return n===r||n.startsWith(i)?`/${n}`:`/${r}/${n}`}function M(e,t){if(!t)return null;try{return new URL(e,t).toString()}catch{return null}}function N(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&apos;`)}function P(e,t,n){let{localePrefix:r=`auto`,defaultLocale:i}=t;return r===`always`||r===`auto`&&n||r===`except-default`&&e.language_iso!==i?j(e.full_slug,e.language_iso):j(e.full_slug,null)}function F(e){let t=new Set;for(let n of e)if(n.language_iso&&t.add(n.language_iso),t.size>1)return!0;return!1}function I(e,t={}){let{siteUrl:n,locale:r}=t,i=new Set,a=F(e);return e.filter(e=>{if(r&&e.language_iso!==r)return!1;let o=e.meta?.robots?.toLowerCase();if(o?.includes(`noindex`)||o?.includes(`none`))return!1;let s=P(e,t,a),c=n?M(s,n)??s:s;return i.has(c)?!1:(i.add(c),!0)})}function L(e,t,n={}){let r=F(e);return[`<?xml version="1.0" encoding="UTF-8"?>`,`<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`,e.map(e=>{let i=P(e,n,r),a=t?M(i,t):i;if(!a)return``;let o=[` <url>`,` <loc>${N(a)}</loc>`];return e.published_at&&o.push(` <lastmod>${N(e.published_at)}</lastmod>`),o.push(` </url>`),o.join(`
13
13
  `)}).filter(Boolean).join(`
14
14
  `),`</urlset>`].join(`
15
- `)}function M(e,t){return[`<?xml version="1.0" encoding="UTF-8"?>`,`<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`,e.map(e=>{let n=t?O(e,t):e;return n?[` <sitemap>`,` <loc>${k(n)}</loc>`,` </sitemap>`].join(`
15
+ `)}function R(e,t){return[`<?xml version="1.0" encoding="UTF-8"?>`,`<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`,e.map(e=>{let n=t?M(e,t):e;return n?[` <sitemap>`,` <loc>${N(n)}</loc>`,` </sitemap>`].join(`
16
16
  `):``}).filter(Boolean).join(`
17
17
  `),`</sitemapindex>`].join(`
18
- `)}var N=t({}),P=class extends Error{status;endpoint;body;constructor(e,t,n,r){super(e),this.status=t,this.endpoint=n,this.body=r,this.name=`ApiError`}},F=e=>e===429||e>=500&&e<=599,I=e=>new Promise(t=>setTimeout(t,e)),L=class{baseUrl;token;vid;fetchClient;getRvFn;setRvFn;timeoutMs;retries;maxConcurrency;rvValue=0;constructor(e,t){this.baseUrl=e.baseUrl.endsWith(`/`)?e.baseUrl.slice(0,-1):e.baseUrl,this.token=e.token;let n=typeof globalThis.fetch==`function`?globalThis.fetch.bind(globalThis):null;if(e.fetchClient)this.fetchClient=e.fetchClient;else if(n)this.fetchClient=n;else throw Error("No fetch implementation available. Provide `fetchClient` in ApiClient options.");this.getRvFn=e.getRv,this.setRvFn=e.setRv,this.timeoutMs=e.timeoutMs,this.retries=Math.max(0,e.retries??0),this.maxConcurrency=Math.max(1,e.maxConcurrency??6);let r=this.resolveRequestUrl(t);this.vid=e.version||`published`,this.setRv(r?.searchParams.get(`b10cks_rv`)||e.rv||0)}async get(e,t={}){let n=this.buildUrl(e,{vid:this.vid,rv:this.getRv(),...t,token:this.token}),r=await this.requestWithRetry(n,e);return this.hasRevision(r)&&this.setRv(r.rv),r}async post(e,t,n={}){let r=this.buildUrl(e,{vid:this.vid,rv:this.getRv(),...n,token:this.token}),i=await this.fetchWithTimeout(r,{method:`POST`,headers:{"Content-Type":`application/json`},body:t===void 0?void 0:JSON.stringify(t)});return this.parseResponse(i,e)}async fetchWithTimeout(e,t){if(!this.timeoutMs)return this.fetchClient(e,t);let n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs);try{return await this.fetchClient(e,{...t,signal:n.signal})}catch(t){throw t instanceof Error&&t.name===`AbortError`?new P(`Request timed out after ${this.timeoutMs}ms`,0,e):t}finally{clearTimeout(r)}}async requestWithRetry(e,t){let n;for(let r=0;r<=this.retries;r++)try{let n=await this.fetchWithTimeout(e);return await this.parseResponse(n,t)}catch(e){if(n=e,!(r<this.retries&&(!(e instanceof P)||e.status===0||F(e.status))))throw e;await I(200*2**r)}throw n}async getAll(e,t={}){let n=await this.get(e,{...t,page:1}),r=this.normalizeCollectionResponse(n);if(r.rv&&this.setRv(r.rv),!r.meta||r.meta.last_page<=1)return r.data;let i=Array.from({length:r.meta.last_page-1},(e,t)=>t+2),a=await this.mapWithConcurrency(i,n=>this.get(e,{...t,page:n}));return r.data.concat(a.flatMap(e=>this.normalizeCollectionResponse(e).data))}async mapWithConcurrency(e,t){let n=Array.from({length:e.length}),r=0,i=async()=>{for(;r<e.length;){let i=r++;n[i]=await t(e[i])}},a=Array.from({length:Math.min(this.maxConcurrency,e.length)},()=>i());return await Promise.all(a),n}setRv(e){this.setRvFn?this.setRvFn(e):this.rvValue=e}getRv(){return this.getRvFn?this.getRvFn():this.rvValue}async parseResponse(e,t){if(this.isFetchResponse(e)){if(!e.ok){let n=await this.readErrorBody(e);throw new P(`Request to "${t}" failed with status ${e.status}`,e.status,t,n)}return await e.json()}return e}async readErrorBody(e){try{let t=await e.text();if(!t)return;try{return JSON.parse(t)}catch{return t}}catch{return}}normalizeCollectionResponse(e){if(this.isCollectionResponseEnvelope(e)){let t=e.data;return{data:t.data,meta:t.meta,rv:e.rv||t.rv}}if(Array.isArray(e))return{data:e,meta:void 0,rv:void 0};if(typeof e==`object`&&e&&`data`in e){let{data:t,meta:n,rv:r}=e;return{data:t,meta:n,rv:r}}return{data:[],meta:void 0,rv:void 0}}buildUrl(e,t){let n=new URL(`${this.baseUrl}/v1/${e}`);return Object.entries(t).forEach(([e,t])=>{t!=null&&n.searchParams.append(e,String(t))}),n.toString()}resolveRequestUrl(e){return e?e instanceof URL?e:new URL(e,`http://localhost`):null}isFetchResponse(e){return typeof e==`object`&&!!e&&`json`in e&&typeof e.json==`function`&&`ok`in e}hasRevision(e){return typeof e==`object`&&!!e&&`rv`in e&&e.rv!=null}isCollectionResponseEnvelope(e){return typeof e==`object`&&!!e&&`data`in e&&typeof e.data==`object`&&e.data!==null&&`data`in e.data&&Array.isArray(e.data.data)}};exports.ApiClient=L,exports.ApiError=P,exports.B10cksDataApi=l,exports.PreviewBridge=p,exports.PreviewStore=w,exports.attachEditable=y,exports.attachEditableField=b,exports.bindPreviewStore=T,exports.breadcrumbJsonLd=r,exports.buildLocalizedPath=D,exports.createB10cksDataApi=u,exports.ensurePreviewStyles=_,exports.filterSitemapEntries=A,exports.getAtPath=S,exports.normalizePathSegment=E,exports.previewBridge=m,exports.renderSitemapIndex=M,exports.renderSitemapXml=j,exports.resolveB10cksLink=i,exports.serializeFilter=o,exports.setAtPath=C,exports.setPreviewScrollOffset=v,Object.defineProperty(exports,"types",{enumerable:!0,get:function(){return N}});
18
+ `)}var z=t({}),B=class extends Error{status;endpoint;body;constructor(e,t,n,r){super(e),this.status=t,this.endpoint=n,this.body=r,this.name=`ApiError`}},V=e=>e===429||e>=500&&e<=599,H=e=>new Promise(t=>setTimeout(t,e)),U=class{baseUrl;token;vid;fetchClient;getRvFn;setRvFn;timeoutMs;retries;maxConcurrency;rvValue=0;constructor(e,t){this.baseUrl=e.baseUrl.endsWith(`/`)?e.baseUrl.slice(0,-1):e.baseUrl,this.token=e.token;let n=typeof globalThis.fetch==`function`?globalThis.fetch.bind(globalThis):null;if(e.fetchClient)this.fetchClient=e.fetchClient;else if(n)this.fetchClient=n;else throw Error("No fetch implementation available. Provide `fetchClient` in ApiClient options.");this.getRvFn=e.getRv,this.setRvFn=e.setRv,this.timeoutMs=e.timeoutMs,this.retries=Math.max(0,e.retries??0),this.maxConcurrency=Math.max(1,e.maxConcurrency??6);let r=this.resolveRequestUrl(t);this.vid=e.version||`published`,this.setRv(r?.searchParams.get(`b10cks_rv`)||e.rv||0)}async get(e,t={}){let n=this.buildUrl(e,{vid:this.vid,rv:this.getRv(),...t,token:this.token}),r=await this.requestWithRetry(n,e);return this.hasRevision(r)&&this.setRv(r.rv),r}async post(e,t,n={}){let r=this.buildUrl(e,{vid:this.vid,rv:this.getRv(),...n,token:this.token}),i=await this.fetchWithTimeout(r,{method:`POST`,headers:{"Content-Type":`application/json`},body:t===void 0?void 0:JSON.stringify(t)});return this.parseResponse(i,e)}async fetchWithTimeout(e,t){if(!this.timeoutMs)return this.fetchClient(e,t);let n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs);try{return await this.fetchClient(e,{...t,signal:n.signal})}catch(t){throw t instanceof Error&&t.name===`AbortError`?new B(`Request timed out after ${this.timeoutMs}ms`,0,e):t}finally{clearTimeout(r)}}async requestWithRetry(e,t){let n;for(let r=0;r<=this.retries;r++)try{let n=await this.fetchWithTimeout(e);return await this.parseResponse(n,t)}catch(e){if(n=e,!(r<this.retries&&(!(e instanceof B)||e.status===0||V(e.status))))throw e;await H(200*2**r)}throw n}async getAll(e,t={}){let n=await this.get(e,{...t,page:1}),r=this.normalizeCollectionResponse(n);if(r.rv&&this.setRv(r.rv),!r.meta||r.meta.last_page<=1)return r.data;let i=Array.from({length:r.meta.last_page-1},(e,t)=>t+2),a=await this.mapWithConcurrency(i,n=>this.get(e,{...t,page:n}));return r.data.concat(a.flatMap(e=>this.normalizeCollectionResponse(e).data))}async mapWithConcurrency(e,t){let n=Array.from({length:e.length}),r=0,i=async()=>{for(;r<e.length;){let i=r++;n[i]=await t(e[i])}},a=Array.from({length:Math.min(this.maxConcurrency,e.length)},()=>i());return await Promise.all(a),n}setRv(e){this.setRvFn?this.setRvFn(e):this.rvValue=e}getRv(){return this.getRvFn?this.getRvFn():this.rvValue}async parseResponse(e,t){if(this.isFetchResponse(e)){if(!e.ok){let n=await this.readErrorBody(e);throw new B(`Request to "${t}" failed with status ${e.status}`,e.status,t,n)}return await e.json()}return e}async readErrorBody(e){try{let t=await e.text();if(!t)return;try{return JSON.parse(t)}catch{return t}}catch{return}}normalizeCollectionResponse(e){if(this.isCollectionResponseEnvelope(e)){let t=e.data;return{data:t.data,meta:t.meta,rv:e.rv||t.rv}}if(Array.isArray(e))return{data:e,meta:void 0,rv:void 0};if(typeof e==`object`&&e&&`data`in e){let{data:t,meta:n,rv:r}=e;return{data:t,meta:n,rv:r}}return{data:[],meta:void 0,rv:void 0}}buildUrl(e,t){let n=new URL(`${this.baseUrl}/v1/${e}`);return Object.entries(t).forEach(([e,t])=>{t!=null&&n.searchParams.append(e,String(t))}),n.toString()}resolveRequestUrl(e){return e?e instanceof URL?e:new URL(e,`http://localhost`):null}isFetchResponse(e){return typeof e==`object`&&!!e&&`json`in e&&typeof e.json==`function`&&`ok`in e}hasRevision(e){return typeof e==`object`&&!!e&&`rv`in e&&e.rv!=null}isCollectionResponseEnvelope(e){return typeof e==`object`&&!!e&&`data`in e&&typeof e.data==`object`&&e.data!==null&&`data`in e.data&&Array.isArray(e.data.data)}};exports.ApiClient=U,exports.ApiError=B,exports.B10cksDataApi=d,exports.PreviewBridge=h,exports.PreviewStore=O,exports.attachEditable=x,exports.attachEditableField=S,exports.bindPreviewStore=k,exports.blockAnchorAttrs=o,exports.breadcrumbJsonLd=r,exports.buildLocalizedPath=j,exports.createB10cksDataApi=f,exports.ensurePreviewStyles=y,exports.filterSitemapEntries=I,exports.findPathById=E,exports.getAtPath=w,exports.mergeContentUpdate=D,exports.normalizePathSegment=A,exports.previewBridge=g,exports.renderSitemapIndex=R,exports.renderSitemapXml=L,exports.resolveB10cksLink=a,exports.serializeFilter=c,exports.setAtPath=T,exports.setPreviewScrollOffset=b,exports.toRootBlock=i,Object.defineProperty(exports,"types",{enumerable:!0,get:function(){return z}});
19
19
  //# sourceMappingURL=index.cjs.map