@sandboxsonny/ace-kit 0.8.0 → 0.10.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @sandboxsonny/ace-kit
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4215f32: Show the site's favicon in `<AceSeo>`. The favicon is chosen in the site's settings in the CMS; `<AceSeo>` adds `<link rel="icon">` and `<link rel="apple-touch-icon">` for it when you don't pass a `favicon` yourself. If your layout already has its own icon link, remove it, or the browser may pick either. `getSite()` now also returns the favicon.
8
+
9
+ ## 0.9.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 6e6885f: Show the site's social image in `<AceSeo>`. The image is chosen in the site's settings in the CMS; `<AceSeo>` uses it for `og:image` and the Twitter card when you don't pass an `image` yourself. Also adds `getSite()` (Astro and React Router) for the site's name and social image.
14
+
3
15
  ## 0.8.0
4
16
 
5
17
  ### Minor Changes
@@ -3,10 +3,19 @@ import { SEO } from "astro-seo";
3
3
 
4
4
  import { seoProperties } from "../dist/index.js";
5
5
  import type { AceSeoOptions } from "../dist/index.js";
6
+ import { getSite } from "../dist/astro.js";
6
7
 
7
8
  type Props = AceSeoOptions;
8
9
 
9
- const properties = seoProperties(Astro.props);
10
+ // With no picture or icon of its own, a page shows the ones chosen in the site's settings. The tags are never worth
11
+ // failing the page for, so a CMS that can't be reached just means none.
12
+ const needsSite = Astro.props.image === undefined || Astro.props.favicon === undefined;
13
+ const site = needsSite ? await getSite().catch(() => null) : null;
14
+ const properties = seoProperties({
15
+ ...Astro.props,
16
+ image: Astro.props.image ?? site?.social_image?.src,
17
+ favicon: Astro.props.favicon ?? site?.favicon ?? undefined,
18
+ });
10
19
  ---
11
20
 
12
21
  <SEO {...properties} />
package/dist/astro.d.ts CHANGED
@@ -14,6 +14,45 @@ type AceContentTypeSchema = {
14
14
  fields: AceFieldSchema[];
15
15
  };
16
16
 
17
+ /** An image as the CMS Ace public API returns it. Use `src` and `srcset` in an `<img>`. */
18
+ type AceImageData = {
19
+ id: number;
20
+ /** The untouched original. */
21
+ url: string;
22
+ /** A web-sized copy, for browsers that ignore `srcset`. */
23
+ src: string;
24
+ /** Copies at several widths; empty when the image is too small to need them. */
25
+ srcset: string;
26
+ width: number | null;
27
+ height: number | null;
28
+ alt_text: string;
29
+ content_type: string;
30
+ };
31
+
32
+ /** The site's favicon as the CMS Ace public API returns it: the original, and square PNG copies. */
33
+ type AceFavicon = {
34
+ id: number;
35
+ /** The untouched original. */
36
+ url: string;
37
+ /** A small square PNG (48 pixels), for the browser tab and search results. */
38
+ icon: string;
39
+ /** A square PNG (180 pixels), for a phone's home screen. */
40
+ apple_touch_icon: string;
41
+ content_type: string;
42
+ };
43
+
44
+ /** The site the request is for, as the CMS Ace public API returns it. */
45
+ type AceSite = {
46
+ id: number;
47
+ name: string;
48
+ domain: string;
49
+ handle: string;
50
+ /** The picture the site's owners chose to show in search results and social posts; null if none. */
51
+ social_image: AceImageData | null;
52
+ /** The small picture the site's owners chose for browser tabs; null if none. */
53
+ favicon: AceFavicon | null;
54
+ };
55
+
17
56
  /** One link of a menu as the public API returns it, with its address already worked out. */
18
57
  type AceMenuItem = {
19
58
  label: string;
@@ -90,6 +129,7 @@ declare const ace: {
90
129
  limit?: number;
91
130
  }): Promise<AceEntry<AceValuesOf<Handle>>[]>;
92
131
  getMenu(handle: string): Promise<AceMenu | null>;
132
+ getSite(): Promise<AceSite | null>;
93
133
  getContentTypes(): Promise<AceContentTypeSchema[]>;
