@getquick/content 0.3.1 → 0.4.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/dist/server.js CHANGED
@@ -1,7 +1,9 @@
1
- import { _ as uriPath, a as siteLanguages, b as parseWordPressBlocks, c as parseDesignBlocks, d as decodeEntities, g as stripHtml, h as routeOf, i as routeLanguage, l as renderDesignPattern, n as isLanguageHome, p as menuItemHref, r as languageSubject, u as cmsTime, x as renderWordPressBlocks, y as hasVideoHero } from "./site-language-BLA2mmOw.js";
1
+ import { n as parseFragment } from "./dist-5jNKLmbn.js";
2
+ import { D as renderWordPressBlocks, E as parseWordPressBlocks, T as hasVideoHero, V as enhanceDesignNavigationHtml, Z as resolveDesignTheme, _ as uriPath, a as siteLanguages, c as parseDesignBlocks, d as decodeEntities, et as assertDesignBlocks, g as stripHtml, h as routeOf, i as routeLanguage, l as renderDesignPattern, n as isLanguageHome, nt as safeContentUrl, p as menuItemHref, r as languageSubject, tt as enhanceDesignCoverVideosHtml, u as cmsTime, w as hasDesignCarousel, x as preloadDesignQueryPosts } from "./site-language-BCGjkpWY.js";
3
+ import { d as parseFormDescriptors, o as findForm } from "./descriptor-x8kHFQ79.js";
2
4
  import { t as bearerMatches } from "./bearer-DMOCoSci.js";
3
5
  import { _ as consentConfiguration } from "./state-BYWc5TUB.js";
4
- import { i as rewriteEmbeds, l as parseLegalLanguage } from "./embeds-p9SyGNZI.js";
6
+ import { i as rewriteEmbeds, l as parseLegalLanguage } from "./embeds-CJJemAmE.js";
5
7
  //#region node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
6
8
  var util;
