@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 +207 -108
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +121 -8
- package/dist/index.mjs +139 -74
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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,
|
|
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
|
|
86
|
-
|
|
87
|
-
| `'value'`
|
|
88
|
-
| `{ eq: 'v' }`
|
|
89
|
-
| `{ neq: 'v' }`
|
|
90
|
-
| `{ in: ['a','b'] }`
|
|
91
|
-
| `{ '!in': ['a','b'] }`
|
|
92
|
-
| `{ like: 'v' }`
|
|
93
|
-
| `{ '!like': 'v' }`
|
|
94
|
-
| `{ '^like': 'v' }`
|
|
95
|
-
| `{ 'like$': 'v' }`
|
|
96
|
-
| `{ gte: 'v' }`
|
|
97
|
-
| `{ gt: 'v' }`
|
|
98
|
-
| `{ lte: 'v' }`
|
|
99
|
-
| `{ lt: 'v' }`
|
|
100
|
-
| `{ between: ['a','b'] }` | `a...b`
|
|
101
|
-
| `{ null: true }`
|
|
102
|
-
| `{ '!null': true }`
|
|
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'
|
|
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
|
|
132
|
-
|
|
133
|
-
| `getContent(slug, params)`
|
|
134
|
-
| `getContents(params, options)`
|
|
135
|
-
| `getBreadcrumb(slug, params)`
|
|
136
|
-
| `getBreadcrumbResponse(slug, params)`
|
|
137
|
-
| `getBlock(blockId, params)`
|
|
138
|
-
| `getBlocks(params, options)`
|
|
139
|
-
| `search(params)`
|
|
140
|
-
| `lookupRedirect(source)`
|
|
141
|
-
| `getRedirects(params, options)`
|
|
142
|
-
| `getDataEntries(source, params, options)` | Entries for a data source slug
|
|
143
|
-
| `getDataSources(params, options)`
|
|
144
|
-
| `getSitemap(params, options)`
|
|
145
|
-
| `getNamedSitemap(name, params, options)`
|
|
146
|
-
| `getSpace(params)`
|
|
147
|
-
| `getConfig(options)`
|
|
148
|
-
| `syncRevision(fallbackRv)`
|
|
149
|
-
| `clearCache()`
|
|
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
|
|
158
|
-
token: string
|
|
159
|
-
version?: 'published' | 'draft'
|
|
160
|
-
rv?: string | number
|
|
161
|
-
fetchClient?: FetchClient
|
|
162
|
-
getRv?: () => string | number
|
|
163
|
-
setRv?: (value: string | number) => void
|
|
164
|
-
timeoutMs?: number
|
|
165
|
-
retries?: number
|
|
166
|
-
maxConcurrency?: number
|
|
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
|
|
214
|
-
|
|
215
|
-
| `blocks`
|
|
216
|
-
| `blocks/{id}`
|
|
217
|
-
| `breadcrumbs/{slug}`
|
|
218
|
-
| `contents`
|
|
219
|
-
| `contents/{slug}`
|
|
220
|
-
| `datasources`
|
|
221
|
-
| `datasources/{slug}/entries` | GET (list)
|
|
222
|
-
| `redirects`
|
|
223
|
-
| `redirects/lookup`
|
|
224
|
-
| `search`
|
|
225
|
-
| `sitemap`
|
|
226
|
-
| `sitemaps/{name}`
|
|
227
|
-
| `spaces/me`
|
|
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')
|
|
256
|
-
buildLocalizedPath('home', 'en')
|
|
257
|
-
buildLocalizedPath('de/uber-uns', 'de')
|
|
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
|
|
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
|
|
288
|
-
|
|
289
|
-
| `language` / `language_iso` | space default | Language every level is resolved for
|
|
290
|
-
| `vid`
|
|
291
|
-
| `include_self`
|
|
292
|
-
| `ancestors`
|
|
293
|
-
| `translations`
|
|
294
|
-
| `include_content`
|
|
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()
|
|
322
|
-
previewBridge.selectItem(blockId)
|
|
323
|
-
previewBridge.selectField(blockId, path)
|
|
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
|
|
333
|
-
|
|
334
|
-
| `SELECT_UPDATE`
|
|
335
|
-
| `HOVER_UPDATE`
|
|
336
|
-
| `FIELD_SELECT`
|
|
337
|
-
| `FIELD_UPDATE`
|
|
338
|
-
| `CONTENT_UPDATE` | editor → preview | `{ content }`
|
|
339
|
-
| `CONTENT_PATCH`
|
|
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 {
|
|
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()
|
|
378
|
-
setPreviewScrollOffset(80)
|
|
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,
|
|
404
|
-
B10cksLinkResolved,
|
|
405
|
-
IBContent,
|
|
406
|
-
IBContentBlock,
|
|
407
|
-
IBBlock,
|
|
408
|
-
IBDataEntry,
|
|
409
|
-
IBDataSource,
|
|
410
|
-
IBSitemapEntry,
|
|
411
|
-
IBSeoMeta,
|
|
412
|
-
IBSpace,
|
|
413
|
-
IBRedirect,
|
|
414
|
-
IBSearchResult,
|
|
415
|
-
IBSearchResponse,
|
|
416
|
-
IBCollectionResponse
|
|
417
|
-
IBMeta,
|
|
418
|
-
IBResponse,
|
|
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
|
|
2
|
-
.b10cks-preview { scroll-margin-top: var(${
|
|
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
|
|
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,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`)}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
|
|
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
|
|
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
|