94
134
  };
95
135
  /** One live entry of a content type by its handle, or `null`. See `createAceClient`. */
@@ -102,6 +142,8 @@ declare const getEntries: <Handle extends AceContentTypeHandle>(contentTypeHandl
102
142
  }) => Promise<AceEntry<AceValuesOf<Handle>>[]>;
103
143
  /** A navigation menu with every link worked out, or `null`. */
104
144
  declare const getMenu: (handle: string) => Promise<AceMenu | null>;
145
+ /** The site: its name and the social image its owners chose (`social_image`, or `null`). */
146
+ declare const getSite: () => Promise<AceSite | null>;
105
147
  /**
106
148
  * A menu's links, ready to draw: `[{ label, to, children? }]`. If the site has no such menu, or the CMS can't be
107
149
  * reached, you get `fallback` instead, so a missing menu never takes a page down.
@@ -136,4 +178,4 @@ declare function getPage<Values = Record<string, unknown>>(astro: {
136
178
  };
137
179
  }): Promise<AcePage<Values>>;
138
180
 
139
- export { ace, applyPage, getEntries, getEntry, getMenu, getMenuLinks, getPage };
181
+ export { ace, applyPage, getEntries, getEntry, getMenu, getMenuLinks, getPage, getSite };
package/dist/astro.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createAceClient, loadMenuLinks, loadPage } from './chunk-XYOW63V6.js';
1
+ import { createAceClient, loadMenuLinks, loadPage } from './chunk-EKZRLWNO.js';
2
2
  import './chunk-M5WR5TCR.js';
3
3
  import './chunk-YLO6IAXF.js';
4
4
  import { ACE_SITE_DOMAIN, ACE_API_URL } from 'astro:env/server';
@@ -7,6 +7,7 @@ var ace = createAceClient({ apiUrl: ACE_API_URL, siteDomain: ACE_SITE_DOMAIN });
7
7
  var getEntry = ace.getEntry;
8
8
  var getEntries = ace.getEntries;
9
9
  var getMenu = ace.getMenu;
10
+ var getSite = ace.getSite;
10
11
  function getMenuLinks(handle, options = {}) {
11
12
  return loadMenuLinks(ace, handle, options.fallback ?? []);
12
13
  }
@@ -21,4 +22,4 @@ async function getPage(astro) {
21
22
  return page;
22
23
  }
23
24
 
24
- export { ace, applyPage, getEntries, getEntry, getMenu, getMenuLinks, getPage };
25
+ export { ace, applyPage, getEntries, getEntry, getMenu, getMenuLinks, getPage, getSite };
@@ -89,6 +89,10 @@ function createAceClient({
89
89
  getMenu(handle) {
90
90
  return get(`/public/menus/${encodeURIComponent(handle)}`);
91
91
  },
92
+ /** The site itself: its name, and the social image its owners chose (null if none). */
93
+ getSite() {
94
+ return get("/public/site");
95
+ },
92
96
  /** What the site's content types and fields are, which is what type generation reads. */
