@obelism/content 0.8.0 → 0.9.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
@@ -241,7 +241,27 @@ bridge.on('change', ({ stats, statsVisible }) => …) // stats[uid].rate, 0–1
241
241
  ## Helpers
242
242
 
243
243
  - `richTextToHtml(doc, { renderEntry?, renderAsset? })`, `richTextToPlainText(doc)`
244
- - `imageUrl(asset, { w, h, fit, format, q })`, `imageSrcSet(asset, [480, 960])`
244
+ - `imageUrl(asset, { w, h, fit, format, q, focus })`, `imageSrcSet(asset, [480, 960])`:
245
+ with `fit: 'cover'` or `'crop'` the crop keeps the asset's `focalPoint` (set
246
+ in the CMS under Media) in view, or the `focus` you pass (`{ x, y }` or
247
+ `'auto'`)
248
+ - `alternateLinks(entry, { url, defaultLocale })`: every locale's URL of a page
249
+ (`entry.paths`), plus `x-default`, for `<link rel="alternate" hreflang>`
250
+ - `sitemapXml(entries, { url, defaultLocale, exclude? })`: a sitemap with one
251
+ `<url>` per page and locale and its alternates; pages with "Hide from search
252
+ engines" (`fields.noIndex`) are left out. `url(path, locale)` decides where
253
+ a locale's path lives on your site:
254
+
255
+ ```ts
256
+ const pages = []
257
+ for await (const page of content.getAllEntries({ contentType: 'page' })) {
258
+ pages.push(page)
259
+ }
260
+ const xml = sitemapXml(pages, {
261
+ url: (path, locale) => `https://example.com/${locale}/${path}`,
262
+ defaultLocale: 'en',
263
+ })
264
+ ```
245
265
 
246
266
  ## A/B tests and feature flags
247
267
 
@@ -25,7 +25,8 @@ const exampleAsset = (mimeTypes)=>{
25
25
  width: image ? 1600 : null,
26
26
  height: image ? 900 : null,
27
27
  title: null,
28
- alt: image ? 'An example image' : null
28
+ alt: image ? 'An example image' : null,
29
+ focalPoint: null
29
30
  };
30
31
  };
31
32
  const exampleRichText = (name)=>({
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, d as EntryStub, e as RichTextNode } from './types-2evub96q.js';
2
- export { C as ContentTypeDefinition, f as ContentTypeKind, g as Environment, F as FieldDefinition, h as FieldType, i as RichTextDocument, j as RichTextMark } from './types-2evub96q.js';
1
+ import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, d as EntryStub, e as RichTextNode } from './types-fxvuz29w.js';
2
+ export { C as ContentTypeDefinition, f as ContentTypeKind, g as Environment, F as FieldDefinition, h as FieldType, i as RichTextDocument, j as RichTextMark } from './types-fxvuz29w.js';
3
3
 
4
4
  type ContentClientOptions = {
5
5
  token: string;
@@ -84,9 +84,13 @@ type ImageTransform = {
84
84
  fit?: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad' | undefined;
85
85
  format?: 'auto' | 'avif' | 'webp' | 'jpeg' | 'png' | undefined;
86
86
  q?: number | undefined;
87
+ focus?: {
88
+ x: number;
89
+ y: number;
90
+ } | 'auto' | undefined;
87
91
  };
88
- declare const imageUrl: (asset: Pick<Asset, "url"> | string, transform?: ImageTransform) => string;
89
- declare const imageSrcSet: (asset: Pick<Asset, "url"> | string, widths: number[], transform?: Omit<ImageTransform, "w">) => string;
92
+ declare const imageUrl: (asset: (Pick<Asset, "url"> & Partial<Pick<Asset, "focalPoint">>) | string, transform?: ImageTransform) => string;
93
+ declare const imageSrcSet: (asset: (Pick<Asset, "url"> & Partial<Pick<Asset, "focalPoint">>) | string, widths: number[], transform?: Omit<ImageTransform, "w">) => string;
90
94
 
91
95
  declare const IMPROVE_BLOCK = "_improve";
92
96
  declare const isImproveBlock: (block: Block) => block is ImproveBlock;
@@ -116,6 +120,21 @@ declare const richTextToHtml: (doc: RichTextNode | null | undefined, { renderEnt
116
120
  declare const headingLevel: (node: RichTextNode) => 1 | 2 | 3 | 4 | 5 | 6;
117
121
  declare const richTextToPlainText: (doc: RichTextNode | null | undefined) => string;
118
122
 
119
- export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, ModelFile, PREVIEW_PARAMS, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, UID_ATTRIBUTE, editable, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText };
120
- export type { ContentClientOptions, EntriesQuery, ImageTransform, PageLookup, PreviewParams, RedirectsQuery, RequestOptions, RichTextHtmlOptions };
123
+ type LocalizedUrlOptions = {
124
+ url: (path: string, locale: string) => string;
125
+ defaultLocale?: string | undefined;
126
+ };
127
+ type AlternateLink = {
128
+ hreflang: string;
129
+ href: string;
130
+ };
131
+ type Page = Pick<Entry, 'paths'> & Partial<Pick<Entry, 'updatedAt' | 'fields'>>;
132
+ declare const alternateLinks: (entry: Pick<Entry, "paths">, { url, defaultLocale }: LocalizedUrlOptions) => AlternateLink[];
133
+ type SitemapOptions = LocalizedUrlOptions & {
134
+ exclude?: ((entry: Page) => boolean) | undefined;
135
+ };
136
+ declare const sitemapXml: (entries: Iterable<Page>, { exclude, ...options }: SitemapOptions) => string;
137
+
138
+ export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, ModelFile, PREVIEW_PARAMS, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, UID_ATTRIBUTE, alternateLinks, editable, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText, sitemapXml };
139
+ export type { AlternateLink, ContentClientOptions, EntriesQuery, ImageTransform, LocalizedUrlOptions, PageLookup, PreviewParams, RedirectsQuery, RequestOptions, RichTextHtmlOptions, SitemapOptions };
121
140
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/preview.ts","../src/richText.ts"],"mappings":";;;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA;AACA,eAAe,cAAc;AAC7B;AACA,WAAW,WAAW;AACtB,mBAAmB,KAAK;AACxB;AACA,KAAK,cAAc;AACnB;AACA;AACA,eAAe,cAAc;AAC7B,WAAW,WAAW;AACtB;AACA,KAAK,gBAAgB;AACrB,KAAK,YAAY,GAAG,cAAc;AAClC;AACA;AACA,aAAa,MAAM,SAAS,gBAAgB,YAAY,gBAAgB;AACxE;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB;AACA;AACA;AACA,WAAW,WAAW;AACtB;AACA,KAAK,UAAU,gBAAgB,KAAK,GAAG,KAAK;AAC5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,aAAa;AAC3B;AACA;AACA,2FAA2F,oBAAoB;AAC/G,6BAA6B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO;AAC9F,8BAA8B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO,CAAC,UAAU;AAC1G,oDAAoD,cAAc,KAAK,OAAO,CAAC,YAAY;AAC3F,4BAA4B,IAAI,CAAC,cAAc,YAAY,cAAc,CAAC,QAAQ;AAClF,8BAA8B,KAAK,GAAG,KAAK,wBAAwB,cAAc,KAAK,OAAO;AAC7F,gCAAgC,KAAK,GAAG,KAAK,wEAAwE,YAAY,KAAK,OAAO,CAAC,SAAS;AACvJ,mCAAmC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,wBAAwB,OAAO;AAC1G,iCAAiC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,YAAY,cAAc;AACnG,gCAAgC,IAAI,CAAC,cAAc,cAAc,OAAO,CAAC,SAAS;AAClF;;ACtDA,cAAc,WAAW;;ACAzB,cAAc,aAAa;AAC3B,cAAc,eAAe;AAC7B,KAAK,QAAQ;AACb;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,yBAAyB,QAAQ;;ACXjC,cAAc,eAAe,SAAS,KAAK;AAC3C;AACA;AACA;AACA;AACA;;ACJA,KAAK,cAAc;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ,UAAU,IAAI,CAAC,KAAK,+BAA+B,cAAc;AACvF,cAAc,WAAW,UAAU,IAAI,CAAC,KAAK,iDAAiD,IAAI,CAAC,cAAc;;ACRjH,cAAc,aAAa;AAC3B,cAAc,cAAc,UAAU,KAAK,cAAc,YAAY;AACrE,cAAc,kBAAkB,kBAAkB,KAAK,SAAS,YAAY;;ACH5E,cAAc,cAAc;AAC5B;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA;AACA,KAAK,iBAAiB,GAAG,GAAG,GAAG,eAAe,YAAY,MAAM;AAChE,cAAc,iBAAiB,UAAU,iBAAiB,KAAK,aAAa;;ACX5E,cAAc,SAAS,EAAE,MAAM;AAC/B,cAAc,UAAU;AACxB,KAAK,mBAAmB;AACxB,0BAA0B,KAAK,GAAG,SAAS,QAAQ,YAAY;AAC/D,0BAA0B,KAAK,QAAQ,YAAY;AACnD;AACA,cAAc,cAAc,QAAQ,YAAY,oDAAoD,mBAAmB;AACvH,cAAc,YAAY,SAAS,YAAY;AAC/C,cAAc,mBAAmB,QAAQ,YAAY;;","names":[]}
1
+ {"version":3,"file":"index.d.mts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/preview.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, d as EntryStub, e as RichTextNode } from './types-2evub96q.js';
2
- export { C as ContentTypeDefinition, f as ContentTypeKind, g as Environment, F as FieldDefinition, h as FieldType, i as RichTextDocument, j as RichTextMark } from './types-2evub96q.js';
1
+ import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, d as EntryStub, e as RichTextNode } from './types-fxvuz29w.js';
2
+ export { C as ContentTypeDefinition, f as ContentTypeKind, g as Environment, F as FieldDefinition, h as FieldType, i as RichTextDocument, j as RichTextMark } from './types-fxvuz29w.js';
3
3
 
4
4
  type ContentClientOptions = {
5
5
  token: string;
@@ -84,9 +84,13 @@ type ImageTransform = {
84
84
  fit?: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad' | undefined;
85
85
  format?: 'auto' | 'avif' | 'webp' | 'jpeg' | 'png' | undefined;
86
86
  q?: number | undefined;
87
+ focus?: {
88
+ x: number;
89
+ y: number;
90
+ } | 'auto' | undefined;
87
91
  };
88
- declare const imageUrl: (asset: Pick<Asset, "url"> | string, transform?: ImageTransform) => string;
89
- declare const imageSrcSet: (asset: Pick<Asset, "url"> | string, widths: number[], transform?: Omit<ImageTransform, "w">) => string;
92
+ declare const imageUrl: (asset: (Pick<Asset, "url"> & Partial<Pick<Asset, "focalPoint">>) | string, transform?: ImageTransform) => string;
93
+ declare const imageSrcSet: (asset: (Pick<Asset, "url"> & Partial<Pick<Asset, "focalPoint">>) | string, widths: number[], transform?: Omit<ImageTransform, "w">) => string;
90
94
 
91
95
  declare const IMPROVE_BLOCK = "_improve";
92
96
  declare const isImproveBlock: (block: Block) => block is ImproveBlock;
@@ -116,6 +120,21 @@ declare const richTextToHtml: (doc: RichTextNode | null | undefined, { renderEnt
116
120
  declare const headingLevel: (node: RichTextNode) => 1 | 2 | 3 | 4 | 5 | 6;
117
121
  declare const richTextToPlainText: (doc: RichTextNode | null | undefined) => string;
118
122
 
119
- export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, ModelFile, PREVIEW_PARAMS, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, UID_ATTRIBUTE, editable, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText };
120
- export type { ContentClientOptions, EntriesQuery, ImageTransform, PageLookup, PreviewParams, RedirectsQuery, RequestOptions, RichTextHtmlOptions };
123
+ type LocalizedUrlOptions = {
124
+ url: (path: string, locale: string) => string;
125
+ defaultLocale?: string | undefined;
126
+ };
127
+ type AlternateLink = {
128
+ hreflang: string;
129
+ href: string;
130
+ };
131
+ type Page = Pick<Entry, 'paths'> & Partial<Pick<Entry, 'updatedAt' | 'fields'>>;
132
+ declare const alternateLinks: (entry: Pick<Entry, "paths">, { url, defaultLocale }: LocalizedUrlOptions) => AlternateLink[];
133
+ type SitemapOptions = LocalizedUrlOptions & {
134
+ exclude?: ((entry: Page) => boolean) | undefined;
135
+ };
136
+ declare const sitemapXml: (entries: Iterable<Page>, { exclude, ...options }: SitemapOptions) => string;
137
+
138
+ export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, ModelFile, PREVIEW_PARAMS, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, UID_ATTRIBUTE, alternateLinks, editable, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText, sitemapXml };
139
+ export type { AlternateLink, ContentClientOptions, EntriesQuery, ImageTransform, LocalizedUrlOptions, PageLookup, PreviewParams, RedirectsQuery, RequestOptions, RichTextHtmlOptions, SitemapOptions };
121
140
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/preview.ts","../src/richText.ts"],"mappings":";;;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA;AACA,eAAe,cAAc;AAC7B;AACA,WAAW,WAAW;AACtB,mBAAmB,KAAK;AACxB;AACA,KAAK,cAAc;AACnB;AACA;AACA,eAAe,cAAc;AAC7B,WAAW,WAAW;AACtB;AACA,KAAK,gBAAgB;AACrB,KAAK,YAAY,GAAG,cAAc;AAClC;AACA;AACA,aAAa,MAAM,SAAS,gBAAgB,YAAY,gBAAgB;AACxE;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB;AACA;AACA;AACA,WAAW,WAAW;AACtB;AACA,KAAK,UAAU,gBAAgB,KAAK,GAAG,KAAK;AAC5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,aAAa;AAC3B;AACA;AACA,2FAA2F,oBAAoB;AAC/G,6BAA6B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO;AAC9F,8BAA8B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO,CAAC,UAAU;AAC1G,oDAAoD,cAAc,KAAK,OAAO,CAAC,YAAY;AAC3F,4BAA4B,IAAI,CAAC,cAAc,YAAY,cAAc,CAAC,QAAQ;AAClF,8BAA8B,KAAK,GAAG,KAAK,wBAAwB,cAAc,KAAK,OAAO;AAC7F,gCAAgC,KAAK,GAAG,KAAK,wEAAwE,YAAY,KAAK,OAAO,CAAC,SAAS;AACvJ,mCAAmC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,wBAAwB,OAAO;AAC1G,iCAAiC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,YAAY,cAAc;AACnG,gCAAgC,IAAI,CAAC,cAAc,cAAc,OAAO,CAAC,SAAS;AAClF;;ACtDA,cAAc,WAAW;;ACAzB,cAAc,aAAa;AAC3B,cAAc,eAAe;AAC7B,KAAK,QAAQ;AACb;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,yBAAyB,QAAQ;;ACXjC,cAAc,eAAe,SAAS,KAAK;AAC3C;AACA;AACA;AACA;AACA;;ACJA,KAAK,cAAc;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ,UAAU,IAAI,CAAC,KAAK,+BAA+B,cAAc;AACvF,cAAc,WAAW,UAAU,IAAI,CAAC,KAAK,iDAAiD,IAAI,CAAC,cAAc;;ACRjH,cAAc,aAAa;AAC3B,cAAc,cAAc,UAAU,KAAK,cAAc,YAAY;AACrE,cAAc,kBAAkB,kBAAkB,KAAK,SAAS,YAAY;;ACH5E,cAAc,cAAc;AAC5B;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA;AACA,KAAK,iBAAiB,GAAG,GAAG,GAAG,eAAe,YAAY,MAAM;AAChE,cAAc,iBAAiB,UAAU,iBAAiB,KAAK,aAAa;;ACX5E,cAAc,SAAS,EAAE,MAAM;AAC/B,cAAc,UAAU;AACxB,KAAK,mBAAmB;AACxB,0BAA0B,KAAK,GAAG,SAAS,QAAQ,YAAY;AAC/D,0BAA0B,KAAK,QAAQ,YAAY;AACnD;AACA,cAAc,cAAc,QAAQ,YAAY,oDAAoD,mBAAmB;AACvH,cAAc,YAAY,SAAS,YAAY;AAC/C,cAAc,mBAAmB,QAAQ,YAAY;;","names":[]}
1
+ {"version":3,"file":"index.d.ts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/preview.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -218,16 +218,25 @@ const KEYS = [
218
218
  'format',
219
219
  'q'
220
220
  ];
221
+ const CROPS = new Set([
222
+ 'cover',
223
+ 'crop'
224
+ ]);
221
225
  // An asset's URL with a resize / re-encode query. PNG, JPEG, GIF, WebP and
222
226
  // AVIF are transformed; other files ignore the query.
223
227
  //
224
228
  // imageUrl(asset, { w: 800 })
229
+ // imageUrl(asset, { w: 800, h: 800, fit: 'cover' }) // around its focal point
225
230
  const imageUrl = (asset, transform = {})=>{
226
231
  const url = new URL(typeof asset === 'string' ? asset : asset.url);
227
232
  for (const key of KEYS){
228
233
  const value = transform[key];
229
234
  if (value !== undefined) url.searchParams.set(key, String(value));
230
235
  }
236
+ const focus = transform.focus ?? (typeof asset === 'string' ? undefined : asset.focalPoint ?? undefined);
237
+ if (focus && CROPS.has(transform.fit ?? '')) {
238
+ url.searchParams.set('focus', focus === 'auto' ? 'auto' : `${focus.x},${focus.y}`);
239
+ }
231
240
  return url.toString();
232
241
  };
233
242
  // A `srcset` with one candidate per width.
@@ -366,6 +375,53 @@ const richTextToPlainText = (doc)=>{
366
375
  return parts.join('').replace(/\n+/g, '\n').trim();
367
376
  };
368
377
 
378
+ // Every locale's URL of a page, for `<link rel="alternate" hreflang>` tags
379
+ // and language switchers. Empty for data entries.
380
+ const alternateLinks = (entry, { url, defaultLocale })=>{
381
+ const links = Object.entries(entry.paths).map(([locale, path])=>({
382
+ hreflang: locale,
383
+ href: url(path, locale)
384
+ }));
385
+ const fallback = defaultLocale ? entry.paths[defaultLocale] : undefined;
386
+ if (defaultLocale && fallback !== undefined) {
387
+ links.push({
388
+ hreflang: 'x-default',
389
+ href: url(fallback, defaultLocale)
390
+ });
391
+ }
392
+ return links;
393
+ };
394
+ const escapeXml = (text)=>text.replace(/[<>&'"]/g, (char)=>({
395
+ '<': '&lt;',
396
+ '>': '&gt;',
397
+ '&': '&amp;',
398
+ "'": '&apos;',
399
+ '"': '&quot;'
400
+ })[char]);
401
+ const hidden = (entry)=>entry.fields?.noIndex === true;
402
+ // A sitemap.xml with one `<url>` per page and locale, each listing the page
403
+ // in every locale (`xhtml:link`), and `lastmod` from `updatedAt`. Pages
404
+ // without paths (data entries) are skipped. Fetch the entries with a
405
+ // delivery token: drafts don't belong in a sitemap.
406
+ const sitemapXml = (entries, { exclude = hidden, ...options })=>{
407
+ const urls = [];
408
+ for (const entry of entries){
409
+ if (exclude(entry)) continue;
410
+ const links = alternateLinks(entry, options);
411
+ const alternates = links.map(({ hreflang, href })=>`<xhtml:link rel="alternate" hreflang="${escapeXml(hreflang)}" href="${escapeXml(href)}"/>`).join('');
412
+ for (const { hreflang, href } of links){
413
+ if (hreflang === 'x-default') continue;
414
+ urls.push(`<url><loc>${escapeXml(href)}</loc>${entry.updatedAt ? `<lastmod>${escapeXml(entry.updatedAt)}</lastmod>` : ''}${links.length > 1 ? alternates : ''}</url>`);
415
+ }
416
+ }
417
+ return [
418
+ '<?xml version="1.0" encoding="UTF-8"?>',
419
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">',
420
+ ...urls,
421
+ '</urlset>'
422
+ ].join('\n');
423
+ };
424
+
369
425
  exports.DEFAULT_URL = config.DEFAULT_URL;
370
426
  exports.ENTRY_ATTRIBUTE = preview.ENTRY_ATTRIBUTE;
371
427
  exports.PREVIEW_PARAMS = preview.PREVIEW_PARAMS;
@@ -376,6 +432,7 @@ exports.ContentApiError = ContentApiError;
376
432
  exports.ContentClient = ContentClient;
377
433
  exports.IMPROVE_BLOCK = IMPROVE_BLOCK;
378
434
  exports.SAFE_HREF = SAFE_HREF;
435
+ exports.alternateLinks = alternateLinks;
379
436
  exports.escapeHtml = escapeHtml;
380
437
  exports.headingLevel = headingLevel;
381
438
  exports.imageSrcSet = imageSrcSet;
@@ -384,3 +441,4 @@ exports.isImproveBlock = isImproveBlock;
384
441
  exports.pickImproveVariant = pickImproveVariant;
385
442
  exports.richTextToHtml = richTextToHtml;
386
443
  exports.richTextToPlainText = richTextToPlainText;
444
+ exports.sitemapXml = sitemapXml;
package/dist/index.mjs CHANGED
@@ -216,16 +216,25 @@ const KEYS = [
216
216
  'format',
217
217
  'q'
218
218
  ];
219
+ const CROPS = new Set([
220
+ 'cover',
221
+ 'crop'
222
+ ]);
219
223
  // An asset's URL with a resize / re-encode query. PNG, JPEG, GIF, WebP and
220
224
  // AVIF are transformed; other files ignore the query.
221
225
  //
222
226
  // imageUrl(asset, { w: 800 })
227
+ // imageUrl(asset, { w: 800, h: 800, fit: 'cover' }) // around its focal point
223
228
  const imageUrl = (asset, transform = {})=>{
224
229
  const url = new URL(typeof asset === 'string' ? asset : asset.url);
225
230
  for (const key of KEYS){
226
231
  const value = transform[key];
227
232
  if (value !== undefined) url.searchParams.set(key, String(value));
228
233
  }
234
+ const focus = transform.focus ?? (typeof asset === 'string' ? undefined : asset.focalPoint ?? undefined);
235
+ if (focus && CROPS.has(transform.fit ?? '')) {
236
+ url.searchParams.set('focus', focus === 'auto' ? 'auto' : `${focus.x},${focus.y}`);
237
+ }
229
238
  return url.toString();
230
239
  };
231
240
  // A `srcset` with one candidate per width.
@@ -364,4 +373,51 @@ const richTextToPlainText = (doc)=>{
364
373
  return parts.join('').replace(/\n+/g, '\n').trim();
365
374
  };
366
375
 
367
- export { ContentApiError, ContentClient, DEFAULT_URL, IMPROVE_BLOCK, SAFE_HREF, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, richTextToHtml, richTextToPlainText };
376
+ // Every locale's URL of a page, for `<link rel="alternate" hreflang>` tags
377
+ // and language switchers. Empty for data entries.
378
+ const alternateLinks = (entry, { url, defaultLocale })=>{
379
+ const links = Object.entries(entry.paths).map(([locale, path])=>({
380
+ hreflang: locale,
381
+ href: url(path, locale)
382
+ }));
383
+ const fallback = defaultLocale ? entry.paths[defaultLocale] : undefined;
384
+ if (defaultLocale && fallback !== undefined) {
385
+ links.push({
386
+ hreflang: 'x-default',
387
+ href: url(fallback, defaultLocale)
388
+ });
389
+ }
390
+ return links;
391
+ };
392
+ const escapeXml = (text)=>text.replace(/[<>&'"]/g, (char)=>({
393
+ '<': '&lt;',
394
+ '>': '&gt;',
395
+ '&': '&amp;',
396
+ "'": '&apos;',
397
+ '"': '&quot;'
398
+ })[char]);
399
+ const hidden = (entry)=>entry.fields?.noIndex === true;
400
+ // A sitemap.xml with one `<url>` per page and locale, each listing the page
401
+ // in every locale (`xhtml:link`), and `lastmod` from `updatedAt`. Pages
402
+ // without paths (data entries) are skipped. Fetch the entries with a
403
+ // delivery token: drafts don't belong in a sitemap.
404
+ const sitemapXml = (entries, { exclude = hidden, ...options })=>{
405
+ const urls = [];
406
+ for (const entry of entries){
407
+ if (exclude(entry)) continue;
408
+ const links = alternateLinks(entry, options);
409
+ const alternates = links.map(({ hreflang, href })=>`<xhtml:link rel="alternate" hreflang="${escapeXml(hreflang)}" href="${escapeXml(href)}"/>`).join('');
410
+ for (const { hreflang, href } of links){
411
+ if (hreflang === 'x-default') continue;
412
+ urls.push(`<url><loc>${escapeXml(href)}</loc>${entry.updatedAt ? `<lastmod>${escapeXml(entry.updatedAt)}</lastmod>` : ''}${links.length > 1 ? alternates : ''}</url>`);
413
+ }
414
+ }
415
+ return [
416
+ '<?xml version="1.0" encoding="UTF-8"?>',
417
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">',
418
+ ...urls,
419
+ '</urlset>'
420
+ ].join('\n');
421
+ };
422
+
423
+ export { ContentApiError, ContentClient, DEFAULT_URL, IMPROVE_BLOCK, SAFE_HREF, alternateLinks, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, richTextToHtml, richTextToPlainText, sitemapXml };
@@ -10,6 +10,10 @@ type Asset = {
10
10
  height: number | null;
11
11
  title: string | Record<string, string> | null;
12
12
  alt: string | Record<string, string> | null;
13
+ focalPoint: {
14
+ x: number;
15
+ y: number;
16
+ } | null;
13
17
  };
14
18
  type Entry<TFields extends Record<string, unknown> = Record<string, unknown>, TContentType extends string = string> = {
15
19
  id: string;
@@ -134,4 +138,4 @@ type ModelFile = {
134
138
  };
135
139
 
136
140
  export type { Asset as A, Block as B, ContentTypeDefinition as C, Entry as E, FieldDefinition as F, ImproveBlock as I, ModelFile as M, RichTextFormat as R, RedirectList as a, Redirect as b, EntryList as c, EntryStub as d, RichTextNode as e, ContentTypeKind as f, Environment as g, FieldType as h, RichTextDocument as i, RichTextMark as j };
137
- //# sourceMappingURL=types-2evub96q.d.ts.map
141
+ //# sourceMappingURL=types-fxvuz29w.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-fxvuz29w.d.ts","sources":["../src/types.ts"],"mappings":"AAAA,KAAK,WAAW;AAChB,KAAK,cAAc;AACnB,KAAK,KAAK;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,MAAM;AAC1B,kBAAkB,MAAM;AACxB;AACA;AACA;AACA;AACA;AACA,KAAK,KAAK,iBAAiB,MAAM,oBAAoB,MAAM;AAC3D;AACA;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB;AACA;AACA;AACA;AACA;AACA,KAAK,SAAS;AACd;AACA;AACA;AACA,KAAK,KAAK,iBAAiB,MAAM,oBAAoB,MAAM;AAC3D;AACA;AACA;AACA,KAAK,YAAY,gBAAgB,KAAK,GAAG,KAAK,IAAI,KAAK;AACvD;AACA;AACA,cAAc,MAAM;AACpB;AACA,KAAK,YAAY;AACjB;AACA,YAAY,MAAM;AAClB;AACA,KAAK,YAAY;AACjB;AACA,YAAY,MAAM;AAClB,cAAc,YAAY;AAC1B;AACA,YAAY,YAAY;AACxB;AACA,KAAK,gBAAgB,GAAG,YAAY;AACpC;AACA;AACA,KAAK,SAAS,gBAAgB,KAAK,GAAG,KAAK;AAC3C;AACA;AACA;AACA;AACA;AACA,KAAK,QAAQ;AACb;AACA;AACA;AACA;AACA,KAAK,YAAY;AACjB,WAAW,QAAQ;AACnB;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,KAAK,SAAS;AACd;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,eAAe,GAAG,SAAS;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,SAAS,GAAG,eAAe;AAChC,KAAK,qBAAqB;AAC1B;AACA;AACA,UAAU,eAAe;AACzB;AACA,YAAY,eAAe;AAC3B;AACA,KAAK,SAAS;AACd;AACA;AACA,kBAAkB,qBAAqB;AACvC;;","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@obelism/content",
3
3
  "description": "Obelism Content SDK: the delivery API client, the visual editor bridge with prompt editing, editing on your own site, webhook verification, and the content model CLI",
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "keywords": [
6
6
  "cms",
7
7
  "headless-cms",
@@ -1 +0,0 @@
1
- {"version":3,"file":"types-2evub96q.d.ts","sources":["../src/types.ts"],"mappings":"AAAA,KAAK,WAAW;AAChB,KAAK,cAAc;AACnB,KAAK,KAAK;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,MAAM;AAC1B,kBAAkB,MAAM;AACxB;AACA,KAAK,KAAK,iBAAiB,MAAM,oBAAoB,MAAM;AAC3D;AACA;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB;AACA;AACA;AACA;AACA;AACA,KAAK,SAAS;AACd;AACA;AACA;AACA,KAAK,KAAK,iBAAiB,MAAM,oBAAoB,MAAM;AAC3D;AACA;AACA;AACA,KAAK,YAAY,gBAAgB,KAAK,GAAG,KAAK,IAAI,KAAK;AACvD;AACA;AACA,cAAc,MAAM;AACpB;AACA,KAAK,YAAY;AACjB;AACA,YAAY,MAAM;AAClB;AACA,KAAK,YAAY;AACjB;AACA,YAAY,MAAM;AAClB,cAAc,YAAY;AAC1B;AACA,YAAY,YAAY;AACxB;AACA,KAAK,gBAAgB,GAAG,YAAY;AACpC;AACA;AACA,KAAK,SAAS,gBAAgB,KAAK,GAAG,KAAK;AAC3C;AACA;AACA;AACA;AACA;AACA,KAAK,QAAQ;AACb;AACA;AACA;AACA;AACA,KAAK,YAAY;AACjB,WAAW,QAAQ;AACnB;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,KAAK,SAAS;AACd;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,eAAe,GAAG,SAAS;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,SAAS,GAAG,eAAe;AAChC,KAAK,qBAAqB;AAC1B;AACA;AACA,UAAU,eAAe;AACzB;AACA,YAAY,eAAe;AAC3B;AACA,KAAK,SAAS;AACd;AACA;AACA,kBAAkB,qBAAqB;AACvC;;","names":[]}