7
9
  (function(util) {
@@ -3816,6 +3818,11 @@ function publicationStore(db) {
3816
3818
  failedEvents: count(failed)
3817
3819
  };
3818
3820
  },
3821
+ async searchRows(home, chrome) {
3822
+ const { results } = await guard("be read", () => db.prepare(`SELECT key, state, format, body FROM publications
3823
+ WHERE key IN (?, ?) OR (substr(key, 1, 6) = 'entry:' AND state = 'published')`).bind(home, chrome).all());
3824
+ return results;
3825
+ },
3819
3826
  async listItems(filter, limit, offset) {
3820
3827
  const { where, values } = itemsWhere(filter);
3821
3828
  const { results } = await guard("be read", () => db.prepare(`SELECT key, body, modified_at, count(*) OVER () AS total FROM publications p
@@ -4767,6 +4774,48 @@ function configuredRefresh(languages, delivery) {
4767
4774
  return { handleRefresh };
4768
4775
  }
4769
4776
  //#endregion
4777
+ //#region frontend/src/design-blocks.ts
4778
+ /** Validate publication trees with the producer/renderer contract, preserving every node. */
4779
+ const designBlocksSchema = unknownType().transform((value, context) => {
4780
+ try {
4781
+ assertDesignBlocks(value);
4782
+ return value;
4783
+ } catch {
4784
+ context.addIssue({
4785
+ code: "custom",
4786
+ message: "Invalid canonical Design block list"
4787
+ });
4788
+ return NEVER;
4789
+ }
4790
+ });
4791
+ /** The nullable GraphQL field is absent on older CMS releases and block recovery reads. */
4792
+ const designBlocksJsonSchema = stringType().nullish().transform((value, context) => {
4793
+ if (value == null) return void 0;
4794
+ try {
4795
+ return JSON.parse(value);
4796
+ } catch {
4797
+ context.addIssue({
4798
+ code: "custom",
4799
+ message: "Invalid canonical Design block JSON"
4800
+ });
4801
+ return NEVER;
4802
+ }
4803
+ }).pipe(designBlocksSchema.optional());
4804
+ //#endregion
4805
+ //#region frontend/src/design-theme.ts
4806
+ /** Both CMS reads and stored publications use the owning theme validator. */
4807
+ const designThemeSchema = unknownType().transform((value, context) => {
4808
+ try {
4809
+ return resolveDesignTheme(value);
4810
+ } catch {
4811
+ context.addIssue({
4812
+ code: "custom",
4813
+ message: "Invalid resolved Design theme"
4814
+ });
4815
+ return NEVER;
4816
+ }
4817
+ });
4818
+ //#endregion
4770
4819
  //#region frontend/src/published-html.ts
4771
4820
  /** The one step every published body passes through: the identity unless configured. */
4772
4821
  function publishedHtml(languages, transform) {
@@ -4806,7 +4855,8 @@ function publishedHtml(languages, transform) {
4806
4855
  ...home,
4807
4856
  page: {
4808
4857
  ...home.page,
4809
- content: transform(home.page.content, served)
4858
+ content: transform(home.page.content, served),
4859
+ ...home.page.designBlocks ? { designBlocks: blocks(home.page.designBlocks, served) } : {}
4810
4860
  }
4811
4861
  },
4812
4862
  chrome: chrome(delivery.content.chrome, served)
@@ -4827,7 +4877,8 @@ function publishedHtml(languages, transform) {
4827
4877
  ...delivery.content,
4828
4878
  entry: {
4829
4879
  ...entry,
4830
- content: transform(entry.content, served)
4880
+ content: transform(entry.content, served),
4881
+ ...entry.designBlocks ? { designBlocks: blocks(entry.designBlocks, served) } : {}
4831
4882
  },
4832
4883
  chrome: chrome(delivery.content.chrome, served)
4833
4884
  }
@@ -4911,6 +4962,8 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
4911
4962
  }
4912
4963
  return parsed;
4913
4964
  }) } : {};
4965
+ /** Gravity Forms descriptors stored with an entry (a Site with `forms`); invalid ones are dropped. */
4966
+ const forms = arrayType(unknownType()).optional().transform((value) => value === void 0 ? void 0 : parseFormDescriptors(value));
4914
4967
  const homePublication = objectType({
4915
4968
  settings: objectType({
4916
4969
  title: stringType(),
@@ -4920,10 +4973,14 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
4920
4973
  title: stringType(),
4921
4974
  content: stringType(),
4922
4975
  hasVideoHero: booleanType(),
4976
+ databaseId: numberType().int().positive().optional(),
4977
+ designBlocks: designBlocksSchema.optional(),
4978
+ forms,
4923
4979
  ...extended
4924
4980
  }),
4925
4981
  spacingSizes,
4926
- colors
4982
+ colors,
4983
+ theme: designThemeSchema.optional()
4927
4984
  });
4928
4985
  const translation = objectType({
4929
4986
  uri: stringType(),
@@ -4957,8 +5014,12 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
4957
5014
  date: stringType(),
4958
5015
  featuredImage: image,
4959
5016
  hasVideoHero: booleanType(),
5017
+ databaseId: numberType().int().positive().optional(),
5018
+ designBlocks: designBlocksSchema.optional(),
5019
+ forms,
4960
5020
  spacingSizes,
4961
5021
  colors,
5022
+ theme: designThemeSchema.optional(),
4962
5023
  ...extended
4963
5024
  });
4964
5025
  const legalRole = objectType({
@@ -5001,7 +5062,8 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
5001
5062
  });
5002
5063
  const designPresets = objectType({
5003
5064
  spacingSizes,
5004
- colors
5065
+ colors,
5066
+ theme: designThemeSchema.optional()
5005
5067
  });
5006
5068
  const archives = recordType(stringType());
5007
5069
  const parseWith = (schema) => (body) => {
@@ -5051,7 +5113,8 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
5051
5113
  return design ? {
5052
5114
  ...content,
5053
5115
  spacingSizes: design.spacingSizes,
5054
- colors: design.colors
5116
+ colors: design.colors,
5117
+ theme: design.theme
5055
5118
  } : content;
5056
5119
  }
5057
5120
  /**
@@ -5943,6 +6006,89 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
5943
6006
  pages: Math.ceil(found.total / perPage)
5944
6007
  };
5945
6008
  }
6009
+ /** Search a single snapshot, validating before counting or paging it. Never reads WordPress. */
6010
+ async function search(query) {
6011
+ const subject = "site search";
6012
+ const language = query.language === void 0 ? defaultLanguage : languages.find((each) => each.slug === query.language);
6013
+ if (!language) throw new Error(`${query.language} isn't one of the Site's languages`);
6014
+ const page = searchWhole("page", query.page, 1);
6015
+ const perPage = Math.min(100, searchWhole("perPage", query.perPage, 12));
6016
+ if (typeof query.search !== "string") throw new Error("search must be text");
6017
+ const needle = searchText(query.search);
6018
+ const store = await listingStore(subject);
6019
+ if ("kind" in store) return store;
6020
+ let rows;
6021
+ try {
6022
+ rows = await store.searchRows(homeKey(language), chromeKey(language));
6023
+ } catch (error) {
6024
+ if (!(error instanceof StoreFailure)) throw error;
6025
+ return notServed(subject, "store", error.message);
6026
+ }
6027
+ const unreadable = () => notServed(subject, "not-ready", "Search requires readable publications and this language's home and chrome in the publication store");
6028
+ const home = rows.find((row) => row.key === homeKey(language));
6029
+ const chrome = rows.find((row) => row.key === chromeKey(language));
6030
+ if (!home || !chrome || home.format !== 1 || chrome.format !== 1) return unreadable();
6031
+ const parseBody = (body, parse) => {
6032
+ try {
6033
+ return body === null ? void 0 : parse(JSON.parse(body));
6034
+ } catch {
6035
+ return;
6036
+ }
6037
+ };
6038
+ if (chrome.state !== "published" || !parseBody(chrome.body, parseChrome)) return unreadable();
6039
+ if (![
6040
+ "published",
6041
+ "missing",
6042
+ "withdrawn"
6043
+ ].includes(home.state)) return unreadable();
6044
+ const matches = [];
6045
+ const add = (uri, title, excerpt, body, date) => {
6046
+ const plainTitle = searchPlainText(title);
6047
+ const plainExcerpt = searchPlainText(excerpt);
6048
+ const plainBody = searchPlainText(body);
6049
+ if (needle && searchText(`${plainTitle} ${plainExcerpt} ${plainBody}`).includes(needle)) matches.push({
6050
+ uri,
6051
+ title: plainTitle,
6052
+ excerpt: Array.from(plainExcerpt || plainBody).slice(0, 240).join(""),
6053
+ language: language.slug,
6054
+ date
6055
+ });
6056
+ };
6057
+ if (home.state === "published") {
6058
+ const content = parseBody(home.body, parseHome);
6059
+ if (!content) return unreadable();
6060
+ add(language.home, content.page.title, "", content.page.content, "");
6061
+ }
6062
+ for (const row of rows) {
6063
+ if (!row.key.startsWith("entry:")) continue;
6064
+ const route = row.key.slice(6);
6065
+ if (isLanguageHome$1(route) || routeLanguage$1(route).slug !== language.slug) continue;
6066
+ if (row.format !== 1) return unreadable();
6067
+ const entry = parseBody(row.body, parseEntry);
6068
+ if (!entry) return unreadable();
6069
+ if (!inLanguage(entry, language)) continue;
6070
+ if (entry.collection && !Object.hasOwn(collections, entry.collection.name)) continue;
6071
+ if (routeOf(entry.uri) !== route || !entry.uri.startsWith("/") || !safeContentUrl(entry.uri)) return unreadable();
6072
+ add(entry.uri, entry.title, entry.excerpt, entry.content, entry.date);
6073
+ }
6074
+ matches.sort((a, b) => a.date === b.date ? a.uri < b.uri ? -1 : a.uri > b.uri ? 1 : 0 : a.date > b.date ? -1 : 1);
6075
+ const total = matches.length;
6076
+ return {
6077
+ kind: "found",
6078
+ content: {
6079
+ items: matches.slice((page - 1) * perPage, page * perPage).map(({ date: _date, ...item }) => item),
6080
+ total,
6081
+ page,
6082
+ perPage,
6083
+ totalPages: Math.ceil(total / perPage)
6084
+ }
6085
+ };
6086
+ }
6087
+ function searchWhole(option, value, fallback) {
6088
+ const number = value === void 0 || value === "" ? fallback : Number(value);
6089
+ if (!Number.isSafeInteger(number) || number < 1 || number > Number.MAX_SAFE_INTEGER / 100) throw new Error(`${option} must be a positive safe whole number`);
6090
+ return number;
6091
+ }
5946
6092
  /** A listing's whole-number option (as a number, or a string of one, from a URL), else its default. */
5947
6093
  function whole(option, value, fallback) {
5948
6094
  if (value === void 0 || value === null || value === "") return fallback;
@@ -5982,7 +6128,7 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
5982
6128
  } catch {
5983
6129
  return notServed(subject, "store", "The publication store couldn't be loaded");
5984
6130
  }
5985
- if (!store) return notServed(subject, "not-ready", "this Frontend has no publication store bound (PUBLICATION_DB); collections are only listed from one");
6131
+ if (!store) return notServed(subject, "not-ready", "this Frontend has no publication store bound (PUBLICATION_DB); listings require one");
5986
6132
  return store;
5987
6133
  }
5988
6134
  /**
@@ -6044,6 +6190,7 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
6044
6190
  publishedEntry: (path, siteOrigin) => safely(async () => html.entry(await publishedEntry(path, siteOrigin), routeLanguage$1(routeOf(path))), () => notServed("the entry", "store", "The publication store couldn't be loaded")),
6045
6191
  pageChrome: (siteOrigin) => safely(async () => html.chrome(await pageChrome(siteOrigin)), () => null),
6046
6192
  designPresets: () => safely(() => pageDesign(), () => null),
6193
+ search,
6047
6194
  list,
6048
6195
  terms,
6049
6196
  archiveUri: (collection) => {
@@ -6056,6 +6203,21 @@ function configuredDelivery(config, languages, wordpress, transformHtml) {
6056
6203
  function searchText(text) {
6057
6204
  return text.normalize("NFD").replace(/\p{M}/gu, "").replace(/[\u2018\u2019\u201A\u201B\u2032]/g, "'").replace(/[\u201C\u201D\u201E\u2033]/g, "\"").toLowerCase().replace(/\s+/g, " ").trim();
6058
6205
  }
6206
+ const searchTextBoundary = /^(address|article|aside|blockquote|br|dd|div|dl|dt|figcaption|figure|footer|h[1-6]|header|hr|li|main|nav|ol|p|pre|section|table|td|th|tr|ul)$/;
6207
+ /** Visible text for search and escaped result rendering, excluding script/style contents. */
6208
+ function searchPlainText(value) {
6209
+ const text = (node) => {
6210
+ if (node.nodeName === "#text" && "value" in node) return node.value;
6211
+ if ("tagName" in node && [
6212
+ "script",
6213
+ "style",
6214
+ "template"
6215
+ ].includes(node.tagName)) return "";
6216
+ const children = "childNodes" in node ? node.childNodes.map(text).join("") : "";
6217
+ return "tagName" in node && searchTextBoundary.test(node.tagName) ? ` ${children} ` : children;
6218
+ };
6219
+ return text(parseFragment(value)).replace(/\s+/g, " ").trim();
6220
+ }
6059
6221
  //#endregion
6060
6222
  //#region frontend/src/wordpress.ts
6061
6223
  /** The origins a link into this Site may use: WordPress's, and the Frontend's when given. */
@@ -6081,12 +6243,20 @@ function configuredWordPress(config, languages) {
6081
6243
  const fields = extensionSelection(kind, collection);
6082
6244
  return fields ? `\n... @include(if: $withBlocks) {\n${fields}\n}` : "";
6083
6245
  };
6246
+ /**
6247
+ * The public descriptors of the Gravity Forms forms an entry references
6248
+ * (gq-content's CMS forms module), read with the blocks on a Site that
6249
+ * opts in to forms. Without the option the queries are unchanged.
6250
+ */
6251
+ const formsField = config.forms ? "\n getquickForms @include(if: $withBlocks)" : "";
6252
+ const formsJson = stringType().nullish().transform((value) => value == null ? void 0 : parseFormDescriptors(value));
6084
6253
  const image = objectType({
6085
6254
  sourceUrl: stringType(),
6086
6255
  altText: stringType().nullable()
6087
6256
  });
6088
6257
  const imageEdge = objectType({ node: image.nullable() }).nullable();
6089
6258
  const designTokens = objectType({
6259
+ theme: designThemeSchema.optional(),
6090
6260
  spacingSizes: arrayType(objectType({
6091
6261
  slug: stringType(),
6092
6262
  size: stringType()
@@ -6104,6 +6274,9 @@ function configuredWordPress(config, languages) {
6104
6274
  nodeByUri: unionType([objectType({
6105
6275
  __typename: literalType("Page"),
6106
6276
  id: stringType().nullable().optional(),
6277
+ databaseId: numberType().int().positive().optional(),
6278
+ getquickBlocksJson: designBlocksJsonSchema,
6279
+ getquickForms: formsJson.optional(),
6107
6280
  isFrontPage: booleanType(),
6108
6281
  title: stringType().nullable(),
6109
6282
  content: stringType().nullable(),
@@ -6113,6 +6286,9 @@ function configuredWordPress(config, languages) {
6113
6286
  });
6114
6287
  const pageEntry = objectType({
6115
6288
  id: stringType(),
6289
+ databaseId: numberType().int().positive().optional(),
6290
+ getquickBlocksJson: designBlocksJsonSchema,
6291
+ getquickForms: formsJson.optional(),
6116
6292
  title: stringType().nullable(),
6117
6293
  content: stringType().nullable(),
6118
6294
  uri: stringType(),
@@ -6146,6 +6322,9 @@ function configuredWordPress(config, languages) {
6146
6322
  });
6147
6323
  const nodeEntry = objectType({
6148
6324
  __typename: stringType(),
6325
+ databaseId: numberType().int().positive().optional(),
6326
+ getquickBlocksJson: designBlocksJsonSchema,
6327
+ getquickForms: formsJson.optional(),
6149
6328
  id: stringType(),
6150
6329
  uri: stringType().nullable(),
6151
6330
  status: stringType().nullable(),
@@ -6162,7 +6341,7 @@ function configuredWordPress(config, languages) {
6162
6341
  translations: translated.translations.optional()
6163
6342
  }).passthrough();
6164
6343
  const nodeEntryData = objectType({
6165
- node: unionType([nodeEntry, objectType({ __typename: stringType() })]).nullable(),
6344
+ node: unionType([nodeEntry, objectType({ __typename: stringType().refine((name) => name !== "Post" && name !== "Page" && !collectionOf(name)) })]).nullable(),
6166
6345
  designTokens
6167
6346
  });
6168
6347
  const translatedEntryData = objectType({
@@ -6249,14 +6428,17 @@ function configuredWordPress(config, languages) {
6249
6428
  __typename
6250
6429
  ... on Page {
6251
6430
  id
6431
+ databaseId
6432
+ getquickBlocksJson @include(if: $withBlocks)
6252
6433
  title
6253
6434
  content
6254
6435
  blocks(attributes: true, htmlContent: true, dynamicContent: true, postTemplate: false)
6255
6436
  @include(if: $withBlocks)
6256
- isFrontPage${extensionFields("page")}
6437
+ isFrontPage${formsField}${extensionFields("page")}
6257
6438
  }
6258
6439
  }
6259
6440
  designTokens {
6441
+ theme
6260
6442
  spacingSizes {
6261
6443
  slug
6262
6444
  size
@@ -6278,14 +6460,17 @@ function configuredWordPress(config, languages) {
6278
6460
  __typename
6279
6461
  ... on Page {
6280
6462
  id
6463
+ databaseId
6464
+ getquickBlocksJson @include(if: $withBlocks)
6281
6465
  title
6282
6466
  content
6283
6467
  blocks(attributes: true, htmlContent: true, dynamicContent: true, postTemplate: false)
6284
6468
  @include(if: $withBlocks)
6285
- isFrontPage${extensionFields("page")}
6469
+ isFrontPage${formsField}${extensionFields("page")}
6286
6470
  }
6287
6471
  }
6288
6472
  designTokens {
6473
+ theme
6289
6474
  spacingSizes {
6290
6475
  slug
6291
6476
  size
@@ -6354,6 +6539,7 @@ function configuredWordPress(config, languages) {
6354
6539
  return `
6355
6540
  query EntryByUri($uri: String!, $withBlocks: Boolean = true) {
6356
6541
  designTokens {
6542
+ theme
6357
6543
  colors {
6358
6544
  slug
6359
6545
  color
@@ -6365,6 +6551,8 @@ function configuredWordPress(config, languages) {
6365
6551
  }
6366
6552
  postBy(uri: $uri) {
6367
6553
  id
6554
+ databaseId
6555
+ getquickBlocksJson @include(if: $withBlocks)
6368
6556
  title
6369
6557
  excerpt
6370
6558
  content
@@ -6380,10 +6568,12 @@ function configuredWordPress(config, languages) {
6380
6568
  sourceUrl
6381
6569
  altText
6382
6570
  }
6383
- }${extensionFields("post")}
6571
+ }${formsField}${extensionFields("post")}
6384
6572
  }
6385
6573
  pageBy(uri: $uri) {
6386
6574
  id
6575
+ databaseId
6576
+ getquickBlocksJson @include(if: $withBlocks)
6387
6577
  title
6388
6578
  content
6389
6579
  blocks(attributes: true, htmlContent: true, dynamicContent: true, postTemplate: false)
@@ -6397,7 +6587,7 @@ function configuredWordPress(config, languages) {
6397
6587
  sourceUrl
6398
6588
  altText
6399
6589
  }
6400
- }${extensionFields("page")}
6590
+ }${formsField}${extensionFields("page")}
6401
6591
  }
6402
6592
  }
6403
6593
  `;
@@ -6427,6 +6617,7 @@ function configuredWordPress(config, languages) {
6427
6617
  return `
6428
6618
  query EntryByUri($uri: String!, $withBlocks: Boolean = true) {
6429
6619
  designTokens {
6620
+ theme
6430
6621
  colors {
6431
6622
  slug
6432
6623
  color
@@ -6446,7 +6637,7 @@ function configuredWordPress(config, languages) {
6446
6637
  isRestricted
6447
6638
  modifiedGmt
6448
6639
  blocks(attributes: true, htmlContent: true, dynamicContent: true, postTemplate: false)
6449
- @include(if: $withBlocks)
6640
+ @include(if: $withBlocks)${formsField}
6450
6641
  }
6451
6642
  ... on NodeWithTitle {
6452
6643
  title
@@ -6464,7 +6655,11 @@ function configuredWordPress(config, languages) {
6464
6655
  altText
6465
6656
  }
6466
6657
  }
6467
- }${typeFields("Post", `${languageFields}${extensionFields("post")}`)}${typeFields("Page", `${languageFields}${extensionFields("page")}`)}${itemFields}
6658
+ }${typeFields("Post", `
6659
+ databaseId
6660
+ getquickBlocksJson @include(if: $withBlocks)${languageFields}${extensionFields("post")}`)}${typeFields("Page", `
6661
+ databaseId
6662
+ getquickBlocksJson @include(if: $withBlocks)${languageFields}${extensionFields("page")}`)}${itemFields}
6468
6663
  }
6469
6664
  }
6470
6665
  `;
@@ -6474,6 +6669,7 @@ function configuredWordPress(config, languages) {
6474
6669
  const designQuery = `
6475
6670
  query DesignPresets {
6476
6671
  designTokens {
6672
+ theme
6477
6673
  spacingSizes {
6478
6674
  slug
6479
6675
  size
@@ -6522,6 +6718,9 @@ function configuredWordPress(config, languages) {
6522
6718
  function normalizePost(post) {
6523
6719
  return {
6524
6720
  id: post.id,
6721
+ ...post.databaseId !== void 0 ? { databaseId: post.databaseId } : {},
6722
+ ...post.getquickBlocksJson !== void 0 ? { designBlocks: post.getquickBlocksJson } : {},
6723
+ ...config.forms && post.getquickForms !== void 0 ? { forms: post.getquickForms } : {},
6525
6724
  title: post.title ?? "",
6526
6725
  excerpt: post.excerpt ?? "",
6527
6726
  content: post.content ?? "",
@@ -6534,9 +6733,11 @@ function configuredWordPress(config, languages) {
6534
6733
  /** A read WordPress didn't complete; becomes an Unavailable delivery. */
6535
6734
  class CmsFailureError extends Error {
6536
6735
  failure;
6537
- constructor(failure, options) {
6736
+ graphqlErrors;
6737
+ constructor(failure, options, graphqlErrors) {
6538
6738
  super(failure.message, options);
6539
6739
  this.failure = failure;
6740
+ this.graphqlErrors = graphqlErrors;
6540
6741
  }
6541
6742
  }
6542
6743
  function describeIssues(error) {
@@ -6639,7 +6840,7 @@ function configuredWordPress(config, languages) {
6639
6840
  if (result?.errors?.length) throw new CmsFailureError({
6640
6841
  reason: "graphql",
6641
6842
  message: safeMessage(result.errors.map((error) => error.message).join(", "))
6642
- });
6843
+ }, void 0, result.errors.map((error) => error.message));
6643
6844
  const parsed = schema.safeParse(result?.data);
6644
6845
  if (!parsed.success) throw new CmsFailureError({
6645
6846
  reason: "schema",
@@ -6650,6 +6851,9 @@ function configuredWordPress(config, languages) {
6650
6851
  function isServerError(error) {
6651
6852
  return error instanceof CmsFailureError && error.failure.reason === "http" && (error.failure.httpStatus ?? 0) >= 500;
6652
6853
  }
6854
+ /** Optional fields (getquickBlocksJson, getquickForms) this CMS has answered it lacks. */
6855
+ const absentFields = /* @__PURE__ */ new Set();
6856
+ const OPTIONAL_FIELD_ERROR = /^Cannot query field "(getquickBlocksJson|getquickForms)" on type "(?:Post|Page|ContentNode)"\.(?: Did you mean [^\n]+)?$/;
6653
6857
  /**
6654
6858
  * The CMS can fail on one page's blocks (a 502 for a block's attributes) while
6655
6859
  * the rest of it is fine: read it again without them rather than fail it. Only
@@ -6657,16 +6861,30 @@ function configuredWordPress(config, languages) {
6657
6861
  * too is a failure, never absence.
6658
6862
  */
6659
6863
  async function queryWithBlockRecovery(schema, queryText, variables = {}) {
6864
+ for (const field of absentFields) queryText = queryText.replaceAll(`${field} @include(if: $withBlocks)`, "");
6865
+ const read = async (readVariables) => {
6866
+ try {
6867
+ return await query(schema, queryText, readVariables);
6868
+ } catch (error) {
6869
+ if (!(error instanceof CmsFailureError) || error.failure.reason !== "graphql" || !error.graphqlErrors?.length || !error.graphqlErrors.every((message) => OPTIONAL_FIELD_ERROR.test(message))) throw error;
6870
+ for (const message of error.graphqlErrors) {
6871
+ const field = OPTIONAL_FIELD_ERROR.exec(message)[1];
6872
+ absentFields.add(field);
6873
+ queryText = queryText.replaceAll(`${field} @include(if: $withBlocks)`, "");
6874
+ }
6875
+ return query(schema, queryText, readVariables);
6876
+ }
6877
+ };
6660
6878
  try {
6661
6879
  return {
6662
- data: await query(schema, queryText, variables),
6880
+ data: await read(variables),
6663
6881
  blocksOmitted: false
6664
6882
  };
6665
6883
  } catch (error) {
6666
6884
  if (!isServerError(error)) throw error;
6667
6885
  try {
6668
6886
  return {
6669
- data: await query(schema, queryText, {
6887
+ data: await read({
6670
6888
  ...variables,
6671
6889
  withBlocks: false
6672
6890
  }),
@@ -6724,14 +6942,18 @@ function configuredWordPress(config, languages) {
6724
6942
  description: data.generalSettings.description ?? ""
6725
6943
  },
6726
6944
  page: {
6945
+ ...node.databaseId !== void 0 ? { databaseId: node.databaseId } : {},
6946
+ ...node.getquickBlocksJson !== void 0 ? { designBlocks: node.getquickBlocksJson } : {},
6947
+ ...config.forms && node.getquickForms !== void 0 ? { forms: node.getquickForms } : {},
6727
6948
  title: node.title ?? "",
6728
- content: containsVideoHero ? renderWordPressBlocks(blocks) : node.content ?? "",
6949
+ content: containsVideoHero || hasDesignCarousel(blocks) ? renderWordPressBlocks(blocks) : node.content ?? "",
6729
6950
  hasVideoHero: containsVideoHero,
6730
6951
  ...extended("page", node, blocksOmitted)
6731
6952
  },
6732
6953
  blocksOmitted,
6733
6954
  spacingSizes: data.designTokens.spacingSizes,
6734
- colors: data.designTokens.colors
6955
+ colors: data.designTokens.colors,
6956
+ ...data.designTokens.theme ? { theme: data.designTokens.theme } : {}
6735
6957
  }
6736
6958
  };
6737
6959
  });
@@ -6754,12 +6976,13 @@ function configuredWordPress(config, languages) {
6754
6976
  kind: "found",
6755
6977
  content: {
6756
6978
  ...normalized,
6757
- content: containsVideoHero ? renderWordPressBlocks(blocks) : normalized.content,
6979
+ content: containsVideoHero || hasDesignCarousel(blocks) ? renderWordPressBlocks(blocks) : normalized.content,
6758
6980
  hasVideoHero: containsVideoHero,
6759
6981
  blocksOmitted,
6760
6982
  modifiedAt: cmsTime(post.modifiedGmt),
6761
6983
  spacingSizes: data.designTokens.spacingSizes,
6762
6984
  colors: data.designTokens.colors,
6985
+ ...data.designTokens.theme ? { theme: data.designTokens.theme } : {},
6763
6986
  ...multilingual ? languageOf(post) : {},
6764
6987
  ...extended(data.postBy ? "post" : "page", post, blocksOmitted)
6765
6988
  }
@@ -6796,12 +7019,13 @@ function configuredWordPress(config, languages) {
6796
7019
  kind: "found",
6797
7020
  content: {
6798
7021
  ...normalized,
6799
- content: containsVideoHero ? renderWordPressBlocks(blocks) : normalized.content,
7022
+ content: containsVideoHero || hasDesignCarousel(blocks) ? renderWordPressBlocks(blocks) : normalized.content,
6800
7023
  hasVideoHero: containsVideoHero,
6801
7024
  blocksOmitted,
6802
7025
  modifiedAt: cmsTime(node.modifiedGmt),
6803
7026
  spacingSizes: data.designTokens.spacingSizes,
6804
7027
  colors: data.designTokens.colors,
7028
+ ...data.designTokens.theme ? { theme: data.designTokens.theme } : {},
6805
7029
  ...multilingual ? languageOf(entry) : {},
6806
7030
  ...item ? { collection: {
6807
7031
  name: item[0],
@@ -6837,7 +7061,8 @@ function configuredWordPress(config, languages) {
6837
7061
  kind: "found",
6838
7062
  content: {
6839
7063
  spacingSizes: designTokens.spacingSizes,
6840
- colors: designTokens.colors
7064
+ colors: designTokens.colors,
7065
+ ...designTokens.theme ? { theme: designTokens.theme } : {}
6841
7066
  }
6842
7067
  };
6843
7068
  });
@@ -7133,11 +7358,11 @@ function createContentRuntime(input) {
7133
7358
  new URL(config.graphqlEndpoint).hostname,
7134
7359
  ...consent.firstPartyHosts ?? []
7135
7360
  ] : [];
7136
- const delivery = configuredDelivery(config, languages, wordpress, embedConfiguration ? (html, language) => rewriteEmbeds(html, {
7361
+ const delivery = configuredDelivery(config, languages, wordpress, (html, language) => enhanceDesignNavigationHtml(enhanceDesignCoverVideosHtml(embedConfiguration ? rewriteEmbeds(html, {
7137
7362
  configuration: embedConfiguration,
7138
7363
  language: language.lang,
7139
7364
  firstPartyHosts
7140
- }).html : void 0);
7365
+ }).html : html)));
7141
7366
  const events = configuredEvents(config, languages, delivery, wordpress);
7142
7367
  const refresh = configuredRefresh(languages, delivery);
7143
7368
  const answer = (status, error, headers = {}) => Response.json({ error }, {
@@ -7174,6 +7399,25 @@ function createContentRuntime(input) {
7174
7399
  publishedHome: delivery.publishedHome,
7175
7400
  publishedEntry: delivery.publishedEntry,
7176
7401
  /**
7402
+ * A Gravity Forms form's descriptor as published with the page at a path
7403
+ * (the publication store's, as the page is served), for the form endpoint
7404
+ * (`createFormsServer`'s `descriptor`). Null when the page isn't served,
7405
+ * doesn't reference the form, or the Site doesn't opt in to `forms`.
7406
+ */
7407
+ publishedForm: async (path, formId, siteOrigin) => {
7408
+ if (!config.forms) return null;
7409
+ const pathname = new URL(path, "https://site.invalid").pathname;
7410
+ const route = isLanguageHome(`${pathname}/`, languages) ? `${pathname}/` : pathname;
7411
+ if (isLanguageHome(route, languages)) {
7412
+ const home = await delivery.publishedHome(siteOrigin, routeLanguage(route, languages));
7413
+ return home.kind === "found" ? findForm(home.content.home.page.forms, formId) : null;
7414
+ }
7415
+ const entry = await delivery.publishedEntry(route, siteOrigin);
7416
+ return entry.kind === "found" ? findForm(entry.content.entry.forms, formId) : null;
7417
+ },
7418
+ /** Site-wide text search over delivered public content; never reads WordPress. */
7419
+ search: delivery.search,
7420
+ /**
7177
7421
  * A page of a collection's items, from the publication store alone (never
7178
7422
  * WordPress), newest first: those with the given terms and text, in the
7179
7423
  * language. Unavailable when the store can't be read, or there is none.
@@ -7196,10 +7440,21 @@ function createContentRuntime(input) {
7196
7440
  * `renderDesignPattern`), with links into the CMS or `siteOrigin` served
7197
7441
  * as this Site's paths.
7198
7442
  */
7443
+ /** Preload selected standard Post/Page records through the existing store reader. */
7444
+ preloadDesignQueryPosts: (pattern, siteOrigin) => preloadDesignQueryPosts(pattern, async (item) => {
7445
+ if (item.type && item.type !== "post" && item.type !== "page") throw new Error(`Neutral query preload is not available for ${item.type}`);
7446
+ const delivered = await delivery.publishedEntry(item.uri, siteOrigin);
7447
+ return delivered.kind === "found" ? delivered.content.entry : null;
7448
+ }),
7199
7449
  renderDesignPattern: (pattern, options = {}) => {
7200
7450
  const { siteOrigin, internalOrigins: extraOrigins = [], ...rest } = options;
7201
7451
  return renderDesignPattern(pattern, {
7202
7452
  ...rest,
7453
+ mediaHtml: embedConfiguration ? (html) => rewriteEmbeds(html, {
7454
+ configuration: embedConfiguration,
7455
+ language: routeLanguage(rest.home ?? "/", languages).lang,
7456
+ firstPartyHosts
7457
+ }).html : rest.mediaHtml,
7203
7458
  internalOrigins: [...internalOrigins(config, siteOrigin), ...extraOrigins]
7204
7459
  });
7205
7460
  },