93
97
  async getContentTypes() {
94
98
  return await get("/public/content-types") ?? [];
package/dist/cli.js CHANGED
@@ -9,8 +9,8 @@ if (major < 22 || major === 22 && minor < 12) {
9
9
  process.exit(1);
10
10
  }
11
11
  try {
12
- const { createRealEnvironment } = await import("./environment-NGXBLWXM.js");
13
- const { runCli } = await import("./main-JOA27GX6.js");
12
+ const { createRealEnvironment } = await import("./environment-M3B4UZ5T.js");
13
+ const { runCli } = await import("./main-6O2IIVHO.js");
14
14
  const environment = await createRealEnvironment();
15
15
  process.exit(await runCli(process.argv.slice(2), environment));
16
16
  } catch (error) {
@@ -145,7 +145,7 @@ function createLastApiUrl(path) {
145
145
  }
146
146
 
147
147
  // src/cli/version.ts
148
- var aceKitVersion = true ? "0.8.0" : "development";
148
+ var aceKitVersion = true ? "0.10.0" : "development";
149
149
 
150
150
  // src/cli/environment.ts
151
151
  var runCommand = (command, commandArguments, input) => new Promise((resolve, reject) => {
package/dist/index.d.ts CHANGED
@@ -84,6 +84,45 @@ type AceNavigationLink = {
84
84
  children?: AceNavigationLink[];
85
85
  };
86
86
 
87
+ /** An image as the CMS Ace public API returns it. Use `src` and `srcset` in an `<img>`. */
88
+ type AceImageData = {
89
+ id: number;
90
+ /** The untouched original. */
91
+ url: string;
92
+ /** A web-sized copy, for browsers that ignore `srcset`. */
93
+ src: string;
94
+ /** Copies at several widths; empty when the image is too small to need them. */
95
+ srcset: string;
96
+ width: number | null;
97
+ height: number | null;
98
+ alt_text: string;
99
+ content_type: string;
100
+ };
101
+
102
+ /** The site's favicon as the CMS Ace public API returns it: the original, and square PNG copies. */
103
+ type AceFavicon = {
104
+ id: number;
105
+ /** The untouched original. */
106
+ url: string;
107
+ /** A small square PNG (48 pixels), for the browser tab and search results. */
108
+ icon: string;
109
+ /** A square PNG (180 pixels), for a phone's home screen. */
110
+ apple_touch_icon: string;
111
+ content_type: string;
112
+ };
113
+
114
+ /** The site the request is for, as the CMS Ace public API returns it. */
115
+ type AceSite = {
116
+ id: number;
117
+ name: string;
118
+ domain: string;
119
+ handle: string;
120
+ /** The picture the site's owners chose to show in search results and social posts; null if none. */
121
+ social_image: AceImageData | null;
122
+ /** The small picture the site's owners chose for browser tabs; null if none. */
123
+ favicon: AceFavicon | null;
124
+ };
125
+
87
126
  /**
88
127
  * A client for the CMS Ace public API. It runs on the server only, so the visitor's browser never talks to the
89
128
  * API. The API finds the site from X-Ace-Site-Domain, which this client sets to the site's domain. X-Forwarded-Host
@@ -104,6 +143,8 @@ declare function createAceClient({ apiUrl, siteDomain, cacheSeconds, fetchImplem
104
143
  }): Promise<AceEntry<AceValuesOf<Handle>>[]>;
105
144
  /** One of the site's navigation menus by its handle, with every link worked out; null if the site has none. */
106
145
  getMenu(handle: string): Promise<AceMenu | null>;
146
+ /** The site itself: its name, and the social image its owners chose (null if none). */
147
+ getSite(): Promise<AceSite | null>;
107
148
  /** What the site's content types and fields are, which is what type generation reads. */
108
149
  getContentTypes(): Promise<AceContentTypeSchema[]>;
109
150
  };
@@ -142,21 +183,6 @@ declare const isHomeAddress: (address: AceAddress) => boolean;
142
183
  */
143
184
  declare function visitablePath(path: string): string;
144
185
 
145
- /** An image as the CMS Ace public API returns it. Use `src` and `srcset` in an `<img>`. */
146
- type AceImageData = {
147
- id: number;
148
- /** The untouched original. */
149
- url: string;
150
- /** A web-sized copy, for browsers that ignore `srcset`. */
151
- src: string;
152
- /** Copies at several widths; empty when the image is too small to need them. */
153
- srcset: string;
154
- width: number | null;
155
- height: number | null;
156
- alt_text: string;
157
- content_type: string;
158
- };
159
-
160
186
  type AceImageOptions = {
161
187
  /** How wide the image is shown, e.g. "(min-width: 64rem) 64rem, 100vw". The browser uses it to pick a copy from srcset. */
162
188
  sizes: string;
@@ -461,8 +487,16 @@ type AceSeoOptions = {
461
487
  siteName?: string;
462
488
  /** The page's address, if it isn't the one requested. Left out, the requested address is used. */
463
489
  canonical?: string;
464
- /** A picture to show when the page is shared: a full web address. */
490
+ /**
491
+ * A picture to show when the page is shared: a full web address. In the Astro `<AceSeo>`, leaving it out uses the
492
+ * social image chosen in the site's settings in the CMS (if any).
493
+ */
465
494
  image?: string;
495
+ /**
496
+ * The site's favicon. In the Astro `<AceSeo>`, leaving it out uses the favicon chosen in the site's settings in the
497
+ * CMS (if any); it then adds `<link rel="icon">` and `<link rel="apple-touch-icon">`.
498
+ */
499
+ favicon?: Pick<AceFavicon, "icon" | "apple_touch_icon">;
466
500
  /** Tells search engines to leave the page out, as for a draft shown in preview. */
467
501
  noindex?: boolean;
468
502
  };
@@ -471,6 +505,11 @@ type Meta = {
471
505
  name?: string;
472
506
  content: string;
473
507
  };
508
+ type Link = {
509
+ rel: string;
510
+ href: string;
511
+ sizes?: string;
512
+ };
474
513
  /** What `astro-seo`'s `<SEO>` takes (only the parts used here). */
475
514
  type AceSeoProperties = {
476
515
  title?: string;
@@ -485,6 +524,7 @@ type AceSeoProperties = {
485
524
  };
486
525
  extend: {
487
526
  meta: Meta[];
527
+ link?: Link[];
488
528
  };
489
529
  };
490
530
  /**
@@ -536,4 +576,4 @@ type AceSection = {
536
576
  blocks: AceSectionBlock[];
537
577
  };
538
578
 
539
- export { type AceAddress, type AceClient, type AceClientOptions, type AceContentTypeHandle, type AceContentTypeSchema, type AceContentTypes, type AceDateFormat, type AceEntry, type AceFieldSchema, type AceImageAttributes, type AceImageData, type AceImageOptions, type AceLinkAttributes, type AceLinkData, type AceMenu, type AceMenuItem, type AceNavigationLink, type AcePage, AcePreviewExpiredError, type AceRichTextDocument, type AceRichTextMark, type AceRichTextNode, type AceSection, type AceSectionBlock, type AceSeoOptions, type AceSeoProperties, AceUnavailableError, type AceValuesOf, type BlockDefinition, type SectionDefinition, type SectionFieldDefinition, type SectionFieldType, type SectionOf, type SectionTypesPayload, type ValuesOfFields, addFramingPolicy, addressFromPath, checkSectionDefinition, createAceClient, defineSection, formatAceDate, framingPolicy, headingLevel, homeAddress, imageAttributes, isCurrentPage, isHomeAddress, isInternalPath, isRefreshRequest, linkAddress, linkAttributes, linksFromMenu, listStart, listenForPreviewRefresh, loadMenuLinks, loadPage, originOf, pageNotices, pathOfAddress, previewHeaders, previewParameter, previewTokenFrom, refreshMessageType, refreshPageContent, renderRichText, renderTextArea, richTextImageSizes, sectionTypesPayload, seoDescriptionHandle, seoProperties, seoTitleHandle, swapPageContent, visitablePath };
579
+ export { type AceAddress, type AceClient, type AceClientOptions, type AceContentTypeHandle, type AceContentTypeSchema, type AceContentTypes, type AceDateFormat, type AceEntry, type AceFavicon, type AceFieldSchema, type AceImageAttributes, type AceImageData, type AceImageOptions, type AceLinkAttributes, type AceLinkData, type AceMenu, type AceMenuItem, type AceNavigationLink, type AcePage, AcePreviewExpiredError, type AceRichTextDocument, type AceRichTextMark, type AceRichTextNode, type AceSection, type AceSectionBlock, type AceSeoOptions, type AceSeoProperties, type AceSite, AceUnavailableError, type AceValuesOf, type BlockDefinition, type SectionDefinition, type SectionFieldDefinition, type SectionFieldType, type SectionOf, type SectionTypesPayload, type ValuesOfFields, addFramingPolicy, addressFromPath, checkSectionDefinition, createAceClient, defineSection, formatAceDate, framingPolicy, headingLevel, homeAddress, imageAttributes, isCurrentPage, isHomeAddress, isInternalPath, isRefreshRequest, linkAddress, linkAttributes, linksFromMenu, listStart, listenForPreviewRefresh, loadMenuLinks, loadPage, originOf, pageNotices, pathOfAddress, previewHeaders, previewParameter, previewTokenFrom, refreshMessageType, refreshPageContent, renderRichText, renderTextArea, richTextImageSizes, sectionTypesPayload, seoDescriptionHandle, seoProperties, seoTitleHandle, swapPageContent, visitablePath };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { pageNotices } from './chunk-XPZY5R5A.js';
2
2
  export { headingLevel, imageAttributes, isInternalPath, linkAddress, linkAttributes, listStart, renderRichText, renderTextArea, richTextImageSizes } from './chunk-FVR55GIU.js';
3
3
  export { listenForPreviewRefresh, refreshPageContent } from './chunk-YN7LJWGI.js';
4
- export { AcePreviewExpiredError, AceUnavailableError, createAceClient, isCurrentPage, linksFromMenu, loadMenuLinks, loadPage } from './chunk-XYOW63V6.js';
4
+ export { AcePreviewExpiredError, AceUnavailableError, createAceClient, isCurrentPage, linksFromMenu, loadMenuLinks, loadPage } from './chunk-EKZRLWNO.js';
5
5
  export { addressFromPath, homeAddress, isHomeAddress, pathOfAddress, visitablePath } from './chunk-M5WR5TCR.js';
6
6
  export { addFramingPolicy, framingPolicy, isRefreshRequest, originOf, previewHeaders, previewParameter, previewTokenFrom, refreshMessageType, swapPageContent } from './chunk-YLO6IAXF.js';
7
7
 
@@ -160,7 +160,15 @@ function seoProperties(options) {
160
160
  ...description && { description },
161
161
  ...options.image && { image: options.image }
162
162
  },
163
- extend: { meta }
163
+ extend: {
164
+ meta,
165
+ ...options.favicon && {
166
+ link: [
167
+ { rel: "icon", href: options.favicon.icon, sizes: "48x48" },
168
+ { rel: "apple-touch-icon", href: options.favicon.apple_touch_icon }
169
+ ]
170
+ }
171
+ }
164
172
  };
165
173
  }
166
174
 
@@ -1367,6 +1367,10 @@ function createAceClient({
1367
1367
  getMenu(handle) {
1368
1368
  return get(`/public/menus/${encodeURIComponent(handle)}`);
1369
1369
  },
1370
+ /** The site itself: its name, and the social image its owners chose (null if none). */
1371
+ getSite() {
1372
+ return get("/public/site");
1373
+ },
1370
1374
  /** What the site's content types and fields are, which is what type generation reads. */
1371
1375
  async getContentTypes() {
1372
1376
  return await get("/public/content-types") ?? [];
@@ -35,6 +35,45 @@ type AceValuesOf<Handle extends string> = Handle extends keyof AceContentTypes
35
35
  ? AceContentTypes[Handle]
36
36
  : Record<string, unknown>;
37
37
 
38
+ /** An image as the CMS Ace public API returns it. Use `src` and `srcset` in an `<img>`. */
39
+ type AceImageData = {
40
+ id: number;
41
+ /** The untouched original. */
42
+ url: string;
43
+ /** A web-sized copy, for browsers that ignore `srcset`. */
44
+ src: string;
45
+ /** Copies at several widths; empty when the image is too small to need them. */
46
+ srcset: string;
47
+ width: number | null;
48
+ height: number | null;
49
+ alt_text: string;
50
+ content_type: string;
51
+ };
52
+
53
+ /** The site's favicon as the CMS Ace public API returns it: the original, and square PNG copies. */
54
+ type AceFavicon = {
55
+ id: number;
56
+ /** The untouched original. */
57
+ url: string;
58
+ /** A small square PNG (48 pixels), for the browser tab and search results. */
59
+ icon: string;
60
+ /** A square PNG (180 pixels), for a phone's home screen. */
61
+ apple_touch_icon: string;
62
+ content_type: string;
63
+ };
64
+
65
+ /** The site the request is for, as the CMS Ace public API returns it. */
66
+ type AceSite = {
67
+ id: number;
68
+ name: string;
69
+ domain: string;
70
+ handle: string;
71
+ /** The picture the site's owners chose to show in search results and social posts; null if none. */
72
+ social_image: AceImageData | null;
73
+ /** The small picture the site's owners chose for browser tabs; null if none. */
74
+ favicon: AceFavicon | null;
75
+ };
76
+
38
77
  /** One link of a menu as the public API returns it, with its address already worked out. */
39
78
  type AceMenuItem = {
40
79
  label: string;
@@ -107,6 +146,8 @@ declare function createAceClient({ apiUrl, siteDomain, cacheSeconds, fetchImplem
107
146
  }): Promise<AceEntry<AceValuesOf<Handle>>[]>;
108
147
  /** One of the site's navigation menus by its handle, with every link worked out; null if the site has none. */
109
148
  getMenu(handle: string): Promise<AceMenu | null>;
149
+ /** The site itself: its name, and the social image its owners chose (null if none). */
150
+ getSite(): Promise<AceSite | null>;
110
151
  /** What the site's content types and fields are, which is what type generation reads. */
111
152
  getContentTypes(): Promise<AceContentTypeSchema[]>;
112
153
  };
@@ -134,6 +175,8 @@ declare const getEntry: AceClient["getEntry"];
134
175
  declare const getEntries: AceClient["getEntries"];
135
176
  /** A navigation menu with every link worked out, or `null`. */
136
177
  declare const getMenu: (handle: string) => Promise<AceMenu | null>;
178
+ /** The site: its name and the social image its owners chose (`social_image`, or `null`). */
179
+ declare const getSite: () => Promise<AceSite | null>;
137
180
  /**
138
181
  * A menu's links, ready to draw. If the site has no such menu, or the CMS can't be reached, you get `fallback`
139
182
  * instead, so a missing menu never takes a page down.
@@ -223,4 +266,4 @@ declare const streamTimeout = 5000;
223
266
  */
224
267
  declare function handleRequest(request: Request, responseStatusCode: number, responseHeaders: Headers, routerContext: EntryContext, _loadContext: AppLoadContext): Promise<Response>;
225
268
 
226
- export { createPageLoader, createRootLoader, getAceClient, getEntries, getEntry, getMenu, getMenuLinks, handleRequest, headers, loader, streamTimeout };
269
+ export { createPageLoader, createRootLoader, getAceClient, getEntries, getEntry, getMenu, getMenuLinks, getSite, handleRequest, headers, loader, streamTimeout };
@@ -1,4 +1,4 @@
1
- import { createAceClient, loadMenuLinks, loadPage } from './chunk-XYOW63V6.js';
1
+ import { createAceClient, loadMenuLinks, loadPage } from './chunk-EKZRLWNO.js';
2
2
  import './chunk-M5WR5TCR.js';
3
3
  import { hostedAdminOrigin, hostedApiUrl } from './chunk-N5DIMG3X.js';
4
4
  import { previewTokenFrom, addFramingPolicy } from './chunk-YLO6IAXF.js';
@@ -32,6 +32,7 @@ function getAceClient() {
32
32
  var getEntry = ((...parameters) => getAceClient().getEntry(...parameters));
33
33
  var getEntries = ((...parameters) => getAceClient().getEntries(...parameters));
34
34
  var getMenu = (handle) => getAceClient().getMenu(handle);
35
+ var getSite = () => getAceClient().getSite();
35
36
  var getMenuLinks = (handle, options = {}) => loadMenuLinks(getAceClient(), handle, options.fallback ?? []);
36
37
  function createPageLoader() {
37
38
  return async function loader2({ request }) {
@@ -87,4 +88,4 @@ function handleRequest(request, responseStatusCode, responseHeaders, routerConte
87
88
  });
88
89
  }
89
90
 
90
- export { createPageLoader, createRootLoader, getAceClient, getEntries, getEntry, getMenu, getMenuLinks, handleRequest, headers, loader, streamTimeout };
91
+ export { createPageLoader, createRootLoader, getAceClient, getEntries, getEntry, getMenu, getMenuLinks, getSite, handleRequest, headers, loader, streamTimeout };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sandboxsonny/ace-kit",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Everything a website needs to use CMS Ace: a typed client, addressing, images, menus, rich text, live preview, and Astro and React adapters.",
5
5
  "keywords": [
6
6
  "cms",