@b10cks/client 1.3.0 → 1.5.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
@@ -16,9 +16,13 @@ import { ApiClient, createB10cksDataApi } from '@b10cks/client'
16
16
  const client = new ApiClient({
17
17
  baseUrl: 'https://api.b10cks.com/api',
18
18
  token: 'your-access-token',
19
+ version: 'published', // 'published' (default) or 'draft'
20
+ rv: 0, // revision token — pass Date.now() to bypass CDN cache
19
21
  })
20
22
 
23
+ // Factory function — equivalent to new B10cksDataApi(client)
21
24
  const dataApi = createB10cksDataApi(client)
25
+ // or: import { B10cksDataApi } from '@b10cks/client'; const dataApi = new B10cksDataApi(client)
22
26
 
23
27
  // Fetch a page of contents
24
28
  const contents = await dataApi.getContents({ vid: 'published', page: 1, per_page: 20 })
@@ -143,6 +147,20 @@ const params = serializeFilter({
143
147
 
144
148
  Pass `{ allPages: true }` as the second argument to any collection method to fetch every page automatically.
145
149
 
150
+ ## `ApiClient` configuration
151
+
152
+ ```typescript
153
+ interface B10cksApiClientOptions {
154
+ baseUrl: string // Base URL of the b10cks data API (include `/api`)
155
+ token: string // Space access token
156
+ version?: 'published' | 'draft' // Content version to fetch (default: 'published')
157
+ rv?: string | number // Revision token; use Date.now() to bypass CDN cache
158
+ fetchClient?: FetchClient // Custom fetch implementation (required in environments without globalThis.fetch)
159
+ getRv?: () => string | number // Custom getter for the shared revision token
160
+ setRv?: (value: string | number) => void // Custom setter for the shared revision token
161
+ }
162
+ ```
163
+
146
164
  ## Low-level `ApiClient`
147
165
 
148
166
  `ApiClient` implements the `DataApiClient` interface and can be used directly for one-off requests:
@@ -150,8 +168,12 @@ Pass `{ allPages: true }` as the second argument to any collection method to fet
150
168
  ```typescript
151
169
  const client = new ApiClient({ baseUrl, token })
152
170
 
153
- // GET with custom params
154
- const data = await client.get('contents', { page: 1, language: 'de' })
171
+ // GET with custom params — all extra keys are forwarded as query params as-is
172
+ const data = await client.get('contents', { page: 1, language_iso: 'de', vid: 'published' })
173
+
174
+ // Per-request rv override — pass rv in params to override the instance-level revision token
175
+ // (useful to bypass CDN cache for a single request without affecting other calls)
176
+ const fresh = await client.get('contents', { rv: Date.now() })
155
177
 
156
178
  // GET all pages
157
179
  const all = await client.getAll('blocks')
@@ -176,6 +198,165 @@ const result = await client.post('redirects/lookup', { source: '/old' })
176
198
  | `sitemap` | GET (list) |
177
199
  | `spaces/me` | GET |
178
200
 
201
+ ## Link resolution
202
+
203
+ `resolveB10cksLink` converts a `B10cksLink` value (the union type emitted by the type generator for `link`-type fields) into a plain `{ href, target }` object. Locale prefixing and router integration are left to the caller.
204
+
205
+ ```typescript
206
+ import { resolveB10cksLink } from '@b10cks/client'
207
+
208
+ const resolved = resolveB10cksLink(block.ctaLink)
209
+ // resolved: { href: 'https://example.com', target: '_blank' }
210
+ // or: { href: 'mailto:hi@example.com', target: '_self' }
211
+ // or: undefined (when link is nullish or type === 'asset')
212
+ ```
213
+
214
+ ## Sitemap utilities
215
+
216
+ Framework-agnostic helpers for building multilingual sitemaps from `IBSitemapEntry[]`.
217
+
218
+ ```typescript
219
+ import {
220
+ buildLocalizedPath,
221
+ filterSitemapEntries,
222
+ renderSitemapXml,
223
+ renderSitemapIndex,
224
+ } from '@b10cks/client'
225
+
226
+ // Build a rooted locale-prefixed path from a content entry
227
+ buildLocalizedPath('about', 'en') // → '/en/about'
228
+ buildLocalizedPath('home', 'en') // → '/en'
229
+ buildLocalizedPath('de/uber-uns', 'de') // → '/de/uber-uns' (no double prefix)
230
+
231
+ // Filter: deduplicate, drop noindex, optionally restrict to one locale
232
+ const filtered = filterSitemapEntries(entries, {
233
+ siteUrl: 'https://example.com',
234
+ locale: 'en',
235
+ })
236
+
237
+ // Render <urlset> XML
238
+ const xml = renderSitemapXml(filtered, 'https://example.com')
239
+
240
+ // Render <sitemapindex> XML for multi-sitemap setups
241
+ const index = renderSitemapIndex(['/sitemap-en.xml', '/sitemap-de.xml'], 'https://example.com')
242
+ ```
243
+
244
+ ## Preview bridge & visual editing
245
+
246
+ When a page is rendered inside the b10cks visual editor (in an `<iframe>`), the SDK exchanges `postMessage` events with the editor so blocks can be selected, hovered, and live-updated while editing. These are the framework-agnostic building blocks — the `@b10cks/vue`, `@b10cks/react`, `@b10cks/svelte`, `@b10cks/nuxt`, and `@b10cks/next` packages wrap them in idiomatic directives/hooks/actions.
247
+
248
+ ### `previewBridge`
249
+
250
+ A singleton that bridges the preview iframe and the editor.
251
+
252
+ ```typescript
253
+ import { previewBridge } from '@b10cks/client'
254
+
255
+ // Call once on the client when your app boots (the framework packages do this for you).
256
+ previewBridge.init({ allowedOrigins: ['https://app.b10cks.com'] })
257
+
258
+ previewBridge.isInPreviewMode() // true only inside the editor iframe
259
+ previewBridge.selectItem(blockId) // tell the editor to select a block
260
+ previewBridge.selectField(blockId, path) // deep-select a nested field (e.g. rich text)
261
+ previewBridge.updateFieldAt(blockId, ['headline'], 'New text') // stream an inline edit
262
+
263
+ // Subscribe to editor → preview events; returns an unsubscribe function.
264
+ const off = previewBridge.on('CONTENT_UPDATE', ({ content }) => { /* … */ })
265
+ ```
266
+
267
+ Event protocol:
268
+
269
+ | Event | Direction | Payload | Purpose |
270
+ |---|---|---|---|
271
+ | `SELECT_UPDATE` | both | `{ selectedItem }` | Select a block |
272
+ | `HOVER_UPDATE` | editor → preview | `{ selectedItem }` | Hover-highlight a block |
273
+ | `FIELD_SELECT` | preview → editor | `{ itemId, path }` | Deep-select a field so the editor opens its own editor |
274
+ | `FIELD_UPDATE` | preview → editor | `{ itemId, path?, field?, value }` | Stream an inline edit |
275
+ | `CONTENT_UPDATE` | editor → preview | `{ content }` | Replace the whole content tree |
276
+ | `CONTENT_PATCH` | editor → preview | `{ path, value }` | Replace a single value at `path` |
277
+
278
+ `path` is a `FieldPath` (`(string | number)[]`) that addresses any value at any depth, including array indices — e.g. `['body', 2, 'headline']`.
279
+
280
+ #### Security
281
+
282
+ By default the bridge locks onto the origin of the first message it receives (trust-on-first-use) and ignores everything else afterwards; outbound messages are then targeted to that origin instead of `*`. Pass `allowedOrigins` to `init()` to restrict the handshake to a known list of editor origins.
283
+
284
+ ### Editable DOM helpers
285
+
286
+ `attachEditable` and `attachEditableField` wire a DOM element to the bridge and return a cleanup function. Both are no-ops outside preview mode.
287
+
288
+ ```typescript
289
+ import { attachEditable, attachEditableField } from '@b10cks/client'
290
+
291
+ // Selectable block: click selects it; editor-driven select/hover toggle outline classes.
292
+ const detach = attachEditable(el, { id: block.id })
293
+
294
+ // Inline string field (contenteditable, streams plain-text edits):
295
+ attachEditableField(el, { id: block.id, field: 'headline' })
296
+
297
+ // Rich text / complex field — deep-select instead of editing inline:
298
+ attachEditableField(el, { id: block.id, path: ['body'], mode: 'select' })
299
+ ```
300
+
301
+ ### Scroll offset (fixed headers)
302
+
303
+ 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:
304
+
305
+ ```css
306
+ :root { --b10cks-scroll-offset: 80px; }
307
+ ```
308
+
309
+ or from JS (the framework packages expose a `scrollOffset` option that calls this):
310
+
311
+ ```typescript
312
+ import { ensurePreviewStyles, setPreviewScrollOffset } from '@b10cks/client'
313
+
314
+ ensurePreviewStyles() // inject the outline + scroll-margin styles once
315
+ setPreviewScrollOffset(80) // number → px, or pass a string like '5rem'
316
+ ```
317
+
318
+ ### `PreviewStore` — reactive live content
319
+
320
+ `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`.
321
+
322
+ ```typescript
323
+ import { PreviewStore, bindPreviewStore, setAtPath, getAtPath } from '@b10cks/client'
324
+
325
+ const store = new PreviewStore(initialContent)
326
+ const offBridge = bindPreviewStore(store)
327
+ const off = store.subscribe(() => render(store.getSnapshot()))
328
+
329
+ // Immutable path helpers used internally — also exported for your own use:
330
+ const next = setAtPath(content, ['body', 0, 'headline'], 'New')
331
+ const value = getAtPath(content, ['body', 0, 'headline'])
332
+ ```
333
+
334
+ ## TypeScript
335
+
336
+ Common types exported from the package root:
337
+
338
+ ```typescript
339
+ import type {
340
+ B10cksLink, // Union type for link fields (url | email | internal | asset)
341
+ B10cksLinkResolved, // Return type of resolveB10cksLink
342
+ IBContent, // A content entry (generic: IBContent<YourContentShape>)
343
+ IBContentBlock, // Base block shape with id and block fields
344
+ IBBlock, // A block schema
345
+ IBDataEntry, // A data source entry (key/value)
346
+ IBDataSource, // A data source definition
347
+ IBSitemapEntry, // Sitemap entry with full_slug, language_iso, meta, published_at
348
+ IBSeoMeta, // robots and canonical fields
349
+ IBSpace, // Space info
350
+ IBRedirect, // Redirect rule (source, target, status_code)
351
+ IBSearchResult, // Search result extending IBContent with relevance_score
352
+ IBSearchResponse, // Wrapper around IBSearchResult[]
353
+ IBCollectionResponse,// Paginated list envelope
354
+ IBMeta, // Pagination metadata (total, last_page, per_page, …)
355
+ IBResponse, // Single-item envelope { data, rv }
356
+ B10cksApiClientOptions,
357
+ } from '@b10cks/client'
358
+ ```
359
+
179
360
  ## License
180
361
 
181
362
  MIT
package/dist/index.d.ts CHANGED
@@ -5,6 +5,13 @@ export declare class ApiClient {
5
5
  private readonly fetchClient;
6
6
  private readonly getRvFn?;
7
7
  private readonly setRvFn?;
8
+ /**
9
+ * Per-instance revision store, used when no `getRv`/`setRv` callbacks are
10
+ * supplied. Kept on the instance (not module scope) so concurrent requests
11
+ * in a server runtime cannot leak revisions into one another. Server callers
12
+ * that need request-scoped persistence should pass `getRv`/`setRv`.
13
+ */
14
+ private rvValue;
8
15
  constructor(options: B10cksApiClientOptions, requestUrl?: URL | string);
9
16
  get<T>(endpoint: Endpoint, params?: Omit<IBBaseQueryParams, 'token'> & Record<string, unknown>): Promise<ApiResourceResponse<T>>;
10
17
  post<T>(endpoint: string, body?: unknown, params?: Omit<IBBaseQueryParams, 'token'> & Record<string, unknown>): Promise<T>;
@@ -24,6 +31,44 @@ declare type ApiQueryParams = Omit<IBBaseQueryParams, 'token'> & Record<string,
24
31
 
25
32
  declare type ApiResourceResponse<T> = IBResponse<T> | T;
26
33
 
34
+ /**
35
+ * Wire a DOM element as a selectable block: click selects it in the editor,
36
+ * and editor-driven select/hover toggle outline classes. Honors the configured
37
+ * scroll offset and only scrolls when the element isn't already in view.
38
+ * Returns a cleanup function. No-op outside preview mode.
39
+ */
40
+ export declare function attachEditable(el: HTMLElement, options: AttachEditableOptions): () => void;
41
+
42
+ /**
43
+ * Wire a DOM element for field editing. Returns a cleanup function. No-op
44
+ * outside preview mode. See {@link AttachEditableFieldOptions.mode}.
45
+ */
46
+ export declare function attachEditableField(el: HTMLElement, options: AttachEditableFieldOptions): () => void;
47
+
48
+ export declare interface AttachEditableFieldOptions {
49
+ id: string;
50
+ /** @deprecated Use `path`. Kept for flat string fields. */
51
+ field?: string;
52
+ path?: FieldPath;
53
+ /**
54
+ * `inline` makes the element contenteditable and streams plain-text edits
55
+ * back to the editor — suitable for simple string fields only. `select`
56
+ * instead deep-selects the field so the editor opens its own editor, which
57
+ * is the right choice for rich text and other complex types. Default
58
+ * `inline`, or `select` automatically when no `field` is given but a `path`
59
+ * targets a complex value.
60
+ */
61
+ mode?: EditableFieldMode;
62
+ }
63
+
64
+ export declare interface AttachEditableOptions {
65
+ id: string;
66
+ onSelectChange?: (selected: boolean) => void;
67
+ onHoverChange?: (hovered: boolean) => void;
68
+ /** Scroll the element into view when it becomes selected. Default true. */
69
+ scrollOnSelect?: boolean;
70
+ }
71
+
27
72
  export declare interface B10cksApiClientOptions {
28
73
  baseUrl: string;
29
74
  token: string;
@@ -65,16 +110,67 @@ export declare class B10cksDataApi {
65
110
  private unwrapCollection;
66
111
  }
67
112
 
113
+ export declare type B10cksLink = {
114
+ type: 'url';
115
+ url: string;
116
+ anchor?: string;
117
+ target?: '_self' | '_blank' | '_parent' | '_top';
118
+ rel?: string;
119
+ } | {
120
+ type: 'email';
121
+ email: string;
122
+ subject?: string;
123
+ body?: string;
124
+ cc?: string;
125
+ bcc?: string;
126
+ } | {
127
+ type: 'internal';
128
+ url: string;
129
+ title: string;
130
+ content: string;
131
+ params?: string;
132
+ anchor?: string;
133
+ target?: '_self' | '_blank' | '_parent' | '_top';
134
+ } | {
135
+ type: 'asset';
136
+ id: string;
137
+ };
138
+
139
+ export declare interface B10cksLinkResolved {
140
+ href: string;
141
+ target: string;
142
+ }
143
+
144
+ /**
145
+ * Feed the singleton bridge's content events into a {@link PreviewStore}.
146
+ * Returns an unsubscribe function. No-op outside preview mode.
147
+ */
148
+ export declare function bindPreviewStore<T>(store: PreviewStore<T>): () => void;
149
+
68
150
  export declare type BridgeEvent = {
69
151
  type: EventType;
70
152
  payload: EventPayloadMap[EventType];
71
153
  b10cksId?: string;
72
154
  };
73
155
 
156
+ /**
157
+ * Combines a b10cks `full_slug` and `language_iso` into a rooted, locale-prefixed path.
158
+ *
159
+ * - The slug `home` is treated as the root (`/` or `/<locale>`).
160
+ * - Slugs that already start with the locale prefix are not double-prefixed.
161
+ */
162
+ export declare function buildLocalizedPath(fullSlug: string | null | undefined, languageIso: string | null | undefined): string;
163
+
74
164
  export declare interface CollectionFetchOptions {
75
165
  allPages?: boolean;
76
166
  }
77
167
 
168
+ /** Granular update: replace the value at `path` within the content tree. */
169
+ export declare type ContentPatchEvent = {
170
+ path: FieldPath;
171
+ value: unknown;
172
+ };
173
+
78
174
  export declare type ContentSortField = 'published_at' | 'updated_at' | 'created_at' | `content.${string}`;
79
175
 
80
176
  export declare type ContentSortItem = ContentSortField | `-${ContentSortField}`;
@@ -115,27 +211,62 @@ export declare type DateFilter = string | {
115
211
  '!null': true;
116
212
  };
117
213
 
214
+ export declare type EditableFieldMode = 'inline' | 'select';
215
+
118
216
  export declare type Endpoint = 'blocks' | `blocks/${string}` | 'contents' | `contents/${string}` | `datasources/${string}/entries` | 'datasources' | 'redirects' | 'search' | 'sitemap' | 'spaces/me';
119
217
 
218
+ /**
219
+ * Inject the preview outline styles once. Selected/hovered blocks get an
220
+ * outline; `.b10cks-preview` carries a `scroll-margin-top` so scroll-into-view
221
+ * clears a fixed app header. Set the offset via {@link setPreviewScrollOffset}
222
+ * or the `--b10cks-scroll-offset` CSS variable.
223
+ */
224
+ export declare function ensurePreviewStyles(): void;
225
+
120
226
  declare type EventCallback<T> = (payload: T) => void;
121
227
 
122
228
  export declare type EventPayloadMap = {
123
229
  CONTENT_UPDATE: ContentUpdateEvent;
230
+ CONTENT_PATCH: ContentPatchEvent;
124
231
  SELECT_UPDATE: SelectUpdateEvent;
125
232
  HOVER_UPDATE: SelectUpdateEvent;
126
233
  FIELD_UPDATE: FieldUpdateEvent;
234
+ FIELD_SELECT: FieldSelectEvent;
127
235
  };
128
236
 
129
- export declare type EventType = 'CONTENT_UPDATE' | 'SELECT_UPDATE' | 'HOVER_UPDATE' | 'FIELD_UPDATE';
237
+ export declare type EventType = 'CONTENT_UPDATE' | 'CONTENT_PATCH' | 'SELECT_UPDATE' | 'HOVER_UPDATE' | 'FIELD_UPDATE' | 'FIELD_SELECT';
130
238
 
131
239
  export declare type FetchClient = (input: URL | string | RequestInfo, init?: RequestInit) => Promise<unknown>;
132
240
 
241
+ /** Addresses a field within a block, supporting nested objects and arrays. */
242
+ export declare type FieldPath = (string | number)[];
243
+
244
+ /**
245
+ * Deep-select a specific field (e.g. a rich text field) so the editor opens
246
+ * its own editor for that field instead of editing it inline in the preview.
247
+ */
248
+ export declare type FieldSelectEvent = {
249
+ itemId: string;
250
+ path: FieldPath;
251
+ };
252
+
133
253
  export declare type FieldUpdateEvent = {
134
254
  itemId: string;
135
- field: string;
136
- value: string;
255
+ /** Path to the field within the block. Preferred over `field`. */
256
+ path?: FieldPath;
257
+ /** @deprecated Use `path`. Kept for flat string fields. */
258
+ field?: string;
259
+ value: unknown;
137
260
  };
138
261
 
262
+ /**
263
+ * Filters sitemap entries by locale, deduplicates by resolved URL, and drops `noindex` entries.
264
+ */
265
+ export declare function filterSitemapEntries(entries: IBSitemapEntry[], options?: SitemapFilterOptions): IBSitemapEntry[];
266
+
267
+ /** Read the value at `path` within `target`, or undefined if absent. */
268
+ export declare function getAtPath(target: unknown, path: FieldPath): unknown;
269
+
139
270
  export declare interface GetConfigOptions extends Omit<IBContentQueryParams, 'token' | 'full_slug'> {
140
271
  slug?: string;
141
272
  language?: string;
@@ -374,24 +505,63 @@ export declare type IdFilter = string | {
374
505
  '!null': true;
375
506
  };
376
507
 
508
+ declare type Listener = () => void;
509
+
510
+ export declare function normalizePathSegment(value: string | null | undefined): string;
511
+
377
512
  export declare class PreviewBridge {
378
513
  private static instance;
379
514
  private readonly listeners;
380
515
  private initialized;
516
+ private allowedOrigins;
517
+ /** Origin of the editor, captured from the first trusted message. */
518
+ private parentOrigin;
381
519
  private constructor();
382
520
  static getInstance(): PreviewBridge;
383
- init(): void;
521
+ init(options?: PreviewBridgeInitOptions): void;
384
522
  destroy(): void;
385
523
  isInPreviewMode(): boolean;
386
524
  on<T extends EventType>(eventType: T, callback: EventCallback<EventPayloadMap[T]>): () => void;
387
525
  selectItem(selectedItem: string): void;
526
+ /** Deep-select a nested field (e.g. a rich text field) in the editor. */
527
+ selectField(itemId: string, path: FieldPath): void;
528
+ /** @deprecated Prefer {@link updateFieldAt} with a path. */
388
529
  updateField(itemId: string, field: string, value: string): void;
530
+ /** Stream an inline edit back to the editor, addressed by path. */
531
+ updateFieldAt(itemId: string, path: FieldPath, value: unknown): void;
532
+ private post;
389
533
  private handleMessage;
534
+ private isOriginTrusted;
390
535
  private notify;
391
536
  }
392
537
 
393
538
  export declare const previewBridge: PreviewBridge;
394
539
 
540
+ export declare interface PreviewBridgeInitOptions {
541
+ /**
542
+ * Origins the editor is allowed to post from. When provided, messages from
543
+ * any other origin are ignored. When omitted, the bridge locks onto the
544
+ * origin of the first valid message it receives (trust-on-first-use).
545
+ */
546
+ allowedOrigins?: string[];
547
+ }
548
+
549
+ /**
550
+ * A framework-agnostic, reactive holder for the content tree shown in the
551
+ * preview. The editor pushes whole-tree (`CONTENT_UPDATE`) or granular
552
+ * (`CONTENT_PATCH`) changes; subscribers re-render from the new snapshot.
553
+ */
554
+ export declare class PreviewStore<T = Record<string, unknown>> {
555
+ private content;
556
+ private readonly listeners;
557
+ constructor(initial: T);
558
+ subscribe: (listener: Listener) => (() => void);
559
+ getSnapshot: () => T;
560
+ setContent(next: T): void;
561
+ patch(path: FieldPath, value: unknown): void;
562
+ private emit;
563
+ }
564
+
395
565
  export declare interface RedirectFetchOptions extends CollectionFetchOptions {
396
566
  forceRefresh?: boolean;
397
567
  }
@@ -401,12 +571,51 @@ export declare type RedirectMap = Record<string, {
401
571
  status_code: number;
402
572
  }>;
403
573
 
574
+ /**
575
+ * Renders a list of sitemap paths as a `<sitemapindex>` XML string.
576
+ */
577
+ export declare function renderSitemapIndex(paths: string[], siteUrl?: string): string;
578
+
579
+ /**
580
+ * Renders `IBSitemapEntry[]` as a `<urlset>` XML string.
581
+ * Pass `siteUrl` to emit absolute `<loc>` values; without it, relative paths are used.
582
+ */
583
+ export declare function renderSitemapXml(entries: IBSitemapEntry[], siteUrl?: string): string;
584
+
585
+ /**
586
+ * Resolves a B10cksLink value to a plain { href, target } object.
587
+ *
588
+ * - `'email'` links produce a `mailto:` href with optional subject/body/cc/bcc query params.
589
+ * - `'url'` and `'internal'` links use the stored `url` field; an `anchor` is appended as a
590
+ * hash fragment when present.
591
+ * - `'asset'` links cannot be resolved to a URL without the asset record — returns `undefined`.
592
+ * - Locale prefixing and router integration are intentionally left to the caller.
593
+ */
594
+ export declare function resolveB10cksLink(link: B10cksLink | undefined | null): B10cksLinkResolved | undefined;
595
+
404
596
  export declare type SelectUpdateEvent = {
405
597
  selectedItem: string;
406
598
  };
407
599
 
408
600
  export declare function serializeFilter(filter: Record<string, unknown>): Record<string, string>;
409
601
 
602
+ /** Return a copy of `target` with the value at `path` replaced. Immutable. */
603
+ export declare function setAtPath<T>(target: T, path: FieldPath, value: unknown): T;
604
+
605
+ /**
606
+ * Set the scroll offset used when a selected block is scrolled into view.
607
+ * Pass your fixed header's height so selection no longer overshoots beneath it.
608
+ * A number is treated as pixels; strings are used verbatim (e.g. `"5rem"`).
609
+ */
610
+ export declare function setPreviewScrollOffset(offset: number | string): void;
611
+
612
+ export declare interface SitemapFilterOptions {
613
+ /** Absolute base URL used for deduplication. Without it, paths are compared as strings. */
614
+ siteUrl?: string;
615
+ /** Only include entries for this locale (ISO code). */
616
+ locale?: string;
617
+ }
618
+
410
619
  export declare type StringFilter = string | {
411
620
  eq: string;
412
621
  } | {
@@ -451,6 +660,7 @@ export declare namespace types {
451
660
  IBSpace,
452
661
  IBRedirect,
453
662
  IBContentQueryParams,
663
+ B10cksLink,
454
664
  B10cksApiClientOptions,
455
665
  StringFilter,
456
666
  IdFilter,
package/dist/index.js CHANGED
@@ -1,2 +1,19 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function R(s){if(s!=null){if(typeof s=="boolean"||typeof s=="number")return String(s);if(typeof s=="string")return s;if(typeof s=="object"&&!Array.isArray(s)){const t=s;if("null"in t)return"null:";if("!null"in t)return"!null:";if("between"in t){const[i,n]=t.between;return`${i}...${n}`}const e=["eq","neq","like","!like","^like","like$","lt","gt","lte","gte","in","!in","empty","!empty"];for(const i of e)if(i in t){const n=t[i];return Array.isArray(n)?`${i}:${n.join(",")}`:`${i}:${String(n)}`}}}}function d(s){const t={};for(const[e,i]of Object.entries(s)){const n=R(i);n!==void 0&&(t[e]=n)}return t}function h(s,t,e){const i=t&&typeof t=="object"?d(t):{},n=Array.isArray(e)?{sort:e.join(",")}:e!=null?{sort:e}:{};return{...s,...i,...n}}class g{constructor(t){this.client=t}redirectsCache=null;configCache=new Map;async getResource(t,e={}){const i=await this.client.get(t,e);return this.unwrapResource(i)}async getCollection(t,e={},i={}){if(i.allPages)return this.client.getAll(t,e);const n=await this.client.get(t,e);return this.unwrapCollection(n)}async getContent(t,e={}){return this.getResource(`contents/${t}`,e)}async getContents(t={},e={}){const{filter:i,sort:n,...r}=t;return this.getCollection("contents",h(r,i,n),e)}async getBlock(t,e={}){return this.getResource(`blocks/${t}`,e)}async getBlocks(t={},e={}){const{filter:i,...n}=t;return this.getCollection("blocks",h(n,i,void 0),e)}async search(t){const e=await this.client.get("search",t);if(typeof e=="object"&&e!==null&&"data"in e&&"meta"in e)return e;if(typeof e=="object"&&e!==null&&"data"in e){const i=e.data;if(typeof i=="object"&&i!==null&&"meta"in i&&"data"in i)return i}return e}async getSitemap(t={},e={}){return this.getCollection("sitemap",t,e)}async getDataEntries(t,e={},i={}){return this.getCollection(`datasources/${t}/entries`,e,i)}async getDataSources(t={},e={}){return this.getCollection("datasources",t,e)}async getSpace(t={}){return this.getResource("spaces/me",t)}async lookupRedirect(t){if(!this.client.post)throw new Error("lookupRedirect requires a client that supports POST. Use ApiClient from @b10cks/client.");return this.client.post("redirects/lookup",{source:t})}async getRedirects(t={},e=!1){const{allPages:i=!1,forceRefresh:n=!1}=typeof e=="boolean"?{allPages:!0,forceRefresh:e}:e;if(i&&this.redirectsCache&&!n)return this.redirectsCache;const{filter:r,...c}=t,a=h(c,r,void 0),f=await this.getCollection("redirects",a,{allPages:i}),l=Object.fromEntries(f.map(({source:y,target:p,status_code:v})=>[y,{target:p,status_code:v}]));return i&&(this.redirectsCache=l),l}async getConfig({slug:t="_config",language_iso:e,language:i,bypassCache:n=!1,...r}={}){const c=e??i,a=JSON.stringify({slug:t,language_iso:c,...r});if(!n&&this.configCache.has(a))return this.configCache.get(a);const l=(await this.getContent(t,{...r,language_iso:c})).content??{};return n||this.configCache.set(a,l),l}async syncRevision(t=426713400){const i=(await this.getSpace()).rv||t;return this.client.setRv(i),i}clearCache(){this.redirectsCache=null,this.configCache.clear()}unwrapResource(t){return typeof t=="object"&&t!==null&&"data"in t?t.data:t}unwrapCollection(t){if(Array.isArray(t))return t;if(typeof t=="object"&&t!==null&&"data"in t){if(Array.isArray(t.data))return t.data;if(typeof t.data=="object"&&t.data!==null&&"data"in t.data&&Array.isArray(t.data.data))return t.data.data}return[]}}function w(s){return new g(s)}class o{static instance;listeners={};initialized=!1;constructor(){}static getInstance(){return o.instance||(o.instance=new o),o.instance}init(){!this.isInPreviewMode()||this.initialized||(window.addEventListener("message",this.handleMessage),this.initialized=!0)}destroy(){this.initialized&&typeof window<"u"&&window.removeEventListener("message",this.handleMessage),this.initialized=!1;for(const t of Object.keys(this.listeners))this.listeners[t]?.clear()}isInPreviewMode(){return typeof window<"u"&&window.self!==window.top}on(t,e){if(!this.isInPreviewMode())return()=>{};const i=this.listeners[t]||new Set;return this.listeners[t]=i,i.add(e),()=>{i.delete(e)}}selectItem(t){this.isInPreviewMode()&&window.parent.postMessage({type:"SELECT_UPDATE",payload:{selectedItem:t}},"*")}updateField(t,e,i){this.isInPreviewMode()&&window.parent.postMessage({type:"FIELD_UPDATE",payload:{itemId:t,field:e,value:i}},"*")}handleMessage=t=>{if(!t.data||typeof t.data!="object")return;const{type:e,payload:i}=t.data;!e||!i||this.notify(e,i)};notify(t,e){const i=this.listeners[t];if(i)for(const n of i)n(e)}}const b=o.getInstance(),C=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"}));let u=0;class m{baseUrl;token;vid;fetchClient;getRvFn;setRvFn;constructor(t,e){this.baseUrl=t.baseUrl.endsWith("/")?t.baseUrl.slice(0,-1):t.baseUrl,this.token=t.token;const i=typeof globalThis.fetch=="function"?globalThis.fetch.bind(globalThis):null;if(t.fetchClient)this.fetchClient=t.fetchClient;else if(i)this.fetchClient=i;else throw new Error("No fetch implementation available. Provide `fetchClient` in ApiClient options.");this.getRvFn=t.getRv,this.setRvFn=t.setRv;const n=this.resolveRequestUrl(e);this.vid=t.version||"published",this.setRv(n?.searchParams.get("b10cks_rv")||t.rv||u)}async get(t,e={}){const i=this.buildUrl(t,{vid:this.vid,...e,rv:this.getRv(),token:this.token}),n=await this.fetchClient(i),r=await this.parseResponse(n);return this.hasRevision(r)&&this.setRv(r.rv),r}async post(t,e,i={}){const n=this.buildUrl(t,{vid:this.vid,...i,rv:this.getRv(),token:this.token}),r=await this.fetchClient(n,{method:"POST",headers:{"Content-Type":"application/json"},body:e!==void 0?JSON.stringify(e):void 0});return this.parseResponse(r)}async getAll(t,e={}){const i=await this.get(t,{...e,page:1}),n=this.normalizeCollectionResponse(i);if(n.rv&&this.setRv(n.rv),!n.meta||n.meta.last_page<=1)return n.data;const r=Array.from({length:n.meta.last_page-1},(a,f)=>this.get(t,{...e,page:f+2})),c=await Promise.all(r);return n.data.concat(c.flatMap(a=>this.normalizeCollectionResponse(a).data))}setRv(t){this.setRvFn?this.setRvFn(t):u=t}getRv(){return this.getRvFn?this.getRvFn():u}async parseResponse(t){if(this.isFetchResponse(t)){if(!t.ok)throw new Error(`Request failed with status ${t.status}`);return await t.json()}return t}normalizeCollectionResponse(t){if(this.isCollectionResponseEnvelope(t)){const e=t.data;return{data:e.data,meta:e.meta,rv:t.rv||e.rv}}if(Array.isArray(t))return{data:t,meta:void 0,rv:void 0};if(typeof t=="object"&&t!==null&&"data"in t){const{data:e,meta:i,rv:n}=t;return{data:e,meta:i,rv:n}}return{data:[],meta:void 0,rv:void 0}}buildUrl(t,e){const i=new URL(`${this.baseUrl}/v1/${t}`);return Object.entries(e).forEach(([n,r])=>{r!=null&&i.searchParams.append(n,String(r))}),i.toString()}resolveRequestUrl(t){return t?t instanceof URL?t:new URL(t,"http://localhost"):null}isFetchResponse(t){return typeof t=="object"&&t!==null&&"json"in t&&typeof t.json=="function"&&"ok"in t}hasRevision(t){return typeof t=="object"&&t!==null&&"rv"in t}isCollectionResponseEnvelope(t){return typeof t=="object"&&t!==null&&"data"in t&&typeof t.data=="object"&&t.data!==null&&"data"in t.data&&Array.isArray(t.data.data)}}exports.ApiClient=m;exports.B10cksDataApi=g;exports.PreviewBridge=o;exports.createB10cksDataApi=w;exports.previewBridge=b;exports.serializeFilter=d;exports.types=C;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function P(i){if(!i)return;if(i.type==="email"){const n=new URLSearchParams;i.subject&&n.set("subject",i.subject),i.body&&n.set("body",i.body),i.cc&&n.set("cc",i.cc),i.bcc&&n.set("bcc",i.bcc);const s=n.toString();return{href:`mailto:${i.email}${s?`?${s}`:""}`,target:"_self"}}if(i.type==="asset")return;const t=i.target??"_self";let e=i.url??"";return"anchor"in i&&i.anchor&&(e=`${e.trimEnd()}#${i.anchor}`),{href:e,target:t}}function T(i){if(i!=null){if(typeof i=="boolean"||typeof i=="number")return String(i);if(typeof i=="string")return i;if(typeof i=="object"&&!Array.isArray(i)){const t=i;if("null"in t)return"null:";if("!null"in t)return"!null:";if("between"in t){const[n,s]=t.between;return`${n}...${s}`}const e=["eq","neq","like","!like","^like","like$","lt","gt","lte","gte","in","!in","empty","!empty"];for(const n of e)if(n in t){const s=t[n];return Array.isArray(s)?`${n}:${s.join(",")}`:`${n}:${String(s)}`}}}}function R(i){const t={};for(const[e,n]of Object.entries(i)){const s=T(n);s!==void 0&&(t[e]=s)}return t}function m(i,t,e){const n=t&&typeof t=="object"?R(t):{},s=Array.isArray(e)?{sort:e.join(",")}:e!=null?{sort:e}:{};return{...i,...n,...s}}class A{constructor(t){this.client=t}redirectsCache=null;configCache=new Map;async getResource(t,e={}){const n=await this.client.get(t,e);return this.unwrapResource(n)}async getCollection(t,e={},n={}){if(n.allPages)return this.client.getAll(t,e);const s=await this.client.get(t,e);return this.unwrapCollection(s)}async getContent(t,e={}){return this.getResource(`contents/${t}`,e)}async getContents(t={},e={}){const{filter:n,sort:s,...r}=t;return this.getCollection("contents",m(r,n,s),e)}async getBlock(t,e={}){return this.getResource(`blocks/${t}`,e)}async getBlocks(t={},e={}){const{filter:n,...s}=t;return this.getCollection("blocks",m(s,n,void 0),e)}async search(t){const e=await this.client.get("search",t);if(typeof e=="object"&&e!==null&&"data"in e&&"meta"in e)return e;if(typeof e=="object"&&e!==null&&"data"in e){const n=e.data;if(typeof n=="object"&&n!==null&&"meta"in n&&"data"in n)return n}return e}async getSitemap(t={},e={}){return this.getCollection("sitemap",t,e)}async getDataEntries(t,e={},n={}){return this.getCollection(`datasources/${t}/entries`,e,n)}async getDataSources(t={},e={}){return this.getCollection("datasources",t,e)}async getSpace(t={}){return this.getResource("spaces/me",t)}async lookupRedirect(t){if(!this.client.post)throw new Error("lookupRedirect requires a client that supports POST. Use ApiClient from @b10cks/client.");return this.client.post("redirects/lookup",{source:t})}async getRedirects(t={},e=!1){const{allPages:n=!1,forceRefresh:s=!1}=typeof e=="boolean"?{allPages:!0,forceRefresh:e}:e;if(n&&this.redirectsCache&&!s)return this.redirectsCache;const{filter:r,...o}=t,a=m(o,r,void 0),c=await this.getCollection("redirects",a,{allPages:n}),d=Object.fromEntries(c.map(({source:g,target:u,status_code:f})=>[g,{target:u,status_code:f}]));return n&&(this.redirectsCache=d),d}async getConfig({slug:t="_config",language_iso:e,language:n,bypassCache:s=!1,...r}={}){const o=e??n,a=JSON.stringify({slug:t,language_iso:o,...r});if(!s&&this.configCache.has(a))return this.configCache.get(a);const d=(await this.getContent(t,{...r,language_iso:o})).content??{};return s||this.configCache.set(a,d),d}async syncRevision(t=426713400){const n=(await this.getSpace()).rv||t;return this.client.setRv(n),n}clearCache(){this.redirectsCache=null,this.configCache.clear()}unwrapResource(t){return typeof t=="object"&&t!==null&&"data"in t?t.data:t}unwrapCollection(t){if(Array.isArray(t))return t;if(typeof t=="object"&&t!==null&&"data"in t){if(Array.isArray(t.data))return t.data;if(typeof t.data=="object"&&t.data!==null&&"data"in t.data&&Array.isArray(t.data.data))return t.data.data}return[]}}function k(i){return new A(i)}const j=new Set(["CONTENT_UPDATE","CONTENT_PATCH","SELECT_UPDATE","HOVER_UPDATE","FIELD_UPDATE","FIELD_SELECT"]),L="B10CKS_BRIDGE_READY";class h{static instance;listeners={};initialized=!1;allowedOrigins=null;parentOrigin=null;constructor(){}static getInstance(){return h.instance||(h.instance=new h),h.instance}init(t={}){!this.isInPreviewMode()||this.initialized||(this.allowedOrigins=t.allowedOrigins??null,window.addEventListener("message",this.handleMessage),this.initialized=!0,window.parent.postMessage({type:L},"*"))}destroy(){this.initialized&&typeof window<"u"&&window.removeEventListener("message",this.handleMessage),this.initialized=!1,this.parentOrigin=null;for(const t of Object.keys(this.listeners))this.listeners[t]?.clear()}isInPreviewMode(){return typeof window<"u"&&window.self!==window.top}on(t,e){if(!this.isInPreviewMode())return()=>{};const n=this.listeners[t]||new Set;return this.listeners[t]=n,n.add(e),()=>{n.delete(e)}}selectItem(t){this.post("SELECT_UPDATE",{selectedItem:t})}selectField(t,e){this.post("FIELD_SELECT",{itemId:t,path:e})}updateField(t,e,n){this.post("FIELD_UPDATE",{itemId:t,field:e,value:n})}updateFieldAt(t,e,n){this.post("FIELD_UPDATE",{itemId:t,path:e,value:n})}post(t,e){this.isInPreviewMode()&&window.parent.postMessage({type:t,payload:e},this.parentOrigin??"*")}handleMessage=t=>{if(!this.isOriginTrusted(t.origin)||!t.data||typeof t.data!="object")return;const{type:e,payload:n}=t.data;!e||!j.has(e)||!n||(!this.parentOrigin&&t.origin&&t.origin!=="null"&&(this.parentOrigin=t.origin),this.notify(e,n))};isOriginTrusted(t){return this.parentOrigin?t===this.parentOrigin:this.allowedOrigins?this.allowedOrigins.includes(t):!0}notify(t,e){const n=this.listeners[t];if(n)for(const s of n)s(e)}}const l=h.getInstance(),C="b10cks-preview-style",S="--b10cks-scroll-offset";function v(){if(typeof document>"u"||document.getElementById(C))return;const i=document.createElement("style");i.id=C,i.textContent=`
2
+ .b10cks-preview { scroll-margin-top: var(${S}, 0px); }
3
+ .b10cks-hover,
4
+ .b10cks-preview:hover {
5
+ outline: 2px dashed rgba(59, 130, 246, 0.5);
6
+ outline-offset: 2px;
7
+ }
8
+ .b10cks-selected {
9
+ outline: 2px solid rgb(59, 130, 246) !important;
10
+ outline-offset: 2px;
11
+ }
12
+ `,document.head.appendChild(i)}function _(i){if(typeof document>"u")return;const t=typeof i=="number"?`${i}px`:i;document.documentElement.style.setProperty(S,t)}function F(i,t){if(!l.isInPreviewMode()||!t.id)return()=>{};v();const{id:e,onSelectChange:n,onHoverChange:s,scrollOnSelect:r=!0}=t;i.classList.add("b10cks-preview");const o=u=>{u.preventDefault(),u.stopPropagation(),l.selectItem(e)},a=({selectedItem:u})=>{const f=u===e;i.classList.toggle("b10cks-selected",f),f&&r&&$(i),n?.(f)},c=({selectedItem:u})=>{const f=u===e;i.classList.toggle("b10cks-hover",f),s?.(f)};i.addEventListener("click",o);const d=l.on("SELECT_UPDATE",a),g=l.on("HOVER_UPDATE",c);return()=>{i.removeEventListener("click",o),d(),g(),i.classList.remove("b10cks-preview","b10cks-selected","b10cks-hover")}}function O(i,t){if(!l.isInPreviewMode()||!t.id)return()=>{};const{id:e,field:n,path:s,mode:r="inline"}=t;if(r==="select"){v(),i.classList.add("b10cks-preview");const a=c=>{c.preventDefault(),c.stopPropagation(),s?l.selectField(e,s):l.selectItem(e)};return i.addEventListener("click",a),()=>{i.removeEventListener("click",a),i.classList.remove("b10cks-preview")}}i.setAttribute("contenteditable","true");const o=a=>{const c=a.target.innerText;s?l.updateFieldAt(e,s,c):l.updateField(e,n??"",c)};return i.addEventListener("input",o),()=>{i.removeEventListener("input",o),i.removeAttribute("contenteditable")}}function $(i){typeof i.scrollIntoView=="function"&&i.scrollIntoView({block:"nearest",behavior:"smooth"})}function D(i,t){let e=i;for(const n of t){if(e==null||typeof e!="object")return;e=e[n]}return e}function p(i,t,e){if(t.length===0)return e;const[n,...s]=t;if(n===void 0)return e;if(typeof n=="number"){const o=Array.isArray(i)?i.slice():[];return o[n]=p(o[n],s,e),o}const r=i&&typeof i=="object"&&!Array.isArray(i)?{...i}:{};return r[n]=p(r[n],s,e),r}class I{content;listeners=new Set;constructor(t){this.content=t}subscribe=t=>(this.listeners.add(t),()=>{this.listeners.delete(t)});getSnapshot=()=>this.content;setContent(t){this.content=t,this.emit()}patch(t,e){this.content=p(this.content,t,e),this.emit()}emit(){for(const t of this.listeners)t()}}function U(i){const t=l.on("CONTENT_UPDATE",({content:n})=>{i.setContent(n)}),e=l.on("CONTENT_PATCH",({path:n,value:s})=>{i.patch(n,s)});return()=>{t(),e()}}function y(i){return(i??"").trim().replace(/^\/+|\/+$/g,"")}function w(i,t){const e=y(i),n=y(t);if(!n)return e&&e!=="home"?`/${e}`:"/";if(!e||e==="home")return`/${n}`;const s=`${n}/`;return e===n||e.startsWith(s)?`/${e}`:`/${n}/${e}`}function E(i,t){if(!t)return null;try{return new URL(i,t).toString()}catch{return null}}function b(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function x(i,t={}){const{siteUrl:e,locale:n}=t,s=new Set;return i.filter(r=>{if(n&&r.language_iso!==n||r.meta?.robots?.toLowerCase().includes("noindex"))return!1;const o=w(r.full_slug,r.language_iso),a=e?E(o,e)??o:o;return s.has(a)?!1:(s.add(a),!0)})}function z(i,t){return['<?xml version="1.0" encoding="UTF-8"?>','<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',i.map(n=>{const s=w(n.full_slug,n.language_iso),r=t?E(s,t):s;if(!r)return"";const o=[" <url>",` <loc>${b(r)}</loc>`];return n.published_at&&o.push(` <lastmod>${b(n.published_at)}</lastmod>`),o.push(" </url>"),o.join(`
13
+ `)}).filter(Boolean).join(`
14
+ `),"</urlset>"].join(`
15
+ `)}function M(i,t){return['<?xml version="1.0" encoding="UTF-8"?>','<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',i.map(n=>{const s=t?E(n,t):n;return s?[" <sitemap>",` <loc>${b(s)}</loc>`," </sitemap>"].join(`
16
+ `):""}).filter(Boolean).join(`
17
+ `),"</sitemapindex>"].join(`
18
+ `)}const B=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"}));class N{baseUrl;token;vid;fetchClient;getRvFn;setRvFn;rvValue=0;constructor(t,e){this.baseUrl=t.baseUrl.endsWith("/")?t.baseUrl.slice(0,-1):t.baseUrl,this.token=t.token;const n=typeof globalThis.fetch=="function"?globalThis.fetch.bind(globalThis):null;if(t.fetchClient)this.fetchClient=t.fetchClient;else if(n)this.fetchClient=n;else throw new Error("No fetch implementation available. Provide `fetchClient` in ApiClient options.");this.getRvFn=t.getRv,this.setRvFn=t.setRv;const s=this.resolveRequestUrl(e);this.vid=t.version||"published",this.setRv(s?.searchParams.get("b10cks_rv")||t.rv||0)}async get(t,e={}){const n=this.buildUrl(t,{vid:this.vid,rv:this.getRv(),...e,token:this.token}),s=await this.fetchClient(n),r=await this.parseResponse(s);return this.hasRevision(r)&&this.setRv(r.rv),r}async post(t,e,n={}){const s=this.buildUrl(t,{vid:this.vid,rv:this.getRv(),...n,token:this.token}),r=await this.fetchClient(s,{method:"POST",headers:{"Content-Type":"application/json"},body:e!==void 0?JSON.stringify(e):void 0});return this.parseResponse(r)}async getAll(t,e={}){const n=await this.get(t,{...e,page:1}),s=this.normalizeCollectionResponse(n);if(s.rv&&this.setRv(s.rv),!s.meta||s.meta.last_page<=1)return s.data;const r=Array.from({length:s.meta.last_page-1},(a,c)=>this.get(t,{...e,page:c+2})),o=await Promise.all(r);return s.data.concat(o.flatMap(a=>this.normalizeCollectionResponse(a).data))}setRv(t){this.setRvFn?this.setRvFn(t):this.rvValue=t}getRv(){return this.getRvFn?this.getRvFn():this.rvValue}async parseResponse(t){if(this.isFetchResponse(t)){if(!t.ok)throw new Error(`Request failed with status ${t.status}`);return await t.json()}return t}normalizeCollectionResponse(t){if(this.isCollectionResponseEnvelope(t)){const e=t.data;return{data:e.data,meta:e.meta,rv:t.rv||e.rv}}if(Array.isArray(t))return{data:t,meta:void 0,rv:void 0};if(typeof t=="object"&&t!==null&&"data"in t){const{data:e,meta:n,rv:s}=t;return{data:e,meta:n,rv:s}}return{data:[],meta:void 0,rv:void 0}}buildUrl(t,e){const n=new URL(`${this.baseUrl}/v1/${t}`);return Object.entries(e).forEach(([s,r])=>{r!=null&&n.searchParams.append(s,String(r))}),n.toString()}resolveRequestUrl(t){return t?t instanceof URL?t:new URL(t,"http://localhost"):null}isFetchResponse(t){return typeof t=="object"&&t!==null&&"json"in t&&typeof t.json=="function"&&"ok"in t}hasRevision(t){return typeof t=="object"&&t!==null&&"rv"in t}isCollectionResponseEnvelope(t){return typeof t=="object"&&t!==null&&"data"in t&&typeof t.data=="object"&&t.data!==null&&"data"in t.data&&Array.isArray(t.data.data)}}exports.ApiClient=N;exports.B10cksDataApi=A;exports.PreviewBridge=h;exports.PreviewStore=I;exports.attachEditable=F;exports.attachEditableField=O;exports.bindPreviewStore=U;exports.buildLocalizedPath=w;exports.createB10cksDataApi=k;exports.ensurePreviewStyles=v;exports.filterSitemapEntries=x;exports.getAtPath=D;exports.normalizePathSegment=y;exports.previewBridge=l;exports.renderSitemapIndex=M;exports.renderSitemapXml=z;exports.resolveB10cksLink=P;exports.serializeFilter=R;exports.setAtPath=p;exports.setPreviewScrollOffset=_;exports.types=B;
2
19
  //# sourceMappingURL=index.js.map