@zeropress/build-pages 0.5.6 → 0.6.1

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/action.js CHANGED
@@ -52242,7 +52242,7 @@ function invalidSlugValidationResult(value, code2) {
52242
52242
  }
52243
52243
 
52244
52244
  // node_modules/@zeropress/preview-data-validator/src/index.js
52245
- var PREVIEW_DATA_VERSION = "0.5";
52245
+ var PREVIEW_DATA_VERSION = "0.6";
52246
52246
  var PREVIEW_DOCUMENT_TYPES = ["plaintext", "markdown", "html"];
52247
52247
  var PREVIEW_MENU_ITEM_TYPES = ["custom", "page", "post", "category"];
52248
52248
  var PREVIEW_MENU_TARGETS = ["_self", "_blank"];
@@ -52251,9 +52251,16 @@ var PREVIEW_WIDGET_AREA_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
52251
52251
  var PREVIEW_COLLECTION_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
52252
52252
  var PREVIEW_COLLECTION_ITEM_TYPES = ["post", "page"];
52253
52253
  var PREVIEW_MEDIA_DELIVERY_MODES = ["none", "media_domain"];
52254
+ var PREVIEW_DATETIME_DISPLAY_MODES = ["static", "client"];
52255
+ var PREVIEW_DATETIME_STYLES = ["none", "short", "medium", "long", "full"];
52256
+ var PREVIEW_DISCOVERABILITY_VALUES = ["default", "noindex", "delist"];
52254
52257
  var PREVIEW_PERMALINK_OUTPUT_STYLES = ["directory", "html-extension"];
52255
52258
  var PREVIEW_PERMALINK_FIELDS = ["posts", "pages", "categories", "tags"];
52256
52259
  var PREVIEW_FRONT_PAGE_TYPES = ["theme_index", "page", "standalone_html"];
52260
+ var PREVIEW_DATA_KEY_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*(?:-[a-zA-Z0-9_]+)*$/;
52261
+ var PREVIEW_DATA_MAX_DEPTH = 4;
52262
+ var PREVIEW_DATA_MAX_KEYS = 64;
52263
+ var PREVIEW_DATA_MAX_ARRAY_LENGTH = 256;
52257
52264
  var PREVIEW_PERMALINK_TOKENS = Object.freeze({
52258
52265
  posts: /* @__PURE__ */ new Set(["slug", "public_id", "year", "month", "day"]),
52259
52266
  pages: /* @__PURE__ */ new Set(["slug"]),
@@ -52307,15 +52314,17 @@ function validateSite(site, path4, errors) {
52307
52314
  "title",
52308
52315
  "description",
52309
52316
  "url",
52310
- "mediaBaseUrl",
52311
- "mediaDeliveryMode",
52317
+ "media_base_url",
52318
+ "media_delivery_mode",
52312
52319
  "favicon",
52320
+ "expose_generator",
52313
52321
  "locale",
52314
- "postsPerPage",
52315
- "dateFormat",
52316
- "timeFormat",
52322
+ "posts_per_page",
52323
+ "datetime_display",
52324
+ "date_style",
52325
+ "time_style",
52317
52326
  "timezone",
52318
- "disallowComments",
52327
+ "disallow_comments",
52319
52328
  "indexing",
52320
52329
  "permalinks",
52321
52330
  "front_page",
@@ -52329,19 +52338,23 @@ function validateSite(site, path4, errors) {
52329
52338
  validateNonEmptyString(site.title, `${path4}.title`, "INVALID_SITE_TITLE", errors);
52330
52339
  validateString(site.description, `${path4}.description`, "INVALID_SITE_DESCRIPTION", errors);
52331
52340
  validateSiteUri(site.url, `${path4}.url`, "INVALID_SITE_URL", errors);
52332
- validateSiteUri(site.mediaBaseUrl, `${path4}.mediaBaseUrl`, "INVALID_SITE_MEDIA_BASE_URL", errors);
52333
- if (site.mediaDeliveryMode !== void 0) {
52334
- validateEnum(site.mediaDeliveryMode, `${path4}.mediaDeliveryMode`, "INVALID_SITE_MEDIA_DELIVERY_MODE", errors, PREVIEW_MEDIA_DELIVERY_MODES);
52341
+ validateSiteUri(site.media_base_url, `${path4}.media_base_url`, "INVALID_SITE_MEDIA_BASE_URL", errors);
52342
+ if (site.media_delivery_mode !== void 0) {
52343
+ validateEnum(site.media_delivery_mode, `${path4}.media_delivery_mode`, "INVALID_SITE_MEDIA_DELIVERY_MODE", errors, PREVIEW_MEDIA_DELIVERY_MODES);
52335
52344
  }
52336
52345
  if (site.favicon !== void 0) {
52337
52346
  validateSiteFavicon(site.favicon, `${path4}.favicon`, errors);
52338
52347
  }
52348
+ if (site.expose_generator !== void 0) {
52349
+ validateBoolean(site.expose_generator, `${path4}.expose_generator`, "INVALID_SITE_EXPOSE_GENERATOR", errors);
52350
+ }
52339
52351
  validateNonEmptyString(site.locale, `${path4}.locale`, "INVALID_SITE_LOCALE", errors);
52340
- validateInteger(site.postsPerPage, `${path4}.postsPerPage`, "INVALID_SITE_POSTS_PER_PAGE", errors, { minimum: 1 });
52341
- validateNonEmptyString(site.dateFormat, `${path4}.dateFormat`, "INVALID_SITE_DATE_FORMAT", errors);
52342
- validateString(site.timeFormat, `${path4}.timeFormat`, "INVALID_SITE_TIME_FORMAT", errors);
52352
+ validateInteger(site.posts_per_page, `${path4}.posts_per_page`, "INVALID_SITE_POSTS_PER_PAGE", errors, { minimum: 1 });
52353
+ validateEnum(site.datetime_display, `${path4}.datetime_display`, "INVALID_SITE_DATETIME_DISPLAY", errors, PREVIEW_DATETIME_DISPLAY_MODES);
52354
+ validateEnum(site.date_style, `${path4}.date_style`, "INVALID_SITE_DATE_STYLE", errors, PREVIEW_DATETIME_STYLES);
52355
+ validateEnum(site.time_style, `${path4}.time_style`, "INVALID_SITE_TIME_STYLE", errors, PREVIEW_DATETIME_STYLES);
52343
52356
  validateNonEmptyString(site.timezone, `${path4}.timezone`, "INVALID_SITE_TIMEZONE", errors);
52344
- validateBoolean(site.disallowComments, `${path4}.disallowComments`, "INVALID_SITE_DISALLOW_COMMENTS", errors);
52357
+ validateBoolean(site.disallow_comments, `${path4}.disallow_comments`, "INVALID_SITE_DISALLOW_COMMENTS", errors);
52345
52358
  if (site.indexing !== void 0) {
52346
52359
  validateBoolean(site.indexing, `${path4}.indexing`, "INVALID_SITE_INDEXING", errors);
52347
52360
  }
@@ -52357,7 +52370,6 @@ function validateSite(site, path4, errors) {
52357
52370
  "site_description",
52358
52371
  "site_url",
52359
52372
  "metadata",
52360
- "media_delivery_mode",
52361
52373
  "media_delivery_base_url",
52362
52374
  "language",
52363
52375
  "siteLocale",
@@ -52395,10 +52407,7 @@ function validateSiteFooter(footer, path4, errors) {
52395
52407
  validateNonEmptyString(footer.copyright_text, `${path4}.copyright_text`, "INVALID_SITE_FOOTER_COPYRIGHT_TEXT", errors);
52396
52408
  }
52397
52409
  if (footer.attribution !== void 0) {
52398
- validateClosedObject(footer.attribution, `${path4}.attribution`, errors, ["enabled"]);
52399
- if (isObject(footer.attribution) && footer.attribution.enabled !== void 0) {
52400
- validateBoolean(footer.attribution.enabled, `${path4}.attribution.enabled`, "INVALID_SITE_FOOTER_ATTRIBUTION_ENABLED", errors);
52401
- }
52410
+ validateBoolean(footer.attribution, `${path4}.attribution`, "INVALID_SITE_FOOTER_ATTRIBUTION", errors);
52402
52411
  }
52403
52412
  }
52404
52413
  function validateMenus(menus, path4, errors) {
@@ -52424,7 +52433,7 @@ function validatePreviewMenu(menu, path4, errors) {
52424
52433
  });
52425
52434
  }
52426
52435
  function validatePreviewMenuItem(item, path4, errors) {
52427
- validateClosedObject(item, path4, errors, ["title", "url", "type", "target", "children"]);
52436
+ validateClosedObject(item, path4, errors, ["title", "url", "type", "target", "meta", "children"]);
52428
52437
  if (!isObject(item)) {
52429
52438
  return;
52430
52439
  }
@@ -52432,6 +52441,7 @@ function validatePreviewMenuItem(item, path4, errors) {
52432
52441
  validateUrlLike(item.url, `${path4}.url`, "INVALID_MENU_ITEM_URL", errors);
52433
52442
  validateEnum(item.type, `${path4}.type`, "INVALID_MENU_ITEM_TYPE", errors, PREVIEW_MENU_ITEM_TYPES);
52434
52443
  validateEnum(item.target, `${path4}.target`, "INVALID_MENU_ITEM_TARGET", errors, PREVIEW_MENU_TARGETS);
52444
+ validatePreviewMeta(item.meta, `${path4}.meta`, errors);
52435
52445
  validateArray(item.children, `${path4}.children`, "INVALID_MENU_ITEM_CHILDREN", errors, (entry, index) => {
52436
52446
  validatePreviewMenuItem(entry, `${path4}.children[${index}]`, errors);
52437
52447
  });
@@ -52637,7 +52647,9 @@ function validatePreviewPost(post, path4, errors, authorIds) {
52637
52647
  "author_id",
52638
52648
  "featured_image",
52639
52649
  "meta",
52650
+ "data",
52640
52651
  "status",
52652
+ "discoverability",
52641
52653
  "allow_comments",
52642
52654
  "category_slugs",
52643
52655
  "tag_slugs"
@@ -52655,6 +52667,9 @@ function validatePreviewPost(post, path4, errors, authorIds) {
52655
52667
  validateDateTimeString(post.updated_at_iso, `${path4}.updated_at_iso`, "INVALID_POST_UPDATED_AT_ISO", errors);
52656
52668
  validateNonEmptyString(post.author_id, `${path4}.author_id`, "INVALID_POST_AUTHOR_ID", errors);
52657
52669
  validateEnum(post.status, `${path4}.status`, "INVALID_POST_STATUS", errors, ["published", "draft"]);
52670
+ if (post.discoverability !== void 0) {
52671
+ validateEnum(post.discoverability, `${path4}.discoverability`, "INVALID_POST_DISCOVERABILITY", errors, PREVIEW_DISCOVERABILITY_VALUES);
52672
+ }
52658
52673
  validateBoolean(post.allow_comments, `${path4}.allow_comments`, "INVALID_POST_ALLOW_COMMENTS", errors);
52659
52674
  validateSlugArray(post.category_slugs, `${path4}.category_slugs`, "INVALID_POST_CATEGORY_SLUGS", errors);
52660
52675
  validateSlugArray(post.tag_slugs, `${path4}.tag_slugs`, "INVALID_POST_TAG_SLUGS", errors);
@@ -52662,12 +52677,13 @@ function validatePreviewPost(post, path4, errors, authorIds) {
52662
52677
  validateUrlLike(post.featured_image, `${path4}.featured_image`, "INVALID_POST_FEATURED_IMAGE", errors);
52663
52678
  }
52664
52679
  validatePreviewMeta(post.meta, `${path4}.meta`, errors);
52680
+ validatePreviewStructuredData(post.data, `${path4}.data`, errors);
52665
52681
  if (typeof post.author_id === "string" && post.author_id.trim() !== "" && !authorIds.has(post.author_id)) {
52666
52682
  errors.push(issue("INVALID_POST_AUTHOR_REFERENCE", `${path4}.author_id`, "Referenced author_id does not exist"));
52667
52683
  }
52668
52684
  }
52669
52685
  function validatePreviewPage(page, path4, errors) {
52670
- validateClosedObject(page, path4, errors, ["title", "slug", "path", "content", "document_type", "excerpt", "featured_image", "meta", "status"]);
52686
+ validateClosedObject(page, path4, errors, ["title", "slug", "path", "content", "document_type", "excerpt", "featured_image", "meta", "data", "status", "discoverability"]);
52671
52687
  if (!isObject(page)) {
52672
52688
  return;
52673
52689
  }
@@ -52683,7 +52699,11 @@ function validatePreviewPage(page, path4, errors) {
52683
52699
  validateUrlLike(page.featured_image, `${path4}.featured_image`, "INVALID_PAGE_FEATURED_IMAGE", errors);
52684
52700
  }
52685
52701
  validatePreviewMeta(page.meta, `${path4}.meta`, errors);
52702
+ validatePreviewStructuredData(page.data, `${path4}.data`, errors);
52686
52703
  validateEnum(page.status, `${path4}.status`, "INVALID_PAGE_STATUS", errors, ["published", "draft"]);
52704
+ if (page.discoverability !== void 0) {
52705
+ validateEnum(page.discoverability, `${path4}.discoverability`, "INVALID_PAGE_DISCOVERABILITY", errors, PREVIEW_DISCOVERABILITY_VALUES);
52706
+ }
52687
52707
  }
52688
52708
  function validatePreviewMeta(meta, path4, errors) {
52689
52709
  if (meta === void 0) {
@@ -52700,6 +52720,66 @@ function validatePreviewMeta(meta, path4, errors) {
52700
52720
  errors.push(issue("INVALID_META_VALUE", `${path4}.${key}`, "meta values must be strings, numbers, booleans, or null"));
52701
52721
  }
52702
52722
  }
52723
+ function validatePreviewStructuredData(data, path4, errors) {
52724
+ if (data === void 0) {
52725
+ return;
52726
+ }
52727
+ if (!isObject(data)) {
52728
+ errors.push(issue("INVALID_DATA", path4, "data must be an object"));
52729
+ return;
52730
+ }
52731
+ validatePreviewDataObject(data, path4, errors, 0);
52732
+ }
52733
+ function validatePreviewDataValue(value, path4, errors, depth) {
52734
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
52735
+ return;
52736
+ }
52737
+ if (typeof value === "number") {
52738
+ if (!Number.isFinite(value)) {
52739
+ errors.push(issue("INVALID_DATA_VALUE", path4, "data numbers must be finite"));
52740
+ }
52741
+ return;
52742
+ }
52743
+ if (Array.isArray(value)) {
52744
+ validatePreviewDataArray(value, path4, errors, depth);
52745
+ return;
52746
+ }
52747
+ if (isObject(value)) {
52748
+ validatePreviewDataObject(value, path4, errors, depth);
52749
+ return;
52750
+ }
52751
+ errors.push(issue("INVALID_DATA_VALUE", path4, "data values must be JSON-safe strings, numbers, booleans, null, arrays, or objects"));
52752
+ }
52753
+ function validatePreviewDataObject(object, path4, errors, depth) {
52754
+ if (depth > PREVIEW_DATA_MAX_DEPTH) {
52755
+ errors.push(issue("INVALID_DATA_DEPTH", path4, `data nesting must not exceed ${PREVIEW_DATA_MAX_DEPTH} container levels`));
52756
+ return;
52757
+ }
52758
+ const entries = Object.entries(object);
52759
+ if (entries.length > PREVIEW_DATA_MAX_KEYS) {
52760
+ errors.push(issue("INVALID_DATA_OBJECT_SIZE", path4, `data objects must not contain more than ${PREVIEW_DATA_MAX_KEYS} keys`));
52761
+ }
52762
+ for (const [key, value] of entries) {
52763
+ const childPath = `${path4}.${key}`;
52764
+ if (!PREVIEW_DATA_KEY_PATTERN.test(key)) {
52765
+ errors.push(issue("INVALID_DATA_KEY", childPath, "data keys must be valid template path segments"));
52766
+ continue;
52767
+ }
52768
+ validatePreviewDataValue(value, childPath, errors, depth + 1);
52769
+ }
52770
+ }
52771
+ function validatePreviewDataArray(array, path4, errors, depth) {
52772
+ if (depth > PREVIEW_DATA_MAX_DEPTH) {
52773
+ errors.push(issue("INVALID_DATA_DEPTH", path4, `data nesting must not exceed ${PREVIEW_DATA_MAX_DEPTH} container levels`));
52774
+ return;
52775
+ }
52776
+ if (array.length > PREVIEW_DATA_MAX_ARRAY_LENGTH) {
52777
+ errors.push(issue("INVALID_DATA_ARRAY_SIZE", path4, `data arrays must not contain more than ${PREVIEW_DATA_MAX_ARRAY_LENGTH} items`));
52778
+ }
52779
+ array.forEach((value, index) => {
52780
+ validatePreviewDataValue(value, `${path4}[${index}]`, errors, depth + 1);
52781
+ });
52782
+ }
52703
52783
  function validatePermalinks(permalinks, path4, errors) {
52704
52784
  if (permalinks === void 0) {
52705
52785
  return;
@@ -52930,7 +53010,7 @@ function isOptionalKey(path4, key) {
52930
53010
  return key === "head_end" || key === "body_end";
52931
53011
  }
52932
53012
  if (path4 === "site") {
52933
- return key === "mediaDeliveryMode" || key === "favicon" || key === "indexing" || key === "permalinks" || key === "front_page" || key === "post_index" || key === "footer" || key === "meta";
53013
+ return key === "media_delivery_mode" || key === "favicon" || key === "expose_generator" || key === "indexing" || key === "permalinks" || key === "front_page" || key === "post_index" || key === "footer" || key === "meta";
52934
53014
  }
52935
53015
  if (path4 === "site.favicon") {
52936
53016
  return key === "icon" || key === "svg" || key === "png" || key === "apple_touch_icon";
@@ -52938,9 +53018,6 @@ function isOptionalKey(path4, key) {
52938
53018
  if (path4 === "site.footer") {
52939
53019
  return key === "copyright_text" || key === "attribution";
52940
53020
  }
52941
- if (path4 === "site.footer.attribution") {
52942
- return key === "enabled";
52943
- }
52944
53021
  if (path4 === "site.front_page") {
52945
53022
  return key === "page_slug" || key === "html";
52946
53023
  }
@@ -52959,6 +53036,9 @@ function isOptionalKey(path4, key) {
52959
53036
  if (path4.startsWith("content.media[")) {
52960
53037
  return key === "alt";
52961
53038
  }
53039
+ if (path4.startsWith("menus.") && (path4.includes(".items[") || path4.includes(".children["))) {
53040
+ return key === "meta";
53041
+ }
52962
53042
  if (path4.startsWith("widgets.") && path4.includes(".items[")) {
52963
53043
  return key === "settings";
52964
53044
  }
@@ -52966,10 +53046,10 @@ function isOptionalKey(path4, key) {
52966
53046
  return key === "title" || key === "description";
52967
53047
  }
52968
53048
  if (path4.startsWith("content.posts[")) {
52969
- return key === "id" || key === "featured_image" || key === "meta";
53049
+ return key === "id" || key === "featured_image" || key === "meta" || key === "data" || key === "discoverability";
52970
53050
  }
52971
53051
  if (path4.startsWith("content.pages[")) {
52972
- return key === "path" || key === "excerpt" || key === "featured_image" || key === "meta";
53052
+ return key === "path" || key === "excerpt" || key === "featured_image" || key === "meta" || key === "data" || key === "discoverability";
52973
53053
  }
52974
53054
  if (path4.startsWith("content.categories[") || path4.startsWith("content.tags[")) {
52975
53055
  return key === "description";
@@ -53014,7 +53094,7 @@ function mapSlugValidationMessage(issueCode) {
53014
53094
  function rejectLegacyKeys(value, path4, errors, keys, code2) {
53015
53095
  for (const key of keys) {
53016
53096
  if (key in value) {
53017
- errors.push(issue(code2, `${path4}.${key}`, "Legacy field is not allowed in preview-data v0.5"));
53097
+ errors.push(issue(code2, `${path4}.${key}`, "Legacy field is not allowed in preview-data v0.6"));
53018
53098
  }
53019
53099
  }
53020
53100
  }
@@ -53124,9 +53204,46 @@ var PARTIAL_TAG_REGEX = /\{\{(partial:[^}]+)\}\}/g;
53124
53204
  var TEMPLATE_PATH_SEGMENT_SOURCE = "[a-zA-Z_][a-zA-Z0-9_]*(?:-[a-zA-Z0-9_]+)*";
53125
53205
  var TEMPLATE_PATH_REGEX = new RegExp(`^${TEMPLATE_PATH_SEGMENT_SOURCE}(?:\\.${TEMPLATE_PATH_SEGMENT_SOURCE})*$`);
53126
53206
  var FOR_TAG_REGEX = new RegExp(`^#for ([a-zA-Z_][a-zA-Z0-9_]*) in (${TEMPLATE_PATH_SEGMENT_SOURCE}(?:\\.${TEMPLATE_PATH_SEGMENT_SOURCE})*)$`);
53127
- var IF_EQ_EXPRESSION_REGEX = new RegExp(`^(${TEMPLATE_PATH_SEGMENT_SOURCE}(?:\\.${TEMPLATE_PATH_SEGMENT_SOURCE})*)\\s+("(?:[^"\\\\]|\\\\.)*")$`);
53207
+ var NUMBER_LITERAL_REGEX = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
53208
+ var COMPARISON_BLOCK_TAGS = /* @__PURE__ */ new Set(["if_eq", "if_neq", "if_in", "if_starts_with"]);
53209
+ var COMPARISON_ELSE_IF_TAGS = /* @__PURE__ */ new Set(["else_if_eq", "else_if_neq", "else_if_in", "else_if_starts_with"]);
53210
+ var COMPARISON_TAG_OPERATORS = {
53211
+ if_eq: "eq",
53212
+ else_if_eq: "eq",
53213
+ if_neq: "neq",
53214
+ else_if_neq: "neq",
53215
+ if_in: "in",
53216
+ else_if_in: "in",
53217
+ if_starts_with: "starts_with",
53218
+ else_if_starts_with: "starts_with"
53219
+ };
53220
+ var PARTIAL_ARG_ROOT_PATHS = /* @__PURE__ */ new Set([
53221
+ "archive",
53222
+ "author",
53223
+ "category",
53224
+ "collection",
53225
+ "collections",
53226
+ "currentUrl",
53227
+ "language",
53228
+ "menu",
53229
+ "menus",
53230
+ "meta",
53231
+ "page",
53232
+ "pagination",
53233
+ "partial",
53234
+ "post",
53235
+ "posts",
53236
+ "route",
53237
+ "site",
53238
+ "tag",
53239
+ "taxonomies",
53240
+ "taxonomy",
53241
+ "widget",
53242
+ "widgets"
53243
+ ]);
53128
53244
  var PARTIAL_ARG_KEY_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
53129
53245
  var SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
53246
+ var LICENSE_REF_REGEX = /^LicenseRef-[A-Za-z0-9][A-Za-z0-9.-]*$/;
53130
53247
  var NAMESPACE_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
53131
53248
  var SLUG_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
53132
53249
  var ALLOWED_LICENSES = [
@@ -53137,7 +53254,10 @@ var ALLOWED_LICENSES = [
53137
53254
  "GPL-3.0-or-later"
53138
53255
  ];
53139
53256
  var LICENSES = new Set(ALLOWED_LICENSES);
53140
- var DEFAULT_RUNTIME = "0.5";
53257
+ var THEME_LINK_KEYS = /* @__PURE__ */ new Set(["homepage", "repository", "documentation", "support", "marketplace", "license"]);
53258
+ var THEME_LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:"]);
53259
+ var DEFAULT_RUNTIME = "0.6";
53260
+ var THEME_RUNTIME_V0_6 = DEFAULT_RUNTIME;
53141
53261
  var SUPPORTED_RUNTIMES = [DEFAULT_RUNTIME];
53142
53262
  var SUPPORTED_RUNTIME_SET = new Set(SUPPORTED_RUNTIMES);
53143
53263
  var NAMESPACE_MIN_LENGTH = 3;
@@ -53152,7 +53272,7 @@ var MENU_SLOT_ID_MAX_LENGTH = 32;
53152
53272
  var MENU_SLOT_COUNT_MAX = 12;
53153
53273
  var MENU_SLOT_TITLE_MAX_LENGTH = 80;
53154
53274
  var MENU_SLOT_DESCRIPTION_MAX_LENGTH = 160;
53155
- var SUPPORTED_THEME_FEATURES = /* @__PURE__ */ new Set(["comments", "newsletter", "postIndex"]);
53275
+ var SUPPORTED_THEME_FEATURES = /* @__PURE__ */ new Set(["comments", "newsletter", "post_index"]);
53156
53276
  var THEME_MANIFEST_KEYS = /* @__PURE__ */ new Set([
53157
53277
  "$schema",
53158
53278
  "name",
@@ -53164,11 +53284,12 @@ var THEME_MANIFEST_KEYS = /* @__PURE__ */ new Set([
53164
53284
  "author",
53165
53285
  "description",
53166
53286
  "thumbnail",
53287
+ "links",
53167
53288
  "features",
53168
- "menuSlots",
53169
- "widgetAreas",
53170
- "siteMeta",
53171
- "collectionSlots"
53289
+ "menu_slots",
53290
+ "widget_areas",
53291
+ "site_meta",
53292
+ "collection_slots"
53172
53293
  ]);
53173
53294
  var SITE_META_KEY_REGEX = /^[a-z][a-z0-9_]*(?:-[a-z0-9_]+)*$/;
53174
53295
  var SITE_META_KEY_MAX_LENGTH = 64;
@@ -53194,7 +53315,11 @@ function validateFeatureFlags(rawValue, errors) {
53194
53315
  "INVALID_THEME_FEATURE",
53195
53316
  `theme.json.features.${featureName}`,
53196
53317
  `Unknown theme feature '${featureName}'`,
53197
- "error"
53318
+ "error",
53319
+ {
53320
+ category: "theme_manifest",
53321
+ hint: themeFeatureHint(featureName)
53322
+ }
53198
53323
  ));
53199
53324
  continue;
53200
53325
  }
@@ -53211,6 +53336,80 @@ function validateFeatureFlags(rawValue, errors) {
53211
53336
  }
53212
53337
  return Object.keys(normalizedFeatures).length > 0 ? normalizedFeatures : void 0;
53213
53338
  }
53339
+ function themeFeatureHint(featureName) {
53340
+ const hints = {
53341
+ postIndex: "ZeroPress runtime v0.6 uses snake_case keys. Use 'post_index' instead of 'postIndex'."
53342
+ };
53343
+ return hints[featureName] || "";
53344
+ }
53345
+ function themeManifestFieldHint(fieldName) {
53346
+ const hints = {
53347
+ menuSlots: "ZeroPress runtime v0.6 uses snake_case keys. Use 'menu_slots' instead of 'menuSlots'.",
53348
+ widgetAreas: "ZeroPress runtime v0.6 uses snake_case keys. Use 'widget_areas' instead of 'widgetAreas'.",
53349
+ siteMeta: "ZeroPress runtime v0.6 uses snake_case keys. Use 'site_meta' instead of 'siteMeta'.",
53350
+ collectionSlots: "ZeroPress runtime v0.6 uses snake_case keys. Use 'collection_slots' instead of 'collectionSlots'.",
53351
+ settings: "theme.json.settings was removed in runtime v0.6. Use preview-data site.meta and theme.json site_meta hints for site-level values."
53352
+ };
53353
+ return hints[fieldName] || "";
53354
+ }
53355
+ function isAllowedLicense(value) {
53356
+ return LICENSES.has(value) || LICENSE_REF_REGEX.test(value);
53357
+ }
53358
+ function isValidThemeLinkUrl(value) {
53359
+ try {
53360
+ const parsed = new URL(value);
53361
+ return THEME_LINK_PROTOCOLS.has(parsed.protocol);
53362
+ } catch {
53363
+ return false;
53364
+ }
53365
+ }
53366
+ function validateThemeLinks(rawValue, errors) {
53367
+ if (rawValue === void 0) {
53368
+ return void 0;
53369
+ }
53370
+ if (!rawValue || typeof rawValue !== "object" || Array.isArray(rawValue)) {
53371
+ errors.push(issue2(
53372
+ "INVALID_LINKS",
53373
+ "theme.json.links",
53374
+ "theme.json field 'links' must be an object when present",
53375
+ "error"
53376
+ ));
53377
+ return void 0;
53378
+ }
53379
+ const normalizedLinks = {};
53380
+ for (const [linkKey, value] of Object.entries(rawValue)) {
53381
+ if (!THEME_LINK_KEYS.has(linkKey)) {
53382
+ errors.push(issue2(
53383
+ "INVALID_THEME_LINK",
53384
+ `theme.json.links.${linkKey}`,
53385
+ `Unknown theme link '${linkKey}'`,
53386
+ "error"
53387
+ ));
53388
+ continue;
53389
+ }
53390
+ if (typeof value !== "string" || value.trim() === "") {
53391
+ errors.push(issue2(
53392
+ "INVALID_THEME_LINK_VALUE",
53393
+ `theme.json.links.${linkKey}`,
53394
+ `theme link '${linkKey}' must be a non-empty URL string`,
53395
+ "error"
53396
+ ));
53397
+ continue;
53398
+ }
53399
+ const normalizedValue = value.trim();
53400
+ if (!isValidThemeLinkUrl(normalizedValue)) {
53401
+ errors.push(issue2(
53402
+ "INVALID_THEME_LINK_VALUE",
53403
+ `theme.json.links.${linkKey}`,
53404
+ `theme link '${linkKey}' must be an absolute http, https, or mailto URL`,
53405
+ "error"
53406
+ ));
53407
+ continue;
53408
+ }
53409
+ normalizedLinks[linkKey] = normalizedValue;
53410
+ }
53411
+ return Object.keys(normalizedLinks).length > 0 ? normalizedLinks : void 0;
53412
+ }
53214
53413
  function validateHelperMetadataMap(rawValue, fieldName, issueCodes, errors) {
53215
53414
  if (rawValue === void 0) {
53216
53415
  return void 0;
@@ -53312,7 +53511,7 @@ function validateSiteMetaMap(rawValue, errors) {
53312
53511
  errors.push(issue2(
53313
53512
  "INVALID_SITE_META",
53314
53513
  "theme.json",
53315
- "theme.json field 'siteMeta' must be an object when present",
53514
+ "theme.json field 'site_meta' must be an object when present",
53316
53515
  "error"
53317
53516
  ));
53318
53517
  return void 0;
@@ -53322,7 +53521,7 @@ function validateSiteMetaMap(rawValue, errors) {
53322
53521
  errors.push(issue2(
53323
53522
  "INVALID_SITE_META",
53324
53523
  "theme.json",
53325
- "theme.json field 'siteMeta' must not be empty",
53524
+ "theme.json field 'site_meta' must not be empty",
53326
53525
  "error"
53327
53526
  ));
53328
53527
  }
@@ -53330,7 +53529,7 @@ function validateSiteMetaMap(rawValue, errors) {
53330
53529
  errors.push(issue2(
53331
53530
  "INVALID_SITE_META",
53332
53531
  "theme.json",
53333
- `theme.json field 'siteMeta' must contain at most ${SITE_META_COUNT_MAX} keys`,
53532
+ `theme.json field 'site_meta' must contain at most ${SITE_META_COUNT_MAX} keys`,
53334
53533
  "error"
53335
53534
  ));
53336
53535
  }
@@ -53339,8 +53538,8 @@ function validateSiteMetaMap(rawValue, errors) {
53339
53538
  if (!SITE_META_KEY_REGEX.test(metaKey) || metaKey.length > SITE_META_KEY_MAX_LENGTH) {
53340
53539
  errors.push(issue2(
53341
53540
  "INVALID_SITE_META_KEY",
53342
- `theme.json.siteMeta.${metaKey}`,
53343
- `siteMeta key '${metaKey}' must start with a lowercase letter and use lowercase letters, digits, underscores, or internal hyphens only`,
53541
+ `theme.json.site_meta.${metaKey}`,
53542
+ `site_meta key '${metaKey}' must start with a lowercase letter and use lowercase letters, digits, underscores, or internal hyphens only`,
53344
53543
  "error"
53345
53544
  ));
53346
53545
  continue;
@@ -53348,8 +53547,8 @@ function validateSiteMetaMap(rawValue, errors) {
53348
53547
  if (!value || typeof value !== "object" || Array.isArray(value)) {
53349
53548
  errors.push(issue2(
53350
53549
  "INVALID_SITE_META_FIELD",
53351
- `theme.json.siteMeta.${metaKey}`,
53352
- `siteMeta key '${metaKey}' must be an object`,
53550
+ `theme.json.site_meta.${metaKey}`,
53551
+ `site_meta key '${metaKey}' must be an object`,
53353
53552
  "error"
53354
53553
  ));
53355
53554
  continue;
@@ -53359,8 +53558,8 @@ function validateSiteMetaMap(rawValue, errors) {
53359
53558
  if (!allowedKeys.has(key)) {
53360
53559
  errors.push(issue2(
53361
53560
  "INVALID_SITE_META_PROPERTY",
53362
- `theme.json.siteMeta.${metaKey}.${key}`,
53363
- `Unknown siteMeta property '${key}' in key '${metaKey}'`,
53561
+ `theme.json.site_meta.${metaKey}.${key}`,
53562
+ `Unknown site_meta property '${key}' in key '${metaKey}'`,
53364
53563
  "error"
53365
53564
  ));
53366
53565
  }
@@ -53368,31 +53567,31 @@ function validateSiteMetaMap(rawValue, errors) {
53368
53567
  if (typeof value.title !== "string" || value.title.trim() === "") {
53369
53568
  errors.push(issue2(
53370
53569
  "INVALID_SITE_META_TITLE",
53371
- `theme.json.siteMeta.${metaKey}.title`,
53372
- `siteMeta key '${metaKey}' must define a non-empty 'title'`,
53570
+ `theme.json.site_meta.${metaKey}.title`,
53571
+ `site_meta key '${metaKey}' must define a non-empty 'title'`,
53373
53572
  "error"
53374
53573
  ));
53375
53574
  } else if (value.title.trim().length > MENU_SLOT_TITLE_MAX_LENGTH) {
53376
53575
  errors.push(issue2(
53377
53576
  "INVALID_SITE_META_TITLE",
53378
- `theme.json.siteMeta.${metaKey}.title`,
53379
- `siteMeta key '${metaKey}' title must be at most ${MENU_SLOT_TITLE_MAX_LENGTH} characters`,
53577
+ `theme.json.site_meta.${metaKey}.title`,
53578
+ `site_meta key '${metaKey}' title must be at most ${MENU_SLOT_TITLE_MAX_LENGTH} characters`,
53380
53579
  "error"
53381
53580
  ));
53382
53581
  }
53383
53582
  if (value.description !== void 0 && (typeof value.description !== "string" || value.description.trim().length > MENU_SLOT_DESCRIPTION_MAX_LENGTH)) {
53384
53583
  errors.push(issue2(
53385
53584
  "INVALID_SITE_META_DESCRIPTION",
53386
- `theme.json.siteMeta.${metaKey}.description`,
53387
- `siteMeta key '${metaKey}' description must be a string at most ${MENU_SLOT_DESCRIPTION_MAX_LENGTH} characters`,
53585
+ `theme.json.site_meta.${metaKey}.description`,
53586
+ `site_meta key '${metaKey}' description must be a string at most ${MENU_SLOT_DESCRIPTION_MAX_LENGTH} characters`,
53388
53587
  "error"
53389
53588
  ));
53390
53589
  }
53391
53590
  if (value.type !== void 0 && !SITE_META_TYPES.has(value.type)) {
53392
53591
  errors.push(issue2(
53393
53592
  "INVALID_SITE_META_TYPE",
53394
- `theme.json.siteMeta.${metaKey}.type`,
53395
- `siteMeta key '${metaKey}' type must be one of: string, number, boolean`,
53593
+ `theme.json.site_meta.${metaKey}.type`,
53594
+ `site_meta key '${metaKey}' type must be one of: string, number, boolean`,
53396
53595
  "error"
53397
53596
  ));
53398
53597
  }
@@ -53401,15 +53600,15 @@ function validateSiteMetaMap(rawValue, errors) {
53401
53600
  if (value.default !== null && defaultType !== "string" && defaultType !== "number" && defaultType !== "boolean") {
53402
53601
  errors.push(issue2(
53403
53602
  "INVALID_SITE_META_DEFAULT",
53404
- `theme.json.siteMeta.${metaKey}.default`,
53405
- `siteMeta key '${metaKey}' default must be a string, number, boolean, or null`,
53603
+ `theme.json.site_meta.${metaKey}.default`,
53604
+ `site_meta key '${metaKey}' default must be a string, number, boolean, or null`,
53406
53605
  "error"
53407
53606
  ));
53408
53607
  } else if (typeof value.type === "string" && SITE_META_TYPES.has(value.type) && value.default !== null && defaultType !== value.type) {
53409
53608
  errors.push(issue2(
53410
53609
  "INVALID_SITE_META_DEFAULT",
53411
- `theme.json.siteMeta.${metaKey}.default`,
53412
- `siteMeta key '${metaKey}' default must match its declared type`,
53610
+ `theme.json.site_meta.${metaKey}.default`,
53611
+ `site_meta key '${metaKey}' default must match its declared type`,
53413
53612
  "error"
53414
53613
  ));
53415
53614
  }
@@ -53454,11 +53653,16 @@ async function validateThemeFiles(fileMap, options2 = {}) {
53454
53653
  manifest = manifestResult.manifest;
53455
53654
  errors.push(...manifestResult.errors);
53456
53655
  } catch (error) {
53656
+ const rawThemeJson = getText(files.get("theme.json"));
53457
53657
  errors.push(issue2(
53458
53658
  "INVALID_THEME_JSON",
53459
53659
  "theme.json",
53460
53660
  `Invalid theme.json: ${error instanceof Error ? error.message : String(error)}`,
53461
- "error"
53661
+ "error",
53662
+ {
53663
+ ...locationForJsonParseError(error, rawThemeJson),
53664
+ category: "json_syntax"
53665
+ }
53462
53666
  ));
53463
53667
  }
53464
53668
  }
@@ -53524,7 +53728,11 @@ function validateManifest(themeJson) {
53524
53728
  "UNKNOWN_THEME_MANIFEST_FIELD",
53525
53729
  `theme.json.${key}`,
53526
53730
  `Unknown theme.json field '${key}'`,
53527
- "error"
53731
+ "error",
53732
+ {
53733
+ category: "theme_manifest",
53734
+ hint: themeManifestFieldHint(key)
53735
+ }
53528
53736
  ));
53529
53737
  }
53530
53738
  }
@@ -53555,13 +53763,24 @@ function validateManifest(themeJson) {
53555
53763
  errors.push(issue2("INVALID_SEMVER", "theme.json", "Theme version must follow semantic versioning (e.g. 1.0.0)", "error"));
53556
53764
  }
53557
53765
  if (typeof themeJson.runtime === "string" && !SUPPORTED_RUNTIME_SET.has(themeJson.runtime.trim())) {
53558
- errors.push(issue2("INVALID_RUNTIME_VERSION", "theme.json", `theme.json field 'runtime' must be one of: ${SUPPORTED_RUNTIMES.join(", ")}`, "error"));
53766
+ errors.push(issue2(
53767
+ "INVALID_RUNTIME_VERSION",
53768
+ "theme.json",
53769
+ `theme.json field 'runtime' must be one of: ${SUPPORTED_RUNTIMES.join(", ")}`,
53770
+ "error",
53771
+ {
53772
+ category: "theme_manifest",
53773
+ hint: `Update theme.json:
53774
+
53775
+ "runtime": "${THEME_RUNTIME_V0_6}"`
53776
+ }
53777
+ ));
53559
53778
  }
53560
- if (typeof themeJson.license === "string" && !LICENSES.has(themeJson.license.trim())) {
53779
+ if (typeof themeJson.license === "string" && !isAllowedLicense(themeJson.license.trim())) {
53561
53780
  errors.push(issue2(
53562
53781
  "INVALID_LICENSE",
53563
53782
  "theme.json",
53564
- "theme.json field 'license' must be one of: MIT, Apache-2.0, BSD-3-Clause, GPL-3.0-only, GPL-3.0-or-later",
53783
+ "theme.json field 'license' must be one of: MIT, Apache-2.0, BSD-3-Clause, GPL-3.0-only, GPL-3.0-or-later, or a LicenseRef-* identifier",
53565
53784
  "error"
53566
53785
  ));
53567
53786
  }
@@ -53625,11 +53844,15 @@ function validateManifest(themeJson) {
53625
53844
  manifest.thumbnail = themeJson.thumbnail;
53626
53845
  }
53627
53846
  }
53847
+ const links = validateThemeLinks(themeJson.links, errors);
53848
+ if (links) {
53849
+ manifest.links = links;
53850
+ }
53628
53851
  const features = validateFeatureFlags(themeJson.features, errors);
53629
53852
  if (features) {
53630
53853
  manifest.features = features;
53631
53854
  }
53632
- const menuSlots = validateHelperMetadataMap(themeJson.menuSlots, "menuSlots", {
53855
+ const menu_slots = validateHelperMetadataMap(themeJson.menu_slots, "menu_slots", {
53633
53856
  itemLabel: "Menu slot",
53634
53857
  propertyLabel: "menu slot property",
53635
53858
  collectionLabel: "slots",
@@ -53640,10 +53863,10 @@ function validateManifest(themeJson) {
53640
53863
  invalidTitleCode: "INVALID_MENU_SLOT_TITLE",
53641
53864
  invalidDescriptionCode: "INVALID_MENU_SLOT_DESCRIPTION"
53642
53865
  }, errors);
53643
- if (menuSlots) {
53644
- manifest.menuSlots = menuSlots;
53866
+ if (menu_slots) {
53867
+ manifest.menu_slots = menu_slots;
53645
53868
  }
53646
- const widgetAreas = validateHelperMetadataMap(themeJson.widgetAreas, "widgetAreas", {
53869
+ const widget_areas = validateHelperMetadataMap(themeJson.widget_areas, "widget_areas", {
53647
53870
  itemLabel: "Widget area",
53648
53871
  propertyLabel: "widget area property",
53649
53872
  collectionLabel: "areas",
@@ -53654,14 +53877,14 @@ function validateManifest(themeJson) {
53654
53877
  invalidTitleCode: "INVALID_WIDGET_AREA_TITLE",
53655
53878
  invalidDescriptionCode: "INVALID_WIDGET_AREA_DESCRIPTION"
53656
53879
  }, errors);
53657
- if (widgetAreas) {
53658
- manifest.widgetAreas = widgetAreas;
53880
+ if (widget_areas) {
53881
+ manifest.widget_areas = widget_areas;
53659
53882
  }
53660
- const siteMeta = validateSiteMetaMap(themeJson.siteMeta, errors);
53661
- if (siteMeta) {
53662
- manifest.siteMeta = siteMeta;
53883
+ const site_meta = validateSiteMetaMap(themeJson.site_meta, errors);
53884
+ if (site_meta) {
53885
+ manifest.site_meta = site_meta;
53663
53886
  }
53664
- const collectionSlots = validateHelperMetadataMap(themeJson.collectionSlots, "collectionSlots", {
53887
+ const collection_slots = validateHelperMetadataMap(themeJson.collection_slots, "collection_slots", {
53665
53888
  itemLabel: "Collection slot",
53666
53889
  propertyLabel: "collection slot property",
53667
53890
  collectionLabel: "slots",
@@ -53672,8 +53895,8 @@ function validateManifest(themeJson) {
53672
53895
  invalidTitleCode: "INVALID_COLLECTION_SLOT_TITLE",
53673
53896
  invalidDescriptionCode: "INVALID_COLLECTION_SLOT_DESCRIPTION"
53674
53897
  }, errors);
53675
- if (collectionSlots) {
53676
- manifest.collectionSlots = collectionSlots;
53898
+ if (collection_slots) {
53899
+ manifest.collection_slots = collection_slots;
53677
53900
  }
53678
53901
  return { errors, manifest: errors.length > 0 ? void 0 : manifest };
53679
53902
  }
@@ -53685,8 +53908,20 @@ function validateTemplateSyntax(templatePath, content, context) {
53685
53908
  if (contentSlotMatches.length !== 1) {
53686
53909
  errors.push(issue2("INVALID_LAYOUT_SLOT", "layout.html", "layout.html must contain exactly one {{slot:content}}", "error"));
53687
53910
  }
53688
- if (/<script\b/i.test(content)) {
53689
- errors.push(issue2("LAYOUT_SCRIPT_NOT_ALLOWED", "layout.html", "layout.html must not contain <script> tags", "error"));
53911
+ const scriptMatch = /<script\b/i.exec(content);
53912
+ if (scriptMatch) {
53913
+ errors.push(issue2(
53914
+ "LAYOUT_SCRIPT_NOT_ALLOWED",
53915
+ "layout.html",
53916
+ "layout.html must not contain <script> tags",
53917
+ "error",
53918
+ {
53919
+ ...locationForIndex(content, scriptMatch.index),
53920
+ category: "theme_validation",
53921
+ hint: "Move shared scripts into a partial such as {{partial:content-enhancements}}, then include that partial from layout.html.",
53922
+ snippet: snippetForIndex(content, scriptMatch.index)
53923
+ }
53924
+ ));
53690
53925
  }
53691
53926
  }
53692
53927
  let match2;
@@ -53737,7 +53972,9 @@ function validateRuntimeV05TemplateSyntax(templatePath, content, errors) {
53737
53972
  index = end + 2;
53738
53973
  if (token.startsWith("partial:")) {
53739
53974
  try {
53740
- parsePartialReferenceToken(token);
53975
+ parsePartialReferenceToken(token, {
53976
+ allowedSingleSegmentPaths: getPartialArgScope(stack)
53977
+ });
53741
53978
  } catch (error) {
53742
53979
  errors.push(issue2(
53743
53980
  "INVALID_PARTIAL_REFERENCE",
@@ -53762,7 +53999,7 @@ function validateRuntimeV05TemplateSyntax(templatePath, content, errors) {
53762
53999
  }
53763
54000
  if (token === "#else") {
53764
54001
  const current = stack[stack.length - 1];
53765
- if (!current || current.tag !== "if" && current.tag !== "if_eq") {
54002
+ if (!current || current.tag !== "if" && !COMPARISON_BLOCK_TAGS.has(current.tag)) {
53766
54003
  errors.push(issue2("UNEXPECTED_TEMPLATE_ELSE", templatePath, `Unexpected {{#else}} in ${templatePath}`, "error"));
53767
54004
  return;
53768
54005
  }
@@ -53773,9 +54010,11 @@ function validateRuntimeV05TemplateSyntax(templatePath, content, errors) {
53773
54010
  current.hasElse = true;
53774
54011
  continue;
53775
54012
  }
53776
- if (token.startsWith("#else_if_eq ")) {
54013
+ const comparisonElseIfTag = getComparisonElseIfTag(token);
54014
+ if (comparisonElseIfTag) {
53777
54015
  const current = stack[stack.length - 1];
53778
- if (!current || current.tag !== "if_eq") {
54016
+ const expectedBlockTag = comparisonElseIfTag.replace(/^else_/, "");
54017
+ if (!current || current.tag !== expectedBlockTag) {
53779
54018
  errors.push(issue2("UNEXPECTED_TEMPLATE_ELSE_IF", templatePath, `Unexpected {{${token}}} in ${templatePath}`, "error"));
53780
54019
  return;
53781
54020
  }
@@ -53783,13 +54022,13 @@ function validateRuntimeV05TemplateSyntax(templatePath, content, errors) {
53783
54022
  errors.push(issue2("INVALID_TEMPLATE_BRANCH_ORDER", templatePath, `{{${token}}} cannot appear after {{#else}} in ${templatePath}`, "error"));
53784
54023
  return;
53785
54024
  }
53786
- const expression = token.slice("#else_if_eq ".length).trim();
53787
- const parsed = parseIfEqExpression(expression);
54025
+ const expression = token.slice(`#${comparisonElseIfTag} `.length).trim();
54026
+ const parsed = parseComparisonExpression(expression, comparisonElseIfTag);
53788
54027
  if (!parsed) {
53789
54028
  errors.push(issue2("UNSUPPORTED_TEMPLATE_TAG", templatePath, `Unsupported template tag '{{${token}}}' in ${templatePath}`, "error"));
53790
54029
  return;
53791
54030
  }
53792
- validateReservedPathUsage(parsed.path, templatePath, errors, stack, { isPartialFile });
54031
+ validateComparisonPathUsage(parsed, templatePath, errors, stack, { isPartialFile });
53793
54032
  continue;
53794
54033
  }
53795
54034
  if (token.startsWith("#else_if ")) {
@@ -53812,7 +54051,7 @@ function validateRuntimeV05TemplateSyntax(templatePath, content, errors) {
53812
54051
  }
53813
54052
  if (token.startsWith("/")) {
53814
54053
  const closingTag = token.slice(1).trim();
53815
- if (!["if", "if_eq", "for"].includes(closingTag)) {
54054
+ if (!["if", "for", ...COMPARISON_BLOCK_TAGS].includes(closingTag)) {
53816
54055
  errors.push(issue2("UNSUPPORTED_TEMPLATE_TAG", templatePath, `Unsupported template closing tag '{{${token}}}' in ${templatePath}`, "error"));
53817
54056
  return;
53818
54057
  }
@@ -53833,22 +54072,23 @@ function validateRuntimeV05TemplateSyntax(templatePath, content, errors) {
53833
54072
  stack.push({ tag: "if", hasElse: false });
53834
54073
  continue;
53835
54074
  }
53836
- if (token.startsWith("#if_eq ")) {
53837
- const expression = token.slice("#if_eq ".length).trim();
53838
- const parsed = parseIfEqExpression(expression);
54075
+ const comparisonBlockTag = getComparisonBlockTag(token);
54076
+ if (comparisonBlockTag) {
54077
+ const expression = token.slice(`#${comparisonBlockTag} `.length).trim();
54078
+ const parsed = parseComparisonExpression(expression, comparisonBlockTag);
53839
54079
  if (!parsed) {
53840
54080
  errors.push(issue2("UNSUPPORTED_TEMPLATE_TAG", templatePath, `Unsupported template tag '{{${token}}}' in ${templatePath}`, "error"));
53841
54081
  return;
53842
54082
  }
53843
- validateReservedPathUsage(parsed.path, templatePath, errors, stack, { isPartialFile });
53844
- stack.push({ tag: "if_eq", hasElse: false });
54083
+ validateComparisonPathUsage(parsed, templatePath, errors, stack, { isPartialFile });
54084
+ stack.push({ tag: comparisonBlockTag, hasElse: false });
53845
54085
  continue;
53846
54086
  }
53847
54087
  const forMatch = FOR_TAG_REGEX.exec(token);
53848
54088
  if (forMatch) {
53849
54089
  const path4 = forMatch[2];
53850
54090
  validateReservedPathUsage(path4, templatePath, errors, stack, { isPartialFile });
53851
- stack.push({ tag: "for", hasElse: false });
54091
+ stack.push({ tag: "for", itemName: forMatch[1], hasElse: false });
53852
54092
  continue;
53853
54093
  }
53854
54094
  errors.push(issue2("UNSUPPORTED_TEMPLATE_TAG", templatePath, `Unsupported template tag '{{${token}}}' in ${templatePath}`, "error"));
@@ -53881,16 +54121,124 @@ function validateReservedPathUsage(path4, templatePath, errors, stack, options2
53881
54121
  ));
53882
54122
  }
53883
54123
  }
53884
- function parseIfEqExpression(expression) {
53885
- const match2 = IF_EQ_EXPRESSION_REGEX.exec(expression);
53886
- if (!match2) {
54124
+ function validateComparisonPathUsage(parsed, templatePath, errors, stack, options2) {
54125
+ for (const operand of [parsed.left, ...parsed.operands]) {
54126
+ if (operand.kind === "path") {
54127
+ validateReservedPathUsage(operand.path, templatePath, errors, stack, options2);
54128
+ }
54129
+ }
54130
+ }
54131
+ function getComparisonBlockTag(token) {
54132
+ for (const tagName of COMPARISON_BLOCK_TAGS) {
54133
+ if (token.startsWith(`#${tagName} `)) {
54134
+ return tagName;
54135
+ }
54136
+ }
54137
+ return "";
54138
+ }
54139
+ function getComparisonElseIfTag(token) {
54140
+ for (const tagName of COMPARISON_ELSE_IF_TAGS) {
54141
+ if (token.startsWith(`#${tagName} `)) {
54142
+ return tagName;
54143
+ }
54144
+ }
54145
+ return "";
54146
+ }
54147
+ function parseComparisonExpression(expression, tagName) {
54148
+ const operator = COMPARISON_TAG_OPERATORS[tagName];
54149
+ let tokens;
54150
+ try {
54151
+ tokens = tokenizeExpression(expression);
54152
+ } catch {
54153
+ return null;
54154
+ }
54155
+ if (!operator || operator === "in" && tokens.length < 2 || operator !== "in" && tokens.length !== 2) {
54156
+ return null;
54157
+ }
54158
+ const left = parsePathOperand(tokens[0]);
54159
+ if (!left) {
53887
54160
  return null;
53888
54161
  }
54162
+ const operands = [];
54163
+ for (const token of tokens.slice(1)) {
54164
+ const operand = parseComparisonOperand(token);
54165
+ if (!operand) {
54166
+ return null;
54167
+ }
54168
+ operands.push(operand);
54169
+ }
53889
54170
  return {
53890
- path: match2[1],
53891
- literal: match2[2]
54171
+ operator,
54172
+ left,
54173
+ operands
53892
54174
  };
53893
54175
  }
54176
+ function tokenizeExpression(expression) {
54177
+ const tokens = [];
54178
+ let index = 0;
54179
+ while (index < expression.length) {
54180
+ while (/\s/.test(expression[index] || "")) {
54181
+ index += 1;
54182
+ }
54183
+ if (index >= expression.length) {
54184
+ break;
54185
+ }
54186
+ if (expression[index] === '"') {
54187
+ const start2 = index;
54188
+ index += 1;
54189
+ let escaped = false;
54190
+ while (index < expression.length) {
54191
+ const char = expression[index];
54192
+ if (escaped) {
54193
+ escaped = false;
54194
+ } else if (char === "\\") {
54195
+ escaped = true;
54196
+ } else if (char === '"') {
54197
+ index += 1;
54198
+ tokens.push(expression.slice(start2, index));
54199
+ break;
54200
+ }
54201
+ index += 1;
54202
+ }
54203
+ if (tokens[tokens.length - 1] !== expression.slice(start2, index)) {
54204
+ throw new Error(`Unclosed string literal in expression: ${expression}`);
54205
+ }
54206
+ continue;
54207
+ }
54208
+ const start = index;
54209
+ while (index < expression.length && !/\s/.test(expression[index])) {
54210
+ index += 1;
54211
+ }
54212
+ tokens.push(expression.slice(start, index));
54213
+ }
54214
+ return tokens;
54215
+ }
54216
+ function parsePathOperand(token) {
54217
+ return TEMPLATE_PATH_REGEX.test(token) ? { kind: "path", path: token } : null;
54218
+ }
54219
+ function parseComparisonOperand(token) {
54220
+ if (token.startsWith('"')) {
54221
+ try {
54222
+ const value = JSON.parse(token);
54223
+ return typeof value === "string" ? { kind: "literal", value } : null;
54224
+ } catch {
54225
+ return null;
54226
+ }
54227
+ }
54228
+ if (token === "true") {
54229
+ return { kind: "literal", value: true };
54230
+ }
54231
+ if (token === "false") {
54232
+ return { kind: "literal", value: false };
54233
+ }
54234
+ if (token === "null") {
54235
+ return { kind: "literal", value: null };
54236
+ }
54237
+ if (NUMBER_LITERAL_REGEX.test(token)) {
54238
+ return { kind: "literal", value: Number(token) };
54239
+ }
54240
+ return parsePathOperand(token);
54241
+ }
53894
54242
  function validatePartialReferences(templateContents, partialContents, context) {
53895
54243
  const { errors } = context;
53896
54244
  for (const [templatePath, content] of templateContents.entries()) {
@@ -53956,7 +54304,7 @@ function getReferencedPartialNames(content) {
53956
54304
  let match2;
53957
54305
  while ((match2 = PARTIAL_TAG_REGEX.exec(content)) !== null) {
53958
54306
  try {
53959
- const { name } = parsePartialReferenceToken(match2[1]);
54307
+ const { name } = parsePartialReferenceToken(match2[1], { validateArgs: false });
53960
54308
  matches.add(name);
53961
54309
  } catch {
53962
54310
  }
@@ -53964,7 +54312,7 @@ function getReferencedPartialNames(content) {
53964
54312
  PARTIAL_TAG_REGEX.lastIndex = 0;
53965
54313
  return matches;
53966
54314
  }
53967
- function parsePartialReferenceToken(token) {
54315
+ function parsePartialReferenceToken(token, options2 = {}) {
53968
54316
  const source = String(token || "").trim();
53969
54317
  if (!source.startsWith("partial:")) {
53970
54318
  throw new Error("Partial token must start with partial:");
@@ -53979,10 +54327,12 @@ function parsePartialReferenceToken(token) {
53979
54327
  throw new Error(`Invalid partial name '${name}'`);
53980
54328
  }
53981
54329
  const argsSource = expression.slice(name.length).trim();
53982
- parsePartialArgs(argsSource);
54330
+ if (options2.validateArgs !== false) {
54331
+ parsePartialArgs(argsSource, options2);
54332
+ }
53983
54333
  return { name };
53984
54334
  }
53985
- function parsePartialArgs(source) {
54335
+ function parsePartialArgs(source, options2 = {}) {
53986
54336
  if (!source) {
53987
54337
  return {};
53988
54338
  }
@@ -54034,7 +54384,8 @@ function parsePartialArgs(source) {
54034
54384
  if (cursor >= source.length || source[cursor] !== '"') {
54035
54385
  throw new Error(`Unclosed string literal for partial argument '${key}'`);
54036
54386
  }
54037
- args[key] = JSON.parse(source.slice(index, cursor + 1));
54387
+ JSON.parse(source.slice(index, cursor + 1));
54388
+ args[key] = true;
54038
54389
  index = cursor + 1;
54039
54390
  continue;
54040
54391
  }
@@ -54044,10 +54395,25 @@ function parsePartialArgs(source) {
54044
54395
  continue;
54045
54396
  }
54046
54397
  if (source.startsWith("false", index) && isValueBoundary(source, index + 5)) {
54047
- args[key] = false;
54398
+ args[key] = true;
54048
54399
  index += 5;
54049
54400
  continue;
54050
54401
  }
54402
+ if (source.startsWith("null", index) && isValueBoundary(source, index + 4)) {
54403
+ args[key] = true;
54404
+ index += 4;
54405
+ continue;
54406
+ }
54407
+ const valueMatch = /^\S+/.exec(source.slice(index));
54408
+ if (!valueMatch) {
54409
+ throw new Error(`Missing partial argument value for '${key}'`);
54410
+ }
54411
+ const valueToken = valueMatch[0];
54412
+ if (NUMBER_LITERAL_REGEX.test(valueToken) || TEMPLATE_PATH_REGEX.test(valueToken) && isAllowedPartialArgPath(valueToken, options2)) {
54413
+ args[key] = true;
54414
+ index += valueToken.length;
54415
+ continue;
54416
+ }
54051
54417
  throw new Error(`Unsupported partial argument value for '${key}'`);
54052
54418
  }
54053
54419
  return args;
@@ -54055,6 +54421,23 @@ function parsePartialArgs(source) {
54055
54421
  function isValueBoundary(source, index) {
54056
54422
  return index >= source.length || /\s/.test(source[index]);
54057
54423
  }
54424
+ function isAllowedPartialArgPath(valueToken, options2) {
54425
+ if (valueToken.includes(".")) {
54426
+ return true;
54427
+ }
54428
+ const allowedSingleSegmentPaths = options2?.allowedSingleSegmentPaths;
54429
+ return allowedSingleSegmentPaths instanceof Set && allowedSingleSegmentPaths.has(valueToken);
54430
+ }
54431
+ function getPartialArgScope(stack) {
54432
+ const scope = new Set(PARTIAL_ARG_ROOT_PATHS);
54433
+ for (const entry of stack) {
54434
+ if (entry.tag === "for" && typeof entry.itemName === "string") {
54435
+ scope.add(entry.itemName);
54436
+ scope.add("loop");
54437
+ }
54438
+ }
54439
+ return scope;
54440
+ }
54058
54441
  function validatePathSafety(pathEntries, errors) {
54059
54442
  for (const entry of pathEntries) {
54060
54443
  const entryPath = normalizePath(String(entry.path || ""));
@@ -54102,13 +54485,71 @@ function normalizeAbsolutePath(value) {
54102
54485
  function isPathInsideRoot(candidate, root) {
54103
54486
  return candidate === root || candidate.startsWith(`${root}/`);
54104
54487
  }
54105
- function issue2(code2, filePath, message, severity) {
54106
- return {
54488
+ function issue2(code2, filePath, message, severity, details = {}) {
54489
+ const nextIssue = {
54107
54490
  code: code2,
54108
54491
  path: filePath,
54109
54492
  message,
54110
54493
  severity
54111
54494
  };
54495
+ if (Number.isInteger(details.line) && details.line > 0) {
54496
+ nextIssue.line = details.line;
54497
+ }
54498
+ if (Number.isInteger(details.column) && details.column > 0) {
54499
+ nextIssue.column = details.column;
54500
+ }
54501
+ if (typeof details.hint === "string" && details.hint.trim()) {
54502
+ nextIssue.hint = details.hint;
54503
+ }
54504
+ if (typeof details.category === "string" && details.category.trim()) {
54505
+ nextIssue.category = details.category;
54506
+ }
54507
+ if (details.snippet && typeof details.snippet === "object" && typeof details.snippet.line === "string" && typeof details.snippet.pointer === "string") {
54508
+ nextIssue.snippet = {
54509
+ line: details.snippet.line,
54510
+ pointer: details.snippet.pointer
54511
+ };
54512
+ }
54513
+ return nextIssue;
54514
+ }
54515
+ function locationForJsonParseError(error, source) {
54516
+ const message = error instanceof Error ? error.message : String(error);
54517
+ const lineColumnMatch = /\bline\s+(\d+)\s+column\s+(\d+)/i.exec(message);
54518
+ if (lineColumnMatch) {
54519
+ return {
54520
+ line: Number(lineColumnMatch[1]),
54521
+ column: Number(lineColumnMatch[2])
54522
+ };
54523
+ }
54524
+ const positionMatch = /\bposition\s+(\d+)/i.exec(message);
54525
+ if (positionMatch) {
54526
+ return locationForIndex(source, Number(positionMatch[1]));
54527
+ }
54528
+ if (/Unexpected end of JSON input/i.test(message)) {
54529
+ return locationForIndex(source, source.length);
54530
+ }
54531
+ return {};
54532
+ }
54533
+ function locationForIndex(source, index) {
54534
+ const boundedIndex = Math.max(0, Math.min(Number.isInteger(index) ? index : 0, source.length));
54535
+ let line = 1;
54536
+ let column = 1;
54537
+ for (let cursor = 0; cursor < boundedIndex; cursor += 1) {
54538
+ if (source[cursor] === "\n") {
54539
+ line += 1;
54540
+ column = 1;
54541
+ } else {
54542
+ column += 1;
54543
+ }
54544
+ }
54545
+ return { line, column };
54546
+ }
54547
+ function snippetForIndex(source, index) {
54548
+ const location = locationForIndex(source, index);
54549
+ const lines = String(source).split(/\r?\n/);
54550
+ const line = lines[location.line - 1] || "";
54551
+ const pointer = `${" ".repeat(Math.max(location.column - 1, 0))}^`;
54552
+ return { line, pointer };
54112
54553
  }
54113
54554
 
54114
54555
  // node_modules/@zeropress/build-core/src/assets/asset-processor.js
@@ -59574,6 +60015,10 @@ function sanitizeHtml(html) {
59574
60015
  "li",
59575
60016
  "blockquote",
59576
60017
  "aside",
60018
+ "figure",
60019
+ "figcaption",
60020
+ "picture",
60021
+ "source",
59577
60022
  "table",
59578
60023
  "thead",
59579
60024
  "tbody",
@@ -59589,9 +60034,10 @@ function sanitizeHtml(html) {
59589
60034
  const allowedAttributes = {
59590
60035
  a: /* @__PURE__ */ new Set(["href", "title", "class", "id"]),
59591
60036
  aside: /* @__PURE__ */ new Set(["role", "class", "id"]),
59592
- img: /* @__PURE__ */ new Set(["src", "alt", "title", "class", "id", "width", "height"]),
60037
+ img: /* @__PURE__ */ new Set(["src", "srcset", "sizes", "alt", "title", "class", "id", "width", "height", "loading", "decoding"]),
59593
60038
  iframe: /* @__PURE__ */ new Set(["src", "width", "height", "frameborder", "allowfullscreen", "class"]),
59594
60039
  input: /* @__PURE__ */ new Set(["type", "checked", "disabled", "class", "id"]),
60040
+ source: /* @__PURE__ */ new Set(["src", "srcset", "sizes", "type", "media", "width", "height", "class", "id"]),
59595
60041
  "*": /* @__PURE__ */ new Set(["class", "id"])
59596
60042
  };
59597
60043
  const safeUriPattern = /^(?:(?:https?|mailto|tel):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
@@ -59619,13 +60065,16 @@ function sanitizeHtml(html) {
59619
60065
  if ((attributeName === "href" || attributeName === "src") && !safeUriPattern.test(attributeValue)) {
59620
60066
  continue;
59621
60067
  }
60068
+ if (attributeName === "srcset" && !isSafeSrcset(attributeValue, safeUriPattern)) {
60069
+ continue;
60070
+ }
59622
60071
  if (normalizedTag === "input" && attributeName === "type" && attributeValue !== "checkbox") {
59623
60072
  continue;
59624
60073
  }
59625
60074
  filteredAttributes.push(`${attributeName}="${attributeValue}"`);
59626
60075
  }
59627
60076
  }
59628
- const isSelfClosing = match2.endsWith("/>") || normalizedTag === "br" || normalizedTag === "hr" || normalizedTag === "img" || normalizedTag === "input";
60077
+ const isSelfClosing = match2.endsWith("/>") || normalizedTag === "br" || normalizedTag === "hr" || normalizedTag === "img" || normalizedTag === "input" || normalizedTag === "source";
59629
60078
  const attributeSuffix = filteredAttributes.length > 0 ? ` ${filteredAttributes.join(" ")}` : "";
59630
60079
  return isSelfClosing ? `<${normalizedTag}${attributeSuffix} />` : `<${normalizedTag}${attributeSuffix}>`;
59631
60080
  });
@@ -59636,6 +60085,13 @@ function sanitizeHtml(html) {
59636
60085
  return part.replace(/&(?!(?:[a-zA-Z]+|#\d+|#x[0-9a-fA-F]+);)/g, "&amp;");
59637
60086
  }).join("");
59638
60087
  }
60088
+ function isSafeSrcset(value, safeUriPattern) {
60089
+ const candidates = String(value).split(",").map((candidate) => candidate.trim()).filter(Boolean);
60090
+ return candidates.length > 0 && candidates.every((candidate) => {
60091
+ const [url] = candidate.split(/\s+/);
60092
+ return Boolean(url) && safeUriPattern.test(url);
60093
+ });
60094
+ }
59639
60095
  function slugify(value) {
59640
60096
  return String(value || "").toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
59641
60097
  }
@@ -59728,6 +60184,9 @@ var VariableResolver = class _VariableResolver {
59728
60184
  }
59729
60185
  }
59730
60186
  }
60187
+ if (variablePath === "data" || variablePath.startsWith("data.") || variablePath.includes(".data.")) {
60188
+ return false;
60189
+ }
59731
60190
  const lastSegment = variablePath.split(".").pop();
59732
60191
  if (lastSegment === "html" || lastSegment?.endsWith("_html")) {
59733
60192
  return true;
@@ -59745,7 +60204,10 @@ var VariableResolver = class _VariableResolver {
59745
60204
  // node_modules/@zeropress/build-core/src/render/partial-resolver.js
59746
60205
  var PARTIAL_NAME_REGEX2 = /^[a-zA-Z_][a-zA-Z0-9_-]*(?:\/[a-zA-Z_][a-zA-Z0-9_-]*)*$/;
59747
60206
  var IDENTIFIER_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
59748
- function parsePartialToken(token) {
60207
+ var PATH_SEGMENT_SOURCE = "[a-zA-Z_][a-zA-Z0-9_]*(?:-[a-zA-Z0-9_]+)*";
60208
+ var PATH_REGEX = new RegExp(`^${PATH_SEGMENT_SOURCE}(?:\\.${PATH_SEGMENT_SOURCE})*$`);
60209
+ var NUMBER_LITERAL_REGEX2 = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
60210
+ function parsePartialToken(token, options2 = {}) {
59749
60211
  const source = String(token || "").trim();
59750
60212
  if (!source.startsWith("partial:")) {
59751
60213
  throw new Error(`Invalid partial token: ${source}`);
@@ -59760,10 +60222,10 @@ function parsePartialToken(token) {
59760
60222
  throw new Error(`Invalid partial name: ${name}`);
59761
60223
  }
59762
60224
  const argsSource = expression.slice(name.length).trim();
59763
- const args = parsePartialArgs2(argsSource);
60225
+ const args = parsePartialArgs2(argsSource, options2);
59764
60226
  return { name, args };
59765
60227
  }
59766
- function parsePartialArgs2(source) {
60228
+ function parsePartialArgs2(source, options2 = {}) {
59767
60229
  if (!source) {
59768
60230
  return {};
59769
60231
  }
@@ -59815,20 +60277,40 @@ function parsePartialArgs2(source) {
59815
60277
  if (cursor >= source.length || source[cursor] !== '"') {
59816
60278
  throw new Error(`Unclosed string literal for partial argument "${key}"`);
59817
60279
  }
59818
- args[key] = JSON.parse(source.slice(index, cursor + 1));
60280
+ args[key] = { kind: "literal", value: JSON.parse(source.slice(index, cursor + 1)) };
59819
60281
  index = cursor + 1;
59820
60282
  continue;
59821
60283
  }
59822
60284
  if (source.startsWith("true", index) && isValueBoundary2(source, index + 4)) {
59823
- args[key] = true;
60285
+ args[key] = { kind: "literal", value: true };
59824
60286
  index += 4;
59825
60287
  continue;
59826
60288
  }
59827
60289
  if (source.startsWith("false", index) && isValueBoundary2(source, index + 5)) {
59828
- args[key] = false;
60290
+ args[key] = { kind: "literal", value: false };
59829
60291
  index += 5;
59830
60292
  continue;
59831
60293
  }
60294
+ if (source.startsWith("null", index) && isValueBoundary2(source, index + 4)) {
60295
+ args[key] = { kind: "literal", value: null };
60296
+ index += 4;
60297
+ continue;
60298
+ }
60299
+ const valueMatch = /^\S+/.exec(source.slice(index));
60300
+ if (!valueMatch) {
60301
+ throw new Error(`Missing partial argument value for "${key}"`);
60302
+ }
60303
+ const valueToken = valueMatch[0];
60304
+ if (NUMBER_LITERAL_REGEX2.test(valueToken)) {
60305
+ args[key] = { kind: "literal", value: Number(valueToken) };
60306
+ index += valueToken.length;
60307
+ continue;
60308
+ }
60309
+ if (PATH_REGEX.test(valueToken) && isAllowedPathArgument(valueToken, options2)) {
60310
+ args[key] = { kind: "path", path: valueToken };
60311
+ index += valueToken.length;
60312
+ continue;
60313
+ }
59832
60314
  throw new Error(`Unsupported partial argument value for "${key}"`);
59833
60315
  }
59834
60316
  return args;
@@ -59836,12 +60318,146 @@ function parsePartialArgs2(source) {
59836
60318
  function isValueBoundary2(source, index) {
59837
60319
  return index >= source.length || /\s/.test(source[index]);
59838
60320
  }
60321
+ function isAllowedPathArgument(valueToken, options2) {
60322
+ if (valueToken.includes(".")) {
60323
+ return true;
60324
+ }
60325
+ const allowedSingleSegmentPaths = options2?.allowedSingleSegmentPaths;
60326
+ return allowedSingleSegmentPaths instanceof Set && allowedSingleSegmentPaths.has(valueToken);
60327
+ }
59839
60328
 
59840
60329
  // node_modules/@zeropress/build-core/src/render/control-flow-renderer.js
59841
- var PATH_SEGMENT_SOURCE = "[a-zA-Z_][a-zA-Z0-9_]*(?:-[a-zA-Z0-9_]+)*";
59842
- var PATH_REGEX = new RegExp(`^${PATH_SEGMENT_SOURCE}(?:\\.${PATH_SEGMENT_SOURCE})*$`);
59843
- var FOR_EXPRESSION_REGEX = new RegExp(`^([a-zA-Z_][a-zA-Z0-9_]*)\\s+in\\s+(${PATH_SEGMENT_SOURCE}(?:\\.${PATH_SEGMENT_SOURCE})*)$`);
59844
- var IF_EQ_EXPRESSION_REGEX2 = new RegExp(`^(${PATH_SEGMENT_SOURCE}(?:\\.${PATH_SEGMENT_SOURCE})*)\\s+("(?:[^"\\\\]|\\\\.)*")$`);
60330
+ var PATH_SEGMENT_SOURCE2 = "[a-zA-Z_][a-zA-Z0-9_]*(?:-[a-zA-Z0-9_]+)*";
60331
+ var PATH_REGEX2 = new RegExp(`^${PATH_SEGMENT_SOURCE2}(?:\\.${PATH_SEGMENT_SOURCE2})*$`);
60332
+ var FOR_EXPRESSION_REGEX = new RegExp(`^([a-zA-Z_][a-zA-Z0-9_]*)\\s+in\\s+(${PATH_SEGMENT_SOURCE2}(?:\\.${PATH_SEGMENT_SOURCE2})*)$`);
60333
+ var NUMBER_LITERAL_REGEX3 = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
60334
+ var COMPARISON_BLOCK_TAGS2 = /* @__PURE__ */ new Set(["if_eq", "if_neq", "if_in", "if_starts_with"]);
60335
+ var COMPARISON_ELSE_IF_TAGS2 = /* @__PURE__ */ new Set(["else_if_eq", "else_if_neq", "else_if_in", "else_if_starts_with"]);
60336
+ var COMPARISON_TAG_OPERATORS2 = {
60337
+ if_eq: "eq",
60338
+ else_if_eq: "eq",
60339
+ if_neq: "neq",
60340
+ else_if_neq: "neq",
60341
+ if_in: "in",
60342
+ else_if_in: "in",
60343
+ if_starts_with: "starts_with",
60344
+ else_if_starts_with: "starts_with"
60345
+ };
60346
+ var PARTIAL_ARG_ROOT_PATHS2 = /* @__PURE__ */ new Set([
60347
+ "archive",
60348
+ "author",
60349
+ "category",
60350
+ "collection",
60351
+ "collections",
60352
+ "currentUrl",
60353
+ "language",
60354
+ "menu",
60355
+ "menus",
60356
+ "meta",
60357
+ "page",
60358
+ "pagination",
60359
+ "partial",
60360
+ "post",
60361
+ "posts",
60362
+ "route",
60363
+ "site",
60364
+ "tag",
60365
+ "taxonomies",
60366
+ "taxonomy",
60367
+ "widget",
60368
+ "widgets"
60369
+ ]);
60370
+ function getComparisonBlockTag2(token) {
60371
+ for (const tagName of COMPARISON_BLOCK_TAGS2) {
60372
+ if (token.startsWith(`#${tagName} `)) {
60373
+ return tagName;
60374
+ }
60375
+ }
60376
+ return "";
60377
+ }
60378
+ function getComparisonElseIfTag2(token) {
60379
+ for (const tagName of COMPARISON_ELSE_IF_TAGS2) {
60380
+ if (token.startsWith(`#${tagName} `)) {
60381
+ return tagName;
60382
+ }
60383
+ }
60384
+ return "";
60385
+ }
60386
+ function tokenizeExpression2(expression) {
60387
+ const tokens = [];
60388
+ let index = 0;
60389
+ while (index < expression.length) {
60390
+ while (/\s/.test(expression[index] || "")) {
60391
+ index += 1;
60392
+ }
60393
+ if (index >= expression.length) {
60394
+ break;
60395
+ }
60396
+ if (expression[index] === '"') {
60397
+ const start2 = index;
60398
+ index += 1;
60399
+ let escaped = false;
60400
+ while (index < expression.length) {
60401
+ const char = expression[index];
60402
+ if (escaped) {
60403
+ escaped = false;
60404
+ } else if (char === "\\") {
60405
+ escaped = true;
60406
+ } else if (char === '"') {
60407
+ index += 1;
60408
+ tokens.push(expression.slice(start2, index));
60409
+ break;
60410
+ }
60411
+ index += 1;
60412
+ }
60413
+ if (tokens[tokens.length - 1] !== expression.slice(start2, index)) {
60414
+ throw new Error(`Unclosed string literal in expression: ${expression}`);
60415
+ }
60416
+ continue;
60417
+ }
60418
+ const start = index;
60419
+ while (index < expression.length && !/\s/.test(expression[index])) {
60420
+ index += 1;
60421
+ }
60422
+ tokens.push(expression.slice(start, index));
60423
+ }
60424
+ return tokens;
60425
+ }
60426
+ function parsePathOperand2(token, tagName, expression) {
60427
+ if (!PATH_REGEX2.test(token)) {
60428
+ throw new Error(`Invalid ${tagName} expression: ${expression}`);
60429
+ }
60430
+ return { kind: "path", path: token };
60431
+ }
60432
+ function parseComparisonOperand2(token, tagName, expression) {
60433
+ if (token.startsWith('"')) {
60434
+ try {
60435
+ const value = JSON.parse(token);
60436
+ if (typeof value !== "string") {
60437
+ throw new Error("Expected string literal");
60438
+ }
60439
+ return { kind: "literal", value };
60440
+ } catch {
60441
+ throw new Error(`Invalid ${tagName} expression: ${expression}`);
60442
+ }
60443
+ }
60444
+ if (token === "true") {
60445
+ return { kind: "literal", value: true };
60446
+ }
60447
+ if (token === "false") {
60448
+ return { kind: "literal", value: false };
60449
+ }
60450
+ if (token === "null") {
60451
+ return { kind: "literal", value: null };
60452
+ }
60453
+ if (NUMBER_LITERAL_REGEX3.test(token)) {
60454
+ return { kind: "literal", value: Number(token) };
60455
+ }
60456
+ if (PATH_REGEX2.test(token)) {
60457
+ return { kind: "path", path: token };
60458
+ }
60459
+ throw new Error(`Invalid ${tagName} expression: ${expression}`);
60460
+ }
59845
60461
  var ControlFlowRenderer = class {
59846
60462
  constructor(options2 = {}) {
59847
60463
  this.resolvePath = typeof options2.resolvePath === "function" ? options2.resolvePath : ((data, path4) => path4.split(".").reduce((current, segment) => {
@@ -59864,7 +60480,7 @@ var ControlFlowRenderer = class {
59864
60480
  }
59865
60481
  parse(template) {
59866
60482
  const source = String(template || "");
59867
- const { nodes, nextIndex, stopTag } = this.parseNodes(source, 0, /* @__PURE__ */ new Set());
60483
+ const { nodes, nextIndex, stopTag } = this.parseNodes(source, 0, /* @__PURE__ */ new Set(), PARTIAL_ARG_ROOT_PATHS2);
59868
60484
  if (stopTag) {
59869
60485
  throw new Error(`Unexpected closing tag ${stopTag}`);
59870
60486
  }
@@ -59873,7 +60489,7 @@ var ControlFlowRenderer = class {
59873
60489
  }
59874
60490
  return nodes;
59875
60491
  }
59876
- parseNodes(source, startIndex, stopTags) {
60492
+ parseNodes(source, startIndex, stopTags, partialArgScope) {
59877
60493
  const nodes = [];
59878
60494
  let index = startIndex;
59879
60495
  while (index < source.length) {
@@ -59924,9 +60540,10 @@ var ControlFlowRenderer = class {
59924
60540
  }
59925
60541
  return { nodes, nextIndex: tokenEnd + 2, stopTag: "else" };
59926
60542
  }
59927
- if (token.startsWith("#else_if_eq ")) {
59928
- if (!stopTags.has("else_if_eq")) {
59929
- throw new Error("Unexpected else_if_eq tag");
60543
+ const comparisonElseIfTag = getComparisonElseIfTag2(token);
60544
+ if (comparisonElseIfTag) {
60545
+ if (!stopTags.has(comparisonElseIfTag)) {
60546
+ throw new Error(`Unexpected ${comparisonElseIfTag} tag`);
59930
60547
  }
59931
60548
  return { nodes, nextIndex: tokenEnd + 2, stopTag: token };
59932
60549
  }
@@ -59937,21 +60554,22 @@ var ControlFlowRenderer = class {
59937
60554
  return { nodes, nextIndex: tokenEnd + 2, stopTag: token };
59938
60555
  }
59939
60556
  if (token.startsWith("partial:")) {
59940
- const { name, args } = parsePartialToken(token);
60557
+ const { name, args } = parsePartialToken(token, { allowedSingleSegmentPaths: partialArgScope });
59941
60558
  nodes.push({ type: "partial", name, args });
59942
60559
  index = tokenEnd + 2;
59943
60560
  continue;
59944
60561
  }
59945
- if (token.startsWith("#if_eq ")) {
59946
- const expression = token.slice("#if_eq ".length).trim();
59947
- const block2 = this.parseIfEqBlock(source, tokenEnd + 2, expression);
60562
+ const comparisonBlockTag = getComparisonBlockTag2(token);
60563
+ if (comparisonBlockTag) {
60564
+ const expression = token.slice(`#${comparisonBlockTag} `.length).trim();
60565
+ const block2 = this.parseComparisonBlock(source, tokenEnd + 2, comparisonBlockTag, expression, partialArgScope);
59948
60566
  nodes.push(block2.node);
59949
60567
  index = block2.nextIndex;
59950
60568
  continue;
59951
60569
  }
59952
60570
  if (token.startsWith("#if ")) {
59953
60571
  const expression = token.slice("#if ".length).trim();
59954
- const block2 = this.parseIfBlock(source, tokenEnd + 2, expression);
60572
+ const block2 = this.parseIfBlock(source, tokenEnd + 2, expression, partialArgScope);
59955
60573
  nodes.push(block2.node);
59956
60574
  index = block2.nextIndex;
59957
60575
  continue;
@@ -59963,7 +60581,8 @@ var ControlFlowRenderer = class {
59963
60581
  throw new Error(`Invalid for expression: ${expression}`);
59964
60582
  }
59965
60583
  const [, itemName, path4] = match2;
59966
- const forResult = this.parseNodes(source, tokenEnd + 2, /* @__PURE__ */ new Set(["for"]));
60584
+ const forScope = /* @__PURE__ */ new Set([...partialArgScope, itemName, "loop"]);
60585
+ const forResult = this.parseNodes(source, tokenEnd + 2, /* @__PURE__ */ new Set(["for"]), forScope);
59967
60586
  if (forResult.stopTag !== "for") {
59968
60587
  throw new Error("Unclosed for block");
59969
60588
  }
@@ -59980,8 +60599,8 @@ var ControlFlowRenderer = class {
59980
60599
  }
59981
60600
  return { nodes, nextIndex: source.length, stopTag: null };
59982
60601
  }
59983
- parseIfBlock(source, startIndex, initialPath) {
59984
- if (!PATH_REGEX.test(initialPath)) {
60602
+ parseIfBlock(source, startIndex, initialPath, partialArgScope) {
60603
+ if (!PATH_REGEX2.test(initialPath)) {
59985
60604
  throw new Error(`Invalid if expression: ${initialPath}`);
59986
60605
  }
59987
60606
  const branches = [];
@@ -59989,13 +60608,13 @@ var ControlFlowRenderer = class {
59989
60608
  let nextIndex = startIndex;
59990
60609
  let currentPath = initialPath;
59991
60610
  while (true) {
59992
- const branchResult = this.parseNodes(source, nextIndex, /* @__PURE__ */ new Set(["else", "else_if", "if"]));
60611
+ const branchResult = this.parseNodes(source, nextIndex, /* @__PURE__ */ new Set(["else", "else_if", "if"]), partialArgScope);
59993
60612
  branches.push({
59994
60613
  path: currentPath,
59995
60614
  consequent: branchResult.nodes
59996
60615
  });
59997
60616
  if (branchResult.stopTag === "else") {
59998
- const elseResult = this.parseNodes(source, branchResult.nextIndex, /* @__PURE__ */ new Set(["if"]));
60617
+ const elseResult = this.parseNodes(source, branchResult.nextIndex, /* @__PURE__ */ new Set(["if"]), partialArgScope);
59999
60618
  if (elseResult.stopTag !== "if") {
60000
60619
  throw new Error("Unclosed if block after else");
60001
60620
  }
@@ -60005,7 +60624,7 @@ var ControlFlowRenderer = class {
60005
60624
  }
60006
60625
  if (typeof branchResult.stopTag === "string" && branchResult.stopTag.startsWith("#else_if ")) {
60007
60626
  currentPath = branchResult.stopTag.slice("#else_if ".length).trim();
60008
- if (!PATH_REGEX.test(currentPath)) {
60627
+ if (!PATH_REGEX2.test(currentPath)) {
60009
60628
  throw new Error(`Invalid else_if expression: ${currentPath}`);
60010
60629
  }
60011
60630
  nextIndex = branchResult.nextIndex;
@@ -60026,60 +60645,65 @@ var ControlFlowRenderer = class {
60026
60645
  nextIndex
60027
60646
  };
60028
60647
  }
60029
- parseIfEqBlock(source, startIndex, expression) {
60030
- const initialBranch = this.parseIfEqBranchExpression(expression, "if_eq");
60648
+ parseComparisonBlock(source, startIndex, tagName, expression, partialArgScope) {
60649
+ const initialBranch = this.parseComparisonBranchExpression(expression, tagName);
60031
60650
  const branches = [];
60032
60651
  let alternate = [];
60033
60652
  let nextIndex = startIndex;
60034
60653
  let currentBranch = initialBranch;
60654
+ const elseIfTag = `else_${tagName}`;
60035
60655
  while (true) {
60036
- const branchResult = this.parseNodes(source, nextIndex, /* @__PURE__ */ new Set(["else", "else_if_eq", "if_eq"]));
60656
+ const branchResult = this.parseNodes(source, nextIndex, /* @__PURE__ */ new Set(["else", elseIfTag, tagName]), partialArgScope);
60037
60657
  branches.push({
60038
- path: currentBranch.path,
60039
- literal: currentBranch.literal,
60658
+ operator: currentBranch.operator,
60659
+ left: currentBranch.left,
60660
+ operands: currentBranch.operands,
60040
60661
  consequent: branchResult.nodes
60041
60662
  });
60042
60663
  if (branchResult.stopTag === "else") {
60043
- const elseResult = this.parseNodes(source, branchResult.nextIndex, /* @__PURE__ */ new Set(["if_eq"]));
60044
- if (elseResult.stopTag !== "if_eq") {
60045
- throw new Error("Unclosed if_eq block after else");
60664
+ const elseResult = this.parseNodes(source, branchResult.nextIndex, /* @__PURE__ */ new Set([tagName]), partialArgScope);
60665
+ if (elseResult.stopTag !== tagName) {
60666
+ throw new Error(`Unclosed ${tagName} block after else`);
60046
60667
  }
60047
60668
  alternate = elseResult.nodes;
60048
60669
  nextIndex = elseResult.nextIndex;
60049
60670
  break;
60050
60671
  }
60051
- if (typeof branchResult.stopTag === "string" && branchResult.stopTag.startsWith("#else_if_eq ")) {
60052
- currentBranch = this.parseIfEqBranchExpression(
60053
- branchResult.stopTag.slice("#else_if_eq ".length).trim(),
60054
- "else_if_eq"
60672
+ if (typeof branchResult.stopTag === "string" && branchResult.stopTag.startsWith(`#${elseIfTag} `)) {
60673
+ currentBranch = this.parseComparisonBranchExpression(
60674
+ branchResult.stopTag.slice(`#${elseIfTag} `.length).trim(),
60675
+ elseIfTag
60055
60676
  );
60056
60677
  nextIndex = branchResult.nextIndex;
60057
60678
  continue;
60058
60679
  }
60059
- if (branchResult.stopTag === "if_eq") {
60680
+ if (branchResult.stopTag === tagName) {
60060
60681
  nextIndex = branchResult.nextIndex;
60061
60682
  break;
60062
60683
  }
60063
- throw new Error("Unclosed if_eq block");
60684
+ throw new Error(`Unclosed ${tagName} block`);
60064
60685
  }
60065
60686
  return {
60066
60687
  node: {
60067
- type: "if_eq",
60688
+ type: "comparison",
60068
60689
  branches,
60069
60690
  alternate
60070
60691
  },
60071
60692
  nextIndex
60072
60693
  };
60073
60694
  }
60074
- parseIfEqBranchExpression(expression, tagName) {
60075
- const match2 = IF_EQ_EXPRESSION_REGEX2.exec(expression);
60076
- if (!match2) {
60695
+ parseComparisonBranchExpression(expression, tagName) {
60696
+ const operator = COMPARISON_TAG_OPERATORS2[tagName];
60697
+ const tokens = tokenizeExpression2(expression);
60698
+ if (!operator || operator === "in" && tokens.length < 2 || operator !== "in" && tokens.length !== 2) {
60077
60699
  throw new Error(`Invalid ${tagName} expression: ${expression}`);
60078
60700
  }
60079
- const [, path4, literalSource] = match2;
60701
+ const left = parsePathOperand2(tokens[0], tagName, expression);
60702
+ const operands = tokens.slice(1).map((token) => parseComparisonOperand2(token, tagName, expression));
60080
60703
  return {
60081
- path: path4,
60082
- literal: JSON.parse(literalSource)
60704
+ operator,
60705
+ left,
60706
+ operands
60083
60707
  };
60084
60708
  }
60085
60709
  renderNodes(nodes, data, renderOptions) {
@@ -60093,8 +60717,8 @@ var ControlFlowRenderer = class {
60093
60717
  const branch = node.branches.find((entry) => this.isTruthy(this.resolvePath(data, entry.path)));
60094
60718
  return branch ? this.renderNodes(branch.consequent, data, renderOptions) : this.renderNodes(node.alternate, data, renderOptions);
60095
60719
  }
60096
- case "if_eq": {
60097
- const branch = node.branches.find((entry) => this.resolvePath(data, entry.path) === entry.literal);
60720
+ case "comparison": {
60721
+ const branch = node.branches.find((entry) => this.evaluateComparison(entry, data));
60098
60722
  return branch ? this.renderNodes(branch.consequent, data, renderOptions) : this.renderNodes(node.alternate, data, renderOptions);
60099
60723
  }
60100
60724
  case "for": {
@@ -60129,15 +60753,62 @@ var ControlFlowRenderer = class {
60129
60753
  throw new Error(`Circular partial reference detected: ${cycle}`);
60130
60754
  }
60131
60755
  const partialTemplate = String(partials.get(node.name) || "");
60756
+ const partialArgs = this.resolvePartialArgs(node.args, data);
60132
60757
  const partialData = {
60133
60758
  ...data,
60134
- partial: { ...node.args }
60759
+ partial: {
60760
+ ...data?.partial && typeof data.partial === "object" ? data.partial : {},
60761
+ ...partialArgs
60762
+ }
60135
60763
  };
60136
60764
  return this.renderTemplate(partialTemplate, partialData, {
60137
60765
  ...renderOptions,
60138
60766
  partialStack: [...activeStack, node.name]
60139
60767
  });
60140
60768
  }
60769
+ resolvePartialArgs(args, data) {
60770
+ const resolved = {};
60771
+ for (const [key, operand] of Object.entries(args || {})) {
60772
+ if (operand?.kind === "literal") {
60773
+ resolved[key] = operand.value;
60774
+ continue;
60775
+ }
60776
+ if (operand?.kind === "path") {
60777
+ resolved[key] = this.resolvePath(data, operand.path);
60778
+ }
60779
+ }
60780
+ return resolved;
60781
+ }
60782
+ evaluateComparison(entry, data) {
60783
+ const left = this.evaluateOperand(entry.left, data);
60784
+ const operands = entry.operands.map((operand) => this.evaluateOperand(operand, data));
60785
+ if (entry.operator === "eq") {
60786
+ const right = operands[0];
60787
+ return !left.missing && !right.missing && left.value === right.value;
60788
+ }
60789
+ if (entry.operator === "neq") {
60790
+ const right = operands[0];
60791
+ return left.missing || right.missing || left.value !== right.value;
60792
+ }
60793
+ if (entry.operator === "in") {
60794
+ if (left.missing) {
60795
+ return false;
60796
+ }
60797
+ return operands.some((operand) => !operand.missing && left.value === operand.value);
60798
+ }
60799
+ if (entry.operator === "starts_with") {
60800
+ const right = operands[0];
60801
+ return !left.missing && !right.missing && typeof left.value === "string" && typeof right.value === "string" && left.value.startsWith(right.value);
60802
+ }
60803
+ return false;
60804
+ }
60805
+ evaluateOperand(operand, data) {
60806
+ if (operand.kind === "literal") {
60807
+ return { value: operand.value, missing: false };
60808
+ }
60809
+ const value = this.resolvePath(data, operand.path);
60810
+ return { value, missing: value === void 0 };
60811
+ }
60141
60812
  isTruthy(value) {
60142
60813
  if (Array.isArray(value)) {
60143
60814
  return value.length > 0;
@@ -60179,7 +60850,7 @@ var ZeroPressEngine = class {
60179
60850
  const renderData = this.combineRenderData(data, context);
60180
60851
  const renderedContent = this.renderTemplate(template, renderData);
60181
60852
  const layoutWithSlots = this.slotResolver.resolve(layout, this.themePackage.partials, CONTENT_SLOT_PLACEHOLDER);
60182
- return this.renderTemplate(layoutWithSlots, renderData).replaceAll(CONTENT_SLOT_PLACEHOLDER, renderedContent);
60853
+ return this.renderTemplate(layoutWithSlots, renderData).replaceAll(CONTENT_SLOT_PLACEHOLDER, () => renderedContent);
60183
60854
  }
60184
60855
  combineRenderData(data, context) {
60185
60856
  return {
@@ -60190,7 +60861,7 @@ var ZeroPressEngine = class {
60190
60861
  };
60191
60862
  }
60192
60863
  renderTemplate(template, data) {
60193
- if (this.themePackage?.metadata?.runtime !== "0.5") {
60864
+ if (this.themePackage?.metadata?.runtime !== "0.6") {
60194
60865
  throw new Error(`Unsupported theme runtime: ${this.themePackage?.metadata?.runtime || "unknown"}`);
60195
60866
  }
60196
60867
  return this.controlFlowRenderer.render(template, data, {
@@ -60210,10 +60881,13 @@ var DEFAULT_OPTIONS = {
60210
60881
  writeManifest: false
60211
60882
  };
60212
60883
  var DEFAULT_POSTS_PER_PAGE = 10;
60213
- var DEFAULT_DATE_FORMAT = "YYYY-MM-DD";
60214
- var DEFAULT_TIME_FORMAT = "HH:mm";
60884
+ var DEFAULT_DATETIME_DISPLAY = "static";
60885
+ var DEFAULT_DATE_STYLE = "medium";
60886
+ var DEFAULT_TIME_STYLE = "none";
60215
60887
  var DEFAULT_TIMEZONE = "UTC";
60216
60888
  var DEFAULT_LOCALE = "en-US";
60889
+ var DATETIME_DISPLAY_MODES = /* @__PURE__ */ new Set(["static", "client"]);
60890
+ var DATETIME_STYLES = /* @__PURE__ */ new Set(["none", "short", "medium", "long", "full"]);
60217
60891
  var DEFAULT_PERMALINKS = Object.freeze({
60218
60892
  output_style: "directory",
60219
60893
  posts: "/posts/:slug/",
@@ -60231,12 +60905,24 @@ var DEFAULT_POST_INDEX = Object.freeze({
60231
60905
  });
60232
60906
  var PERMALINK_OUTPUT_STYLES = /* @__PURE__ */ new Set(["directory", "html-extension"]);
60233
60907
  var COMMENT_POLICY_OUTPUT_PATH = "_zeropress/comment-policy.json";
60908
+ var SEARCH_INDEX_OUTPUT_PATH = "_zeropress/search.json";
60909
+ var SEARCH_ADAPTER_OUTPUT_PATH = "_zeropress/search.js";
60234
60910
  var OUTPUT_PATH_CONTROL_CHAR_PATTERN = /[\u0000-\u001F\u007F]/;
60235
60911
  var SAFE_MEDIA_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
60236
60912
  var SAFE_LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
60237
60913
  var MEDIA_DELIVERY_MODES = /* @__PURE__ */ new Set(["none", "media_domain"]);
60914
+ var DISCOVERABILITY_VALUES = /* @__PURE__ */ new Set(["default", "noindex", "delist"]);
60238
60915
  var RESPONSIVE_IMAGE_WIDTHS = [320, 480, 768, 1024, 1280, 1600, 1920];
60239
60916
  var RESPONSIVE_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["jpg", "jpeg", "png", "webp", "avif"]);
60917
+ var SEARCH_FIELD_WEIGHTS = Object.freeze({
60918
+ title: 5,
60919
+ headings: 3,
60920
+ tags: 2.5,
60921
+ categories: 2,
60922
+ excerpt: 1.5,
60923
+ content_text: 1
60924
+ });
60925
+ var SEARCH_RECENCY_BOOST_MAX = 0.15;
60240
60926
  async function buildSite(input2) {
60241
60927
  const options2 = { ...DEFAULT_OPTIONS, ...input2.options || {} };
60242
60928
  const state = await createBuildState(input2, options2);
@@ -60279,9 +60965,17 @@ async function buildSite(input2) {
60279
60965
  "application/json"
60280
60966
  );
60281
60967
  if (options2.generateSpecialFiles) {
60968
+ await writeOutput(state.writer, state.summaries, SEARCH_INDEX_OUTPUT_PATH, buildSearchIndexJson(state), "application/json");
60969
+ await writeOutput(state.writer, state.summaries, SEARCH_ADAPTER_OUTPUT_PATH, buildSearchAdapterJs(), "application/javascript");
60282
60970
  await maybeRenderNotFoundPage(state);
60283
60971
  if (hasCanonicalSiteUrl(state.previewData.site.url)) {
60284
- await writeOutput(state.writer, state.summaries, "sitemap.xml", buildSitemapXml(state.previewData.site, state.emitted, state.generatedAt), "application/xml");
60972
+ await writeOutput(
60973
+ state.writer,
60974
+ state.summaries,
60975
+ "sitemap.xml",
60976
+ buildSitemapXml(state.previewData.site, state.emitted, state.generatedAt, options2.sitemapStylesheetHref),
60977
+ "application/xml"
60978
+ );
60285
60979
  await writeOutput(state.writer, state.summaries, "feed.xml", buildFeedXml(state.previewData.site, state.emitted, state.generatedAt), "application/rss+xml");
60286
60980
  }
60287
60981
  if (shouldGenerateRobotsTxt(options2)) {
@@ -60323,6 +61017,7 @@ async function createBuildState(input2, options2) {
60323
61017
  customCssHref: customCssAsset ? `/${customCssAsset.path}` : "",
60324
61018
  customHtml: previewData.custom_html,
60325
61019
  favicon: previewData.site.favicon,
61020
+ exposeGenerator: previewData.site.expose_generator !== false,
60326
61021
  commentPolicyContent: buildCommentPolicyManifest(renderData.posts),
60327
61022
  options: options2,
60328
61023
  generatedAt: /* @__PURE__ */ new Date(),
@@ -60432,7 +61127,8 @@ async function renderFrontPage(state, route) {
60432
61127
  title: buildDocumentTitle(page.title, state.previewData.site.title),
60433
61128
  description: page.excerpt,
60434
61129
  ogType: "website",
60435
- image: page.featured_image
61130
+ image: page.featured_image,
61131
+ robotsNoindex: shouldNoindexDocument(page)
60436
61132
  })
60437
61133
  },
60438
61134
  createRenderContext(state.previewData.site, currentUrl)
@@ -60444,7 +61140,8 @@ async function renderFrontPage(state, route) {
60444
61140
  url: currentUrl,
60445
61141
  title: page.title,
60446
61142
  description: page.excerpt,
60447
- includeInFeed: false
61143
+ includeInFeed: false,
61144
+ includeInSitemap: !isDelistedDocument(page)
60448
61145
  };
60449
61146
  return;
60450
61147
  }
@@ -60494,7 +61191,8 @@ async function renderPost(state, post) {
60494
61191
  ogType: "article",
60495
61192
  image: post.featured_image,
60496
61193
  publishedTime: post.published_at_iso,
60497
- modifiedTime: post.updated_at_iso
61194
+ modifiedTime: post.updated_at_iso,
61195
+ robotsNoindex: shouldNoindexDocument(post)
60498
61196
  })
60499
61197
  },
60500
61198
  createRenderContext(state.previewData.site, currentUrl)
@@ -60502,14 +61200,16 @@ async function renderPost(state, post) {
60502
61200
  html = state.assetProcessor.updateAssetReferences(html, state.assetMap);
60503
61201
  html = injectSiteCustomizations(html, state);
60504
61202
  await writeOutput(state.writer, state.summaries, routePathToOutputPath(post.url, state.previewData.site.permalinks.output_style), html, "text/html");
60505
- state.emitted.posts.push({
60506
- url: currentUrl,
60507
- title: post.title,
60508
- description: post.excerpt,
60509
- publishedAt: post.published_at_iso,
60510
- updatedAt: post.updated_at_iso,
60511
- status: post.status
60512
- });
61203
+ if (!isDelistedDocument(post)) {
61204
+ state.emitted.posts.push({
61205
+ url: currentUrl,
61206
+ title: post.title,
61207
+ description: post.excerpt,
61208
+ publishedAt: post.published_at_iso,
61209
+ updatedAt: post.updated_at_iso,
61210
+ status: post.status
61211
+ });
61212
+ }
60513
61213
  }
60514
61214
  async function renderPage(state, page) {
60515
61215
  const currentUrl = page.url;
@@ -60530,7 +61230,8 @@ async function renderPage(state, page) {
60530
61230
  title: buildDocumentTitle(page.title, state.previewData.site.title),
60531
61231
  description: page.excerpt,
60532
61232
  ogType: "website",
60533
- image: page.featured_image
61233
+ image: page.featured_image,
61234
+ robotsNoindex: shouldNoindexDocument(page)
60534
61235
  })
60535
61236
  },
60536
61237
  createRenderContext(state.previewData.site, currentUrl)
@@ -60543,7 +61244,7 @@ async function renderPage(state, page) {
60543
61244
  title: page.title,
60544
61245
  description: page.excerpt,
60545
61246
  status: page.status,
60546
- includeInSitemap: page.omit_from_sitemap !== true
61247
+ includeInSitemap: page.omit_from_sitemap !== true && !isDelistedDocument(page)
60547
61248
  });
60548
61249
  }
60549
61250
  async function maybeRenderNotFoundPage(state) {
@@ -60574,15 +61275,17 @@ async function maybeRenderNotFoundPage(state) {
60574
61275
  function normalizePreviewData(previewData, options2 = {}) {
60575
61276
  const normalizedSite = {
60576
61277
  ...previewData.site,
60577
- mediaBaseUrl: normalizeOptionalString(previewData.site.mediaBaseUrl),
60578
- mediaDeliveryMode: MEDIA_DELIVERY_MODES.has(previewData.site.mediaDeliveryMode) ? previewData.site.mediaDeliveryMode : "none",
61278
+ media_base_url: normalizeOptionalString(previewData.site.media_base_url),
61279
+ media_delivery_mode: MEDIA_DELIVERY_MODES.has(previewData.site.media_delivery_mode) ? previewData.site.media_delivery_mode : "none",
60579
61280
  favicon: normalizeSiteFavicon(previewData.site.favicon || options2.favicon),
60580
- postsPerPage: Number.isInteger(previewData.site.postsPerPage) && previewData.site.postsPerPage > 0 ? previewData.site.postsPerPage : DEFAULT_POSTS_PER_PAGE,
60581
- dateFormat: normalizeNonEmptyString(previewData.site.dateFormat, DEFAULT_DATE_FORMAT),
60582
- timeFormat: typeof previewData.site.timeFormat === "string" ? previewData.site.timeFormat : DEFAULT_TIME_FORMAT,
61281
+ posts_per_page: Number.isInteger(previewData.site.posts_per_page) && previewData.site.posts_per_page > 0 ? previewData.site.posts_per_page : DEFAULT_POSTS_PER_PAGE,
61282
+ datetime_display: DATETIME_DISPLAY_MODES.has(previewData.site.datetime_display) ? previewData.site.datetime_display : DEFAULT_DATETIME_DISPLAY,
61283
+ date_style: DATETIME_STYLES.has(previewData.site.date_style) ? previewData.site.date_style : DEFAULT_DATE_STYLE,
61284
+ time_style: DATETIME_STYLES.has(previewData.site.time_style) ? previewData.site.time_style : DEFAULT_TIME_STYLE,
60583
61285
  timezone: normalizeNonEmptyString(previewData.site.timezone, DEFAULT_TIMEZONE),
60584
61286
  locale: normalizeLocale(previewData.site.locale || DEFAULT_LOCALE),
60585
- disallowComments: previewData.site.disallowComments === true,
61287
+ disallow_comments: previewData.site.disallow_comments === true,
61288
+ expose_generator: previewData.site.expose_generator !== false,
60586
61289
  indexing: previewData.site.indexing !== false,
60587
61290
  permalinks: normalizePermalinks(previewData.site.permalinks),
60588
61291
  front_page: normalizeFrontPage(previewData.site.front_page),
@@ -60596,13 +61299,13 @@ function normalizePreviewData(previewData, options2 = {}) {
60596
61299
  site: normalizedSite,
60597
61300
  menus: normalizeRecordMap(previewData.menus),
60598
61301
  collections: normalizeCollections(previewData.collections),
60599
- widgets: normalizeWidgetAreas(previewData.widgets, normalizedSite.mediaBaseUrl),
61302
+ widgets: normalizeWidgetAreas(previewData.widgets, normalizedSite.media_base_url),
60600
61303
  custom_css: normalizeCustomCss(previewData.custom_css),
60601
61304
  custom_html: normalizeCustomHtml(previewData.custom_html),
60602
61305
  content: {
60603
61306
  ...previewData.content,
60604
61307
  authors: previewData.content.authors.map((author) => {
60605
- const avatar = normalizeMediaField(author.avatar, normalizedSite.mediaBaseUrl);
61308
+ const avatar = normalizeMediaField(author.avatar, normalizedSite.media_base_url);
60606
61309
  const avatarMedia = deriveManagedMedia(avatar, mediaRegistry, normalizedSite);
60607
61310
  return {
60608
61311
  ...author,
@@ -60611,21 +61314,23 @@ function normalizePreviewData(previewData, options2 = {}) {
60611
61314
  };
60612
61315
  }),
60613
61316
  posts: previewData.content.posts.map((post) => {
60614
- const featuredImage = normalizeMediaField(post.featured_image, normalizedSite.mediaBaseUrl);
61317
+ const featuredImage = normalizeMediaField(post.featured_image, normalizedSite.media_base_url);
60615
61318
  const featuredMedia = deriveManagedMedia(featuredImage, mediaRegistry, normalizedSite);
60616
61319
  return {
60617
61320
  ...post,
60618
61321
  published_at_iso: normalizeIsoTimestamp(post.published_at_iso),
60619
61322
  updated_at_iso: normalizeIsoTimestamp(post.updated_at_iso),
61323
+ discoverability: normalizeDiscoverability(post.discoverability),
60620
61324
  featured_image: featuredImage,
60621
61325
  ...featuredMedia ? { featured_media: featuredMedia } : {}
60622
61326
  };
60623
61327
  }).sort((left, right) => toDate(right.published_at_iso).getTime() - toDate(left.published_at_iso).getTime()),
60624
61328
  pages: previewData.content.pages.map((page) => {
60625
- const featuredImage = normalizeMediaField(page.featured_image, normalizedSite.mediaBaseUrl);
61329
+ const featuredImage = normalizeMediaField(page.featured_image, normalizedSite.media_base_url);
60626
61330
  const featuredMedia = deriveManagedMedia(featuredImage, mediaRegistry, normalizedSite);
60627
61331
  return {
60628
61332
  ...page,
61333
+ discoverability: normalizeDiscoverability(page.discoverability),
60629
61334
  featured_image: featuredImage,
60630
61335
  ...featuredMedia ? { featured_media: featuredMedia } : {}
60631
61336
  };
@@ -60642,6 +61347,15 @@ function normalizeRecordMap(value) {
60642
61347
  }
60643
61348
  return { ...value };
60644
61349
  }
61350
+ function normalizeDiscoverability(value) {
61351
+ return DISCOVERABILITY_VALUES.has(value) ? value : "default";
61352
+ }
61353
+ function isDelistedDocument(document2) {
61354
+ return document2?.discoverability === "delist";
61355
+ }
61356
+ function shouldNoindexDocument(document2) {
61357
+ return document2?.discoverability === "noindex" || document2?.discoverability === "delist";
61358
+ }
60645
61359
  function normalizeContentMedia(mediaItems, site) {
60646
61360
  if (!Array.isArray(mediaItems)) {
60647
61361
  return [];
@@ -60650,7 +61364,7 @@ function normalizeContentMedia(mediaItems, site) {
60650
61364
  if (!item || typeof item !== "object") {
60651
61365
  return null;
60652
61366
  }
60653
- const src = normalizeMediaField(item.src, site.mediaBaseUrl);
61367
+ const src = normalizeMediaField(item.src, site.media_base_url);
60654
61368
  const width = Number.isInteger(item.width) && item.width > 0 ? item.width : 0;
60655
61369
  const height = Number.isInteger(item.height) && item.height > 0 ? item.height : 0;
60656
61370
  if (!src || !width || !height) {
@@ -60688,11 +61402,11 @@ function deriveManagedMedia(src, mediaRegistry, site) {
60688
61402
  };
60689
61403
  }
60690
61404
  function buildResponsiveImageSrcset(media, site) {
60691
- if (site.mediaDeliveryMode !== "media_domain") {
61405
+ if (site.media_delivery_mode !== "media_domain") {
60692
61406
  return "";
60693
61407
  }
60694
- const mediaBaseUrl = normalizeOptionalString(site.mediaBaseUrl);
60695
- if (!mediaBaseUrl || !isUrlUnderMediaBase(media.src, mediaBaseUrl) || !isResponsiveRasterImage(media.src)) {
61408
+ const media_base_url = normalizeOptionalString(site.media_base_url);
61409
+ if (!media_base_url || !isUrlUnderMediaBase(media.src, media_base_url) || !isResponsiveRasterImage(media.src)) {
60696
61410
  return "";
60697
61411
  }
60698
61412
  const widths = RESPONSIVE_IMAGE_WIDTHS.filter((width) => width <= media.width);
@@ -60712,10 +61426,10 @@ function buildResponsiveImageVariantUrl(src, width) {
60712
61426
  return src;
60713
61427
  }
60714
61428
  }
60715
- function isUrlUnderMediaBase(src, mediaBaseUrl) {
61429
+ function isUrlUnderMediaBase(src, media_base_url) {
60716
61430
  try {
60717
61431
  const sourceUrl = new URL(src);
60718
- const baseUrl = new URL(mediaBaseUrl);
61432
+ const baseUrl = new URL(media_base_url);
60719
61433
  const basePath = baseUrl.pathname.endsWith("/") ? baseUrl.pathname : `${baseUrl.pathname}/`;
60720
61434
  return sourceUrl.origin === baseUrl.origin && sourceUrl.pathname.startsWith(basePath);
60721
61435
  } catch {
@@ -60748,34 +61462,30 @@ function normalizeCollections(collections) {
60748
61462
  ])
60749
61463
  );
60750
61464
  }
60751
- function normalizeWidgetAreas(widgetAreas, mediaBaseUrl) {
60752
- if (!widgetAreas || typeof widgetAreas !== "object") {
61465
+ function normalizeWidgetAreas(widget_areas, media_base_url) {
61466
+ if (!widget_areas || typeof widget_areas !== "object") {
60753
61467
  return {};
60754
61468
  }
60755
61469
  return Object.fromEntries(
60756
- Object.entries(widgetAreas).map(([widgetAreaId, widgetArea]) => [
61470
+ Object.entries(widget_areas).map(([widgetAreaId, widgetArea]) => [
60757
61471
  widgetAreaId,
60758
61472
  {
60759
61473
  ...widgetArea,
60760
61474
  name: normalizeNonEmptyString(widgetArea?.name, widgetAreaId),
60761
- items: Array.isArray(widgetArea?.items) ? widgetArea.items.map((item) => normalizeWidgetItem(item, mediaBaseUrl)) : []
61475
+ items: Array.isArray(widgetArea?.items) ? widgetArea.items.map((item) => normalizeWidgetItem(item, media_base_url)) : []
60762
61476
  }
60763
61477
  ])
60764
61478
  );
60765
61479
  }
60766
61480
  function normalizeSiteFooter(footer) {
60767
61481
  const source = footer && typeof footer === "object" && !Array.isArray(footer) ? footer : {};
60768
- const attribution = source.attribution && typeof source.attribution === "object" && !Array.isArray(source.attribution) ? source.attribution : {};
60769
61482
  return {
60770
61483
  ...source,
60771
61484
  copyright_text: normalizeOptionalString(source.copyright_text),
60772
- attribution: {
60773
- ...attribution,
60774
- enabled: attribution.enabled !== false
60775
- }
61485
+ attribution: source.attribution !== false
60776
61486
  };
60777
61487
  }
60778
- function normalizeWidgetItem(item, mediaBaseUrl) {
61488
+ function normalizeWidgetItem(item, media_base_url) {
60779
61489
  const normalizedItem = {
60780
61490
  ...item,
60781
61491
  title: typeof item?.title === "string" ? item.title.trim() : ""
@@ -60785,7 +61495,7 @@ function normalizeWidgetItem(item, mediaBaseUrl) {
60785
61495
  ...normalizedItem,
60786
61496
  settings: {
60787
61497
  ...item.settings,
60788
- avatar: normalizeMediaField(item.settings.avatar, mediaBaseUrl)
61498
+ avatar: normalizeMediaField(item.settings.avatar, media_base_url)
60789
61499
  }
60790
61500
  };
60791
61501
  }
@@ -60844,19 +61554,19 @@ function normalizeFrontPage(frontPage) {
60844
61554
  ...type === "standalone_html" ? { html: normalizeOptionalRawString(frontPage.html) } : {}
60845
61555
  };
60846
61556
  }
60847
- function normalizePostIndex(postIndex) {
60848
- if (!postIndex || typeof postIndex !== "object") {
61557
+ function normalizePostIndex(post_index) {
61558
+ if (!post_index || typeof post_index !== "object") {
60849
61559
  return { ...DEFAULT_POST_INDEX };
60850
61560
  }
60851
61561
  return {
60852
- enabled: postIndex.enabled !== false,
60853
- path: normalizeNonEmptyString(postIndex.path, DEFAULT_POST_INDEX.path),
60854
- paginate: postIndex.paginate !== false
61562
+ enabled: post_index.enabled !== false,
61563
+ path: normalizeNonEmptyString(post_index.path, DEFAULT_POST_INDEX.path),
61564
+ paginate: post_index.paginate !== false
60855
61565
  };
60856
61566
  }
60857
61567
  function createRenderData(previewData, themeMetadata = {}) {
60858
61568
  const themeSupportsComments = themeMetadata?.features?.comments === true;
60859
- const themeSupportsPostIndex = themeMetadata?.features?.postIndex !== false;
61569
+ const themeSupportsPostIndex = themeMetadata?.features?.post_index !== false;
60860
61570
  const authorsById = new Map(previewData.content.authors.map((author) => [author.id, author]));
60861
61571
  const categoriesBySlug = new Map(previewData.content.categories.map((category) => [category.slug, category]));
60862
61572
  const tagsBySlug = new Map(previewData.content.tags.map((tag) => [tag.slug, tag]));
@@ -60864,7 +61574,8 @@ function createRenderData(previewData, themeMetadata = {}) {
60864
61574
  const tagPostsBySlug = /* @__PURE__ */ new Map();
60865
61575
  const categoryCountBySlug = /* @__PURE__ */ new Map();
60866
61576
  const tagCountBySlug = /* @__PURE__ */ new Map();
60867
- for (const post of previewData.content.posts) {
61577
+ const discoverableSourcePosts = previewData.content.posts.filter((post) => !isDelistedDocument(post));
61578
+ for (const post of discoverableSourcePosts) {
60868
61579
  for (const slug of post.category_slugs) {
60869
61580
  pushToSlugMap(categoryPostsBySlug, slug, post);
60870
61581
  categoryCountBySlug.set(slug, (categoryCountBySlug.get(slug) || 0) + 1);
@@ -60875,45 +61586,54 @@ function createRenderData(previewData, themeMetadata = {}) {
60875
61586
  }
60876
61587
  }
60877
61588
  const preparedPosts = previewData.content.posts.map((post) => preparePost(post, previewData.site, authorsById, categoriesBySlug, tagsBySlug, themeSupportsComments));
60878
- const posts = preparedPosts.map((post, index) => ({
61589
+ const discoverablePreparedPosts = preparedPosts.filter((post) => !isDelistedDocument(post));
61590
+ const adjacentPostsBySlug = new Map(
61591
+ discoverablePreparedPosts.map((post, index) => [post.slug, {
61592
+ prev: index > 0 ? buildAdjacentPostSummary(discoverablePreparedPosts[index - 1]) : null,
61593
+ next: index < discoverablePreparedPosts.length - 1 ? buildAdjacentPostSummary(discoverablePreparedPosts[index + 1]) : null
61594
+ }])
61595
+ );
61596
+ const posts = preparedPosts.map((post) => ({
60879
61597
  ...post,
60880
- prev: index > 0 ? buildAdjacentPostSummary(preparedPosts[index - 1]) : null,
60881
- next: index < preparedPosts.length - 1 ? buildAdjacentPostSummary(preparedPosts[index + 1]) : null
61598
+ prev: adjacentPostsBySlug.get(post.slug)?.prev || null,
61599
+ next: adjacentPostsBySlug.get(post.slug)?.next || null
60882
61600
  }));
60883
61601
  const pages = previewData.content.pages.map((page) => preparePage(page, previewData.site));
60884
61602
  const postBySlug = new Map(posts.map((post) => [post.slug, post]));
60885
61603
  const pageBySlug = new Map(pages.map((page) => [page.slug, page]));
60886
61604
  const frontPage = previewData.site.front_page;
60887
- const postIndex = previewData.site.post_index;
60888
- const effectivePostIndexEnabled = postIndex.enabled !== false && themeSupportsPostIndex;
60889
- const effectivePostIndexPaginate = effectivePostIndexEnabled && postIndex.paginate !== false;
60890
- const postIndexBasePath = normalizeRoutePath(postIndex.path || DEFAULT_POST_INDEX.path);
60891
- if (frontPage.type !== "theme_index" && effectivePostIndexEnabled && postIndexBasePath === "/") {
61605
+ const collections = resolveCollections(previewData.collections, postBySlug, pageBySlug, frontPage);
61606
+ attachCollectionCursors(posts, pages, collections);
61607
+ const post_index = previewData.site.post_index;
61608
+ const effectivePostIndexEnabled = post_index.enabled !== false && themeSupportsPostIndex;
61609
+ const effectivePostIndexPaginate = effectivePostIndexEnabled && post_index.paginate !== false;
61610
+ const post_indexBasePath = normalizeRoutePath(post_index.path || DEFAULT_POST_INDEX.path);
61611
+ if (frontPage.type !== "theme_index" && effectivePostIndexEnabled && post_indexBasePath === "/") {
60892
61612
  throw new Error('Invalid front page configuration: site.front_page occupies "/" so site.post_index.path must not be "/". Set site.post_index.path to a non-root path or disable site.post_index.');
60893
61613
  }
60894
- const frontPageRoute = buildFrontPageRoute(frontPage, pages, effectivePostIndexEnabled, postIndexBasePath);
61614
+ const frontPageRoute = buildFrontPageRoute(frontPage, pages, effectivePostIndexEnabled, post_indexBasePath);
60895
61615
  const pageFrontPageSlug = frontPage.type === "page" ? frontPage.page_slug : "";
60896
61616
  const preparedPages = pageFrontPageSlug ? pages.filter((page) => page.slug !== pageFrontPageSlug) : pages;
60897
61617
  return {
60898
61618
  posts,
60899
61619
  pages: preparedPages,
60900
61620
  postBySlug,
60901
- collections: resolveCollections(previewData.collections, postBySlug, pageBySlug, frontPage),
61621
+ collections,
60902
61622
  taxonomies: buildGlobalTaxonomies(previewData, categoryCountBySlug, tagCountBySlug),
60903
61623
  frontPageRoute,
60904
61624
  indexRoutes: buildPostIndexRoutes({
60905
61625
  enabled: effectivePostIndexEnabled,
60906
61626
  paginate: effectivePostIndexPaginate,
60907
- items: previewData.content.posts,
60908
- postsPerPage: previewData.site.postsPerPage,
60909
- basePath: postIndexBasePath,
61627
+ items: discoverableSourcePosts,
61628
+ posts_per_page: previewData.site.posts_per_page,
61629
+ basePath: post_indexBasePath,
60910
61630
  outputStyle: previewData.site.permalinks.output_style,
60911
61631
  postBySlug,
60912
61632
  frontPage
60913
61633
  }),
60914
61634
  archiveRoutes: buildPaginatedCollection({
60915
- items: previewData.content.posts,
60916
- postsPerPage: previewData.site.postsPerPage,
61635
+ items: discoverableSourcePosts,
61636
+ posts_per_page: previewData.site.posts_per_page,
60917
61637
  basePath: "/archive/",
60918
61638
  outputStyle: previewData.site.permalinks.output_style
60919
61639
  }).map((entry) => ({
@@ -60929,7 +61649,7 @@ function createRenderData(previewData, themeMetadata = {}) {
60929
61649
  items: previewData.content.categories,
60930
61650
  postsBySlug: categoryPostsBySlug,
60931
61651
  postBySlug,
60932
- postsPerPage: previewData.site.postsPerPage,
61652
+ posts_per_page: previewData.site.posts_per_page,
60933
61653
  outputStyle: previewData.site.permalinks.output_style,
60934
61654
  buildBasePath: (category) => resolvePermalink(previewData.site, "categories", category).path,
60935
61655
  renderExtras: (category) => ({
@@ -60940,7 +61660,7 @@ function createRenderData(previewData, themeMetadata = {}) {
60940
61660
  items: previewData.content.tags,
60941
61661
  postsBySlug: tagPostsBySlug,
60942
61662
  postBySlug,
60943
- postsPerPage: previewData.site.postsPerPage,
61663
+ posts_per_page: previewData.site.posts_per_page,
60944
61664
  outputStyle: previewData.site.permalinks.output_style,
60945
61665
  buildBasePath: (tag) => resolvePermalink(previewData.site, "tags", tag).path,
60946
61666
  renderExtras: (tag) => ({
@@ -60960,15 +61680,19 @@ function resolveCollections(collections, postBySlug, pageBySlug, frontPage) {
60960
61680
  return {};
60961
61681
  }
60962
61682
  return Object.fromEntries(
60963
- Object.entries(collections).map(([collectionId, collection]) => [
60964
- collectionId,
60965
- {
60966
- id: collectionId,
60967
- title: normalizeOptionalString(collection?.title),
60968
- description: normalizeOptionalString(collection?.description),
60969
- items: resolveCollectionItems(collectionId, collection?.items, postBySlug, pageBySlug, frontPage)
60970
- }
60971
- ])
61683
+ Object.entries(collections).map(([collectionId, collection]) => {
61684
+ const items = resolveCollectionItems(collectionId, collection?.items, postBySlug, pageBySlug, frontPage);
61685
+ return [
61686
+ collectionId,
61687
+ {
61688
+ id: collectionId,
61689
+ title: normalizeOptionalString(collection?.title),
61690
+ description: normalizeOptionalString(collection?.description),
61691
+ count: items.length,
61692
+ items
61693
+ }
61694
+ ];
61695
+ })
60972
61696
  );
60973
61697
  }
60974
61698
  function resolveCollectionItems(collectionId, items, postBySlug, pageBySlug, frontPage) {
@@ -61007,12 +61731,57 @@ function buildCollectionPageSummary(page, frontPage) {
61007
61731
  excerpt: page.excerpt || "",
61008
61732
  featured_image: page.featured_image || "",
61009
61733
  ...page.featured_media ? { featured_media: { ...page.featured_media } } : {},
61010
- meta: page.meta
61734
+ meta: page.meta,
61735
+ data: page.data
61736
+ };
61737
+ }
61738
+ function attachCollectionCursors(posts, pages, collections) {
61739
+ const postTargets = new Map(posts.map((post) => [post.slug, post]));
61740
+ const pageTargets = new Map(pages.map((page) => [page.slug, page]));
61741
+ for (const [collectionId, collection] of Object.entries(collections || {})) {
61742
+ const items = Array.isArray(collection.items) ? collection.items : [];
61743
+ items.forEach((item, index) => {
61744
+ const target = item.type === "post" ? postTargets.get(item.slug) : item.type === "page" ? pageTargets.get(item.slug) : null;
61745
+ if (!target) {
61746
+ return;
61747
+ }
61748
+ target.collection_cursors = target.collection_cursors || {};
61749
+ target.collection_cursors[collectionId] = buildCollectionCursor(collectionId, collection, items, index);
61750
+ });
61751
+ }
61752
+ }
61753
+ function buildCollectionCursor(collectionId, collection, items, index) {
61754
+ const count = items.length;
61755
+ return {
61756
+ collection_id: collectionId,
61757
+ collection_title: collection.title || "",
61758
+ index,
61759
+ position: index + 1,
61760
+ count,
61761
+ first: index === 0,
61762
+ last: index === count - 1,
61763
+ prev: index > 0 ? buildCollectionCursorItemSummary(items[index - 1]) : null,
61764
+ next: index < count - 1 ? buildCollectionCursorItemSummary(items[index + 1]) : null
61765
+ };
61766
+ }
61767
+ function buildCollectionCursorItemSummary(item) {
61768
+ if (!item) {
61769
+ return null;
61770
+ }
61771
+ return {
61772
+ type: item.type,
61773
+ title: item.title,
61774
+ slug: item.slug,
61775
+ url: item.url,
61776
+ excerpt: item.excerpt || "",
61777
+ featured_image: item.featured_image || "",
61778
+ meta: item.meta,
61779
+ data: item.data
61011
61780
  };
61012
61781
  }
61013
- function buildFrontPageRoute(frontPage, pages, effectivePostIndexEnabled, postIndexBasePath) {
61782
+ function buildFrontPageRoute(frontPage, pages, effectivePostIndexEnabled, post_indexBasePath) {
61014
61783
  if (frontPage.type === "theme_index") {
61015
- if (effectivePostIndexEnabled && postIndexBasePath === "/") {
61784
+ if (effectivePostIndexEnabled && post_indexBasePath === "/") {
61016
61785
  return null;
61017
61786
  }
61018
61787
  return {
@@ -61050,7 +61819,7 @@ function buildPostIndexRoutes(options2) {
61050
61819
  return [];
61051
61820
  }
61052
61821
  if (!options2.paginate) {
61053
- const items = options2.items.slice(0, options2.postsPerPage);
61822
+ const items = options2.items.slice(0, options2.posts_per_page);
61054
61823
  return [{
61055
61824
  path: options2.basePath,
61056
61825
  route_type: "post_index",
@@ -61064,7 +61833,7 @@ function buildPostIndexRoutes(options2) {
61064
61833
  }
61065
61834
  return buildPaginatedCollection({
61066
61835
  items: options2.items,
61067
- postsPerPage: options2.postsPerPage,
61836
+ posts_per_page: options2.posts_per_page,
61068
61837
  basePath: options2.basePath,
61069
61838
  outputStyle: options2.outputStyle
61070
61839
  }).map((entry) => ({
@@ -61143,7 +61912,7 @@ function resolveWidgetItem(item, previewData, renderData, widgetAreaId, index) {
61143
61912
  }
61144
61913
  function resolveRecentPostsWidget(baseWidget, settings, renderData) {
61145
61914
  const limit = clampInteger(settings?.limit, 5, 1, 20);
61146
- const items = renderData.posts.slice(0, limit).map((post) => ({
61915
+ const items = renderData.posts.filter((post) => !isDelistedDocument(post)).slice(0, limit).map((post) => ({
61147
61916
  title: post.title,
61148
61917
  url: `/posts/${encodeSlugSegment(post.slug)}/`,
61149
61918
  published_at: post.published_at,
@@ -61312,7 +62081,9 @@ function preparePost(post, site, authorsById, categoriesBySlug, tagsBySlug, them
61312
62081
  featured_image: post.featured_image,
61313
62082
  ...post.featured_media ? { featured_media: { ...post.featured_media } } : {},
61314
62083
  meta: post.meta,
62084
+ data: post.data,
61315
62085
  status: post.status,
62086
+ discoverability: post.discoverability,
61316
62087
  allow_comments: post.allow_comments,
61317
62088
  category_slugs: post.category_slugs,
61318
62089
  tag_slugs: post.tag_slugs,
@@ -61329,7 +62100,7 @@ function preparePost(post, site, authorsById, categoriesBySlug, tagsBySlug, them
61329
62100
  published_at: formatTimestamp(post.published_at_iso, site),
61330
62101
  updated_at: formatTimestamp(post.updated_at_iso, site),
61331
62102
  reading_time: calculateReadingTime(renderedDocument.html),
61332
- comments_enabled: themeSupportsComments && site.disallowComments !== true && post.allow_comments === true
62103
+ comments_enabled: themeSupportsComments && site.disallow_comments !== true && post.allow_comments === true
61333
62104
  };
61334
62105
  }
61335
62106
  function buildCommentPolicyManifest(posts) {
@@ -61348,7 +62119,7 @@ function buildTaxonomyRoutes(options2) {
61348
62119
  }
61349
62120
  const paginated = buildPaginatedCollection({
61350
62121
  items: matchedPosts,
61351
- postsPerPage: options2.postsPerPage,
62122
+ posts_per_page: options2.posts_per_page,
61352
62123
  basePath: options2.buildBasePath(item),
61353
62124
  outputStyle: options2.outputStyle
61354
62125
  });
@@ -61369,13 +62140,13 @@ function buildTaxonomyRoutes(options2) {
61369
62140
  function buildPaginatedCollection(options2) {
61370
62141
  const basePath = normalizePaginationBasePath(options2.basePath);
61371
62142
  const outputStyle = PERMALINK_OUTPUT_STYLES.has(options2.outputStyle) ? options2.outputStyle : DEFAULT_PERMALINKS.output_style;
61372
- const postsPerPage = Number.isInteger(options2.postsPerPage) && options2.postsPerPage > 0 ? options2.postsPerPage : DEFAULT_POSTS_PER_PAGE;
62143
+ const posts_per_page = Number.isInteger(options2.posts_per_page) && options2.posts_per_page > 0 ? options2.posts_per_page : DEFAULT_POSTS_PER_PAGE;
61373
62144
  const totalPosts = options2.items.length;
61374
- const totalPages = Math.max(1, Math.ceil(totalPosts / postsPerPage));
62145
+ const totalPages = Math.max(1, Math.ceil(totalPosts / posts_per_page));
61375
62146
  const pages = [];
61376
62147
  for (let page = 1; page <= totalPages; page += 1) {
61377
- const start = (page - 1) * postsPerPage;
61378
- const end = start + postsPerPage;
62148
+ const start = (page - 1) * posts_per_page;
62149
+ const end = start + posts_per_page;
61379
62150
  pages.push({
61380
62151
  path: buildPaginatedPath(basePath, page),
61381
62152
  page,
@@ -61523,6 +62294,8 @@ function buildStructuredPostSummary(post) {
61523
62294
  reading_time: post.reading_time,
61524
62295
  featured_image: post.featured_image,
61525
62296
  ...post.featured_media ? { featured_media: { ...post.featured_media } } : {},
62297
+ meta: post.meta,
62298
+ data: post.data,
61526
62299
  author: {
61527
62300
  display_name: post.author?.display_name || "",
61528
62301
  avatar: post.author?.avatar || "",
@@ -61542,7 +62315,8 @@ function buildAdjacentPostSummary(post) {
61542
62315
  url: post.url,
61543
62316
  excerpt: post.excerpt,
61544
62317
  published_at: post.published_at,
61545
- published_at_iso: post.published_at_iso
62318
+ published_at_iso: post.published_at_iso,
62319
+ data: post.data
61546
62320
  };
61547
62321
  }
61548
62322
  function buildArchiveGroups(posts, postBySlug) {
@@ -61615,40 +62389,22 @@ function formatArchiveLabel(date, site) {
61615
62389
  function formatTimestamp(value, site) {
61616
62390
  const date = toDate(value);
61617
62391
  const locale = normalizeLocale(site.locale || DEFAULT_LOCALE);
61618
- const dateFormat = normalizeNonEmptyString(site.dateFormat, DEFAULT_DATE_FORMAT);
61619
- const timeFormat = typeof site.timeFormat === "string" ? site.timeFormat : DEFAULT_TIME_FORMAT;
62392
+ const dateStyle = DATETIME_STYLES.has(site.date_style) ? site.date_style : DEFAULT_DATE_STYLE;
62393
+ const timeStyle = DATETIME_STYLES.has(site.time_style) ? site.time_style : DEFAULT_TIME_STYLE;
61620
62394
  const siteTimezone = normalizeNonEmptyString(site.timezone, DEFAULT_TIMEZONE);
61621
- const dateParts = new Intl.DateTimeFormat(locale, {
61622
- timeZone: siteTimezone,
61623
- year: "numeric",
61624
- month: dateFormat === "MMMM D, YYYY" ? "long" : "2-digit",
61625
- day: "2-digit"
61626
- }).formatToParts(date);
61627
- const year = dateParts.find((part) => part.type === "year")?.value || "";
61628
- const month = dateParts.find((part) => part.type === "month")?.value || "";
61629
- const day = dateParts.find((part) => part.type === "day")?.value || "";
61630
- let formattedDate = `${year}-${month}-${day}`;
61631
- if (dateFormat === "DD/MM/YYYY") {
61632
- formattedDate = `${day}/${month}/${year}`;
61633
- } else if (dateFormat === "MM/DD/YYYY") {
61634
- formattedDate = `${month}/${day}/${year}`;
61635
- } else if (dateFormat === "MMMM D, YYYY") {
61636
- formattedDate = `${month} ${String(Number(day))}, ${year}`;
61637
- }
61638
- if (!timeFormat) {
61639
- return formattedDate;
61640
- }
61641
- const timeParts = new Intl.DateTimeFormat(locale, {
61642
- timeZone: siteTimezone,
61643
- hour: "2-digit",
61644
- minute: "2-digit",
61645
- hour12: timeFormat === "hh:mm A"
61646
- }).formatToParts(date);
61647
- const hour = timeParts.find((part) => part.type === "hour")?.value || "";
61648
- const minute = timeParts.find((part) => part.type === "minute")?.value || "";
61649
- const dayPeriod = (timeParts.find((part) => part.type === "dayPeriod")?.value || "").toUpperCase();
61650
- const formattedTime = timeFormat === "hh:mm A" ? `${hour}:${minute}${dayPeriod ? ` ${dayPeriod}` : ""}` : `${hour}:${minute}`;
61651
- return `${formattedDate} ${formattedTime}`.trim();
62395
+ if (dateStyle === "none" && timeStyle === "none") {
62396
+ return "";
62397
+ }
62398
+ const options2 = {
62399
+ timeZone: siteTimezone
62400
+ };
62401
+ if (dateStyle !== "none") {
62402
+ options2.dateStyle = dateStyle;
62403
+ }
62404
+ if (timeStyle !== "none") {
62405
+ options2.timeStyle = timeStyle;
62406
+ }
62407
+ return new Intl.DateTimeFormat(locale, options2).format(date);
61652
62408
  }
61653
62409
  function calculateReadingTime(html) {
61654
62410
  const plainText = String(html || "").replace(/<[^>]*>/g, " ");
@@ -61729,11 +62485,12 @@ async function normalizeAndValidateThemePackage(themePackage) {
61729
62485
  ...themePackage.metadata.author ? { author: themePackage.metadata.author } : {},
61730
62486
  ...themePackage.metadata.description ? { description: themePackage.metadata.description } : {},
61731
62487
  ...themePackage.metadata.thumbnail ? { thumbnail: themePackage.metadata.thumbnail } : {},
62488
+ ...themePackage.metadata.links ? { links: themePackage.metadata.links } : {},
61732
62489
  ...themePackage.metadata.features ? { features: themePackage.metadata.features } : {},
61733
- ...themePackage.metadata.menuSlots ? { menuSlots: themePackage.metadata.menuSlots } : {},
61734
- ...themePackage.metadata.widgetAreas ? { widgetAreas: themePackage.metadata.widgetAreas } : {},
61735
- ...themePackage.metadata.siteMeta ? { siteMeta: themePackage.metadata.siteMeta } : {},
61736
- ...themePackage.metadata.collectionSlots ? { collectionSlots: themePackage.metadata.collectionSlots } : {}
62490
+ ...themePackage.metadata.menu_slots ? { menu_slots: themePackage.metadata.menu_slots } : {},
62491
+ ...themePackage.metadata.widget_areas ? { widget_areas: themePackage.metadata.widget_areas } : {},
62492
+ ...themePackage.metadata.site_meta ? { site_meta: themePackage.metadata.site_meta } : {},
62493
+ ...themePackage.metadata.collection_slots ? { collection_slots: themePackage.metadata.collection_slots } : {}
61737
62494
  }));
61738
62495
  for (const [templateName, templateContent] of themePackage.templates.entries()) {
61739
62496
  fileMap.set(`${templateName}.html`, templateContent);
@@ -61746,7 +62503,7 @@ async function normalizeAndValidateThemePackage(themePackage) {
61746
62503
  }
61747
62504
  const validation = await validateThemeFiles(fileMap);
61748
62505
  if (!validation.ok) {
61749
- throw new Error(`Theme validation failed: ${validation.errors[0]?.message || "Unknown error"}`);
62506
+ throw new Error(formatThemeValidationFailure(validation));
61750
62507
  }
61751
62508
  if (!validation.manifest) {
61752
62509
  throw new Error("Theme validation failed: normalized manifest not available");
@@ -61758,6 +62515,55 @@ async function normalizeAndValidateThemePackage(themePackage) {
61758
62515
  assets: themePackage.assets
61759
62516
  };
61760
62517
  }
62518
+ function formatThemeValidationFailure(validation) {
62519
+ const blocks = [
62520
+ [
62521
+ "Theme validation failed",
62522
+ `Errors: ${validation.errors.length}`,
62523
+ `Checked files: ${validation.checkedFiles}`
62524
+ ].join("\n"),
62525
+ ...validation.errors.map((issue3) => formatThemeValidationIssue(issue3))
62526
+ ];
62527
+ return blocks.join("\n\n");
62528
+ }
62529
+ function formatThemeValidationIssue(issue3) {
62530
+ if (!issue3) {
62531
+ return "Reason: Unknown error";
62532
+ }
62533
+ const lines = [`ERROR ${issue3.code || "THEME_VALIDATION_ERROR"}`];
62534
+ const location = splitIssuePath(issue3.path);
62535
+ if (location.file) {
62536
+ lines.push(`File: ${location.file}`);
62537
+ }
62538
+ if (location.path) {
62539
+ lines.push(`Path: ${location.path}`);
62540
+ }
62541
+ if (Number.isInteger(issue3.line) && Number.isInteger(issue3.column)) {
62542
+ lines.push(`Line: ${issue3.line}, Column: ${issue3.column}`);
62543
+ }
62544
+ if (issue3.category) {
62545
+ lines.push(`Category: ${issue3.category}`);
62546
+ }
62547
+ lines.push(`Reason: ${issue3.message || "Unknown error"}`);
62548
+ if (issue3.snippet) {
62549
+ const lineLabel = Number.isInteger(issue3.line) ? String(issue3.line) : "";
62550
+ lines.push("", `${lineLabel} | ${issue3.snippet.line}`, `${" ".repeat(lineLabel.length)} | ${issue3.snippet.pointer}`);
62551
+ }
62552
+ if (issue3.hint) {
62553
+ lines.push("", "Hint:", issue3.hint);
62554
+ }
62555
+ return lines.join("\n");
62556
+ }
62557
+ function splitIssuePath(issuePath) {
62558
+ const normalizedPath = String(issuePath || "");
62559
+ if (normalizedPath.startsWith("theme.json.")) {
62560
+ return {
62561
+ file: "theme.json",
62562
+ path: normalizedPath.slice("theme.json.".length)
62563
+ };
62564
+ }
62565
+ return { file: normalizedPath, path: "" };
62566
+ }
61761
62567
  function normalizeThemePackageMetadata(sourceMetadata, manifest) {
61762
62568
  return {
61763
62569
  ...manifest,
@@ -61827,6 +62633,7 @@ function buildPageMeta(site, options2 = {}) {
61827
62633
  const ogType = normalizeNonEmptyString(options2.ogType, "website");
61828
62634
  const publishedTime = normalizeOptionalString(options2.publishedTime);
61829
62635
  const modifiedTime = normalizeOptionalString(options2.modifiedTime);
62636
+ const robotsNoindex = options2.robotsNoindex === true;
61830
62637
  const meta = {
61831
62638
  title: escapeHtml2(resolvedTitle),
61832
62639
  description: resolvedDescription ? escapeHtml2(resolvedDescription) : "",
@@ -61838,7 +62645,8 @@ function buildPageMeta(site, options2 = {}) {
61838
62645
  og_site_name: escapeHtml2(site.title),
61839
62646
  og_image: ogImage ? escapeHtml2(ogImage) : "",
61840
62647
  article_published_time: publishedTime ? escapeHtml2(publishedTime) : "",
61841
- article_modified_time: modifiedTime ? escapeHtml2(modifiedTime) : ""
62648
+ article_modified_time: modifiedTime ? escapeHtml2(modifiedTime) : "",
62649
+ robots_noindex: robotsNoindex
61842
62650
  };
61843
62651
  return {
61844
62652
  ...meta,
@@ -61855,6 +62663,9 @@ function buildMetaHeadTags(meta) {
61855
62663
  if (meta.description) {
61856
62664
  tags.push(`<meta name="description" content="${meta.description}">`);
61857
62665
  }
62666
+ if (meta.robots_noindex) {
62667
+ tags.push('<meta name="robots" content="noindex">');
62668
+ }
61858
62669
  if (meta.canonical_url) {
61859
62670
  tags.push(`<link rel="canonical" href="${meta.canonical_url}">`);
61860
62671
  }
@@ -61895,7 +62706,7 @@ function resolveMetaImageUrl(image2) {
61895
62706
  }
61896
62707
  return "";
61897
62708
  }
61898
- function normalizeMediaField(value, mediaBaseUrl) {
62709
+ function normalizeMediaField(value, media_base_url) {
61899
62710
  if (value === void 0) {
61900
62711
  return void 0;
61901
62712
  }
@@ -61906,7 +62717,7 @@ function normalizeMediaField(value, mediaBaseUrl) {
61906
62717
  if (isAbsoluteUrl(normalizedValue)) {
61907
62718
  return normalizeAbsoluteUrl(normalizedValue, SAFE_MEDIA_PROTOCOLS);
61908
62719
  }
61909
- const normalizedBaseUrl = normalizeOptionalString(mediaBaseUrl);
62720
+ const normalizedBaseUrl = normalizeOptionalString(media_base_url);
61910
62721
  if (!normalizedBaseUrl) {
61911
62722
  return normalizedValue;
61912
62723
  }
@@ -62112,6 +62923,7 @@ function assertPlannedOutputPathsSafe(state) {
62112
62923
  COMMENT_POLICY_OUTPUT_PATH
62113
62924
  ];
62114
62925
  if (state.options.generateSpecialFiles) {
62926
+ plannedPaths.push(SEARCH_INDEX_OUTPUT_PATH, SEARCH_ADAPTER_OUTPUT_PATH);
62115
62927
  plannedPaths.push("404.html");
62116
62928
  if (shouldGenerateRobotsTxt(state.options)) {
62117
62929
  plannedPaths.push("robots.txt");
@@ -62191,6 +63003,7 @@ function sha256(content) {
62191
63003
  }
62192
63004
  function injectSiteCustomizations(html, state) {
62193
63005
  let next = injectFaviconLinks(html, state.favicon);
63006
+ next = injectGeneratorMeta(next, state.exposeGenerator);
62194
63007
  next = injectCustomCssAssetLink(next, state.customCssHref);
62195
63008
  next = injectCustomHtml(next, state.customHtml);
62196
63009
  return next;
@@ -62222,6 +63035,12 @@ function buildFaviconLinks(favicon) {
62222
63035
  }
62223
63036
  return lines.join("\n");
62224
63037
  }
63038
+ function injectGeneratorMeta(html, exposeGenerator) {
63039
+ if (exposeGenerator === false) {
63040
+ return html;
63041
+ }
63042
+ return html.replace("</head>", ' <meta name="generator" content="ZeroPress">\n</head>');
63043
+ }
62225
63044
  function injectCustomCssAssetLink(html, href) {
62226
63045
  if (!normalizeOptionalString(href)) {
62227
63046
  return html;
@@ -62243,9 +63062,383 @@ function injectCustomHtml(html, customHtml) {
62243
63062
  }
62244
63063
  return next;
62245
63064
  }
62246
- function buildSitemapXml(site, emitted, generatedAt) {
63065
+ function buildSearchIndexJson(state) {
63066
+ return `${JSON.stringify(buildSearchIndexItems(state), null, 2)}
63067
+ `;
63068
+ }
63069
+ function buildSearchIndexItems(state) {
63070
+ const posts = state.renderData.posts.filter((post) => post.status === "published" && !isDelistedDocument(post)).map((post) => ({
63071
+ id: `post:${post.slug}`,
63072
+ type: "post",
63073
+ title: post.title,
63074
+ url: post.url,
63075
+ excerpt: normalizeSearchText(post.excerpt),
63076
+ headings: buildSearchHeadings(post.toc),
63077
+ categories: Array.isArray(post.categories) ? post.categories.map((category) => category.name).filter(Boolean) : [],
63078
+ tags: Array.isArray(post.tags) ? post.tags.map((tag) => tag.name).filter(Boolean) : [],
63079
+ published_at_iso: normalizeIsoTimestamp(post.published_at_iso),
63080
+ updated_at_iso: normalizeIsoTimestamp(post.updated_at_iso),
63081
+ content_text: htmlToSearchText(post.html)
63082
+ }));
63083
+ const frontPagePage = state.renderData.frontPageRoute?.front_page_type === "page" ? state.renderData.frontPageRoute.page : null;
63084
+ const frontPageItems = frontPagePage && frontPagePage.status === "published" && !isDelistedDocument(frontPagePage) ? [buildSearchPageItem(frontPagePage, "/")] : [];
63085
+ const pageItems = state.renderData.pages.filter((page) => page.status === "published" && !isDelistedDocument(page)).map((page) => buildSearchPageItem(page, page.url));
63086
+ return [...posts, ...frontPageItems, ...pageItems];
63087
+ }
63088
+ function buildSearchPageItem(page, url) {
63089
+ return {
63090
+ id: `page:${page.slug}`,
63091
+ type: "page",
63092
+ title: page.title,
63093
+ url,
63094
+ excerpt: normalizeSearchText(page.excerpt),
63095
+ headings: buildSearchHeadings(page.toc),
63096
+ categories: [],
63097
+ tags: [],
63098
+ published_at_iso: "",
63099
+ updated_at_iso: "",
63100
+ content_text: htmlToSearchText(page.html)
63101
+ };
63102
+ }
63103
+ function buildSearchHeadings(toc) {
63104
+ return Array.isArray(toc) ? toc.map((item) => normalizeSearchText(item?.title)).filter(Boolean) : [];
63105
+ }
63106
+ function htmlToSearchText(html) {
63107
+ return normalizeSearchText(decodeHtmlEntities(
63108
+ String(html || "").replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<[^>]+>/g, " ")
63109
+ ));
63110
+ }
63111
+ function decodeHtmlEntities(value) {
63112
+ const namedEntities = {
63113
+ amp: "&",
63114
+ lt: "<",
63115
+ gt: ">",
63116
+ quot: '"',
63117
+ apos: "'",
63118
+ nbsp: " "
63119
+ };
63120
+ return String(value || "").replace(/&(#x[0-9a-fA-F]+|#\d+|[a-zA-Z][a-zA-Z0-9]+);/g, (match2, entity2) => {
63121
+ if (entity2.startsWith("#x")) {
63122
+ const codePoint = Number.parseInt(entity2.slice(2), 16);
63123
+ return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match2;
63124
+ }
63125
+ if (entity2.startsWith("#")) {
63126
+ const codePoint = Number.parseInt(entity2.slice(1), 10);
63127
+ return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match2;
63128
+ }
63129
+ return Object.prototype.hasOwnProperty.call(namedEntities, entity2) ? namedEntities[entity2] : match2;
63130
+ });
63131
+ }
63132
+ function normalizeSearchText(value) {
63133
+ return String(value || "").replace(/\s+/g, " ").trim();
63134
+ }
63135
+ function buildSearchAdapterJs() {
63136
+ const fieldWeightsJson = JSON.stringify(SEARCH_FIELD_WEIGHTS, null, 2);
63137
+ return `const FIELD_WEIGHTS = ${fieldWeightsJson};
63138
+ const FIELD_NAMES = Object.keys(FIELD_WEIGHTS);
63139
+ const RECENCY_BOOST_MAX = ${SEARCH_RECENCY_BOOST_MAX};
63140
+ const DEFAULT_LIMIT = 20;
63141
+ const BM25_K1 = 1.2;
63142
+ const BM25_B = 0.75;
63143
+
63144
+ let preparedIndexPromise;
63145
+
63146
+ export async function preload() {
63147
+ await loadPreparedIndex();
63148
+ }
63149
+
63150
+ export async function search(query, options = {}) {
63151
+ const prepared = await loadPreparedIndex();
63152
+ const terms = tokenize(query);
63153
+ const phrase = normalizeText(query);
63154
+ if (terms.length === 0 && !phrase) {
63155
+ return { results: [] };
63156
+ }
63157
+
63158
+ const hits = [];
63159
+ for (const document of prepared.documents) {
63160
+ const score = scoreDocument(document, terms, phrase, prepared);
63161
+ if (score > 0) {
63162
+ hits.push({ document, score });
63163
+ }
63164
+ }
63165
+
63166
+ const limit = normalizeLimit(options.limit);
63167
+ hits.sort((left, right) => right.score - left.score || left.document.raw.title.localeCompare(right.document.raw.title));
63168
+
63169
+ return {
63170
+ results: hits.slice(0, limit).map((hit) => ({
63171
+ id: hit.document.raw.id,
63172
+ score: Number(hit.score.toFixed(6)),
63173
+ data: async () => buildResultData(hit.document.raw, query),
63174
+ })),
63175
+ };
63176
+ }
63177
+
63178
+ async function loadPreparedIndex() {
63179
+ if (!preparedIndexPromise) {
63180
+ preparedIndexPromise = fetch(new URL('./search.json', import.meta.url))
63181
+ .then((response) => {
63182
+ if (!response.ok) {
63183
+ throw new Error('ZeroPress search index not found');
63184
+ }
63185
+ return response.json();
63186
+ })
63187
+ .then((items) => prepareIndex(Array.isArray(items) ? items : []));
63188
+ }
63189
+
63190
+ return preparedIndexPromise;
63191
+ }
63192
+
63193
+ function prepareIndex(items) {
63194
+ const documents = items.map((item) => prepareDocument(item));
63195
+ const documentFrequencies = new Map();
63196
+ const averageLengths = Object.fromEntries(FIELD_NAMES.map((fieldName) => [fieldName, 1]));
63197
+ const newestPostTime = documents.reduce((newest, document) => {
63198
+ if (document.raw.type !== 'post') {
63199
+ return newest;
63200
+ }
63201
+ return Math.max(newest, document.publishedTime || 0);
63202
+ }, 0);
63203
+
63204
+ for (const document of documents) {
63205
+ const seenTerms = new Set();
63206
+ for (const fieldName of FIELD_NAMES) {
63207
+ for (const term of document.fieldTokens[fieldName]) {
63208
+ seenTerms.add(term);
63209
+ }
63210
+ }
63211
+ for (const term of seenTerms) {
63212
+ documentFrequencies.set(term, (documentFrequencies.get(term) || 0) + 1);
63213
+ }
63214
+ }
63215
+
63216
+ for (const fieldName of FIELD_NAMES) {
63217
+ const total = documents.reduce((sum, document) => sum + document.fieldLengths[fieldName], 0);
63218
+ averageLengths[fieldName] = documents.length > 0 ? Math.max(1, total / documents.length) : 1;
63219
+ }
63220
+
63221
+ return {
63222
+ documents,
63223
+ documentFrequencies,
63224
+ averageLengths,
63225
+ documentCount: documents.length,
63226
+ newestPostTime,
63227
+ };
63228
+ }
63229
+
63230
+ function prepareDocument(item) {
63231
+ const raw = normalizeItem(item);
63232
+ const fields = {
63233
+ title: raw.title,
63234
+ headings: raw.headings.join(' '),
63235
+ tags: raw.tags.join(' '),
63236
+ categories: raw.categories.join(' '),
63237
+ excerpt: raw.excerpt,
63238
+ content_text: raw.content_text,
63239
+ };
63240
+ const fieldTexts = {};
63241
+ const fieldTokens = {};
63242
+ const fieldTermCounts = {};
63243
+ const fieldLengths = {};
63244
+
63245
+ for (const [fieldName, value] of Object.entries(fields)) {
63246
+ const normalizedText = normalizeText(value);
63247
+ const tokens = tokenize(normalizedText);
63248
+ fieldTexts[fieldName] = normalizedText;
63249
+ fieldTokens[fieldName] = tokens;
63250
+ fieldTermCounts[fieldName] = countTerms(tokens);
63251
+ fieldLengths[fieldName] = Math.max(1, tokens.length);
63252
+ }
63253
+
63254
+ return {
63255
+ raw,
63256
+ fieldTexts,
63257
+ fieldTokens,
63258
+ fieldTermCounts,
63259
+ fieldLengths,
63260
+ publishedTime: Date.parse(raw.published_at_iso) || 0,
63261
+ };
63262
+ }
63263
+
63264
+ function normalizeItem(item) {
63265
+ return {
63266
+ id: String(item && item.id || ''),
63267
+ type: item && item.type === 'page' ? 'page' : 'post',
63268
+ title: String(item && item.title || ''),
63269
+ url: String(item && item.url || ''),
63270
+ excerpt: String(item && item.excerpt || ''),
63271
+ headings: Array.isArray(item && item.headings) ? item.headings.map(String) : [],
63272
+ categories: Array.isArray(item && item.categories) ? item.categories.map(String) : [],
63273
+ tags: Array.isArray(item && item.tags) ? item.tags.map(String) : [],
63274
+ published_at_iso: String(item && item.published_at_iso || ''),
63275
+ updated_at_iso: String(item && item.updated_at_iso || ''),
63276
+ content_text: String(item && item.content_text || ''),
63277
+ };
63278
+ }
63279
+
63280
+ function scoreDocument(document, terms, phrase, prepared) {
63281
+ let score = 0;
63282
+ const uniqueTerms = Array.from(new Set(terms));
63283
+
63284
+ for (const term of uniqueTerms) {
63285
+ const documentFrequency = prepared.documentFrequencies.get(term) || 0;
63286
+ const idf = Math.log(1 + (prepared.documentCount - documentFrequency + 0.5) / (documentFrequency + 0.5));
63287
+
63288
+ for (const fieldName of FIELD_NAMES) {
63289
+ const termFrequency = document.fieldTermCounts[fieldName].get(term) || 0;
63290
+ if (termFrequency === 0) {
63291
+ continue;
63292
+ }
63293
+
63294
+ const fieldLength = document.fieldLengths[fieldName];
63295
+ const averageLength = prepared.averageLengths[fieldName];
63296
+ const denominator = termFrequency + BM25_K1 * (1 - BM25_B + BM25_B * (fieldLength / averageLength));
63297
+ score += FIELD_WEIGHTS[fieldName] * idf * ((termFrequency * (BM25_K1 + 1)) / denominator);
63298
+ }
63299
+ }
63300
+
63301
+ if (phrase && phrase.length > 1) {
63302
+ for (const fieldName of FIELD_NAMES) {
63303
+ if (document.fieldTexts[fieldName].includes(phrase)) {
63304
+ score += FIELD_WEIGHTS[fieldName] * 0.6;
63305
+ }
63306
+ }
63307
+ }
63308
+
63309
+ if (score <= 0) {
63310
+ return 0;
63311
+ }
63312
+
63313
+ return score * (1 + recencyBoost(document, prepared.newestPostTime));
63314
+ }
63315
+
63316
+ function recencyBoost(document, newestPostTime) {
63317
+ if (document.raw.type !== 'post' || !document.publishedTime || !newestPostTime) {
63318
+ return 0;
63319
+ }
63320
+ const ageMs = Math.max(0, newestPostTime - document.publishedTime);
63321
+ const halfLifeMs = 180 * 24 * 60 * 60 * 1000;
63322
+ return RECENCY_BOOST_MAX * Math.exp(-ageMs / halfLifeMs);
63323
+ }
63324
+
63325
+ function buildResultData(item, query) {
63326
+ const excerpt = buildExcerpt(item, query);
63327
+ return {
63328
+ url: item.url,
63329
+ excerpt,
63330
+ plain_excerpt: excerpt,
63331
+ meta: {
63332
+ title: item.title,
63333
+ type: item.type,
63334
+ published_at_iso: item.published_at_iso,
63335
+ updated_at_iso: item.updated_at_iso,
63336
+ categories: item.categories,
63337
+ tags: item.tags,
63338
+ },
63339
+ sub_results: [],
63340
+ };
63341
+ }
63342
+
63343
+ function buildExcerpt(item, query) {
63344
+ const explicitExcerpt = String(item.excerpt || '').trim();
63345
+ if (explicitExcerpt) {
63346
+ return explicitExcerpt;
63347
+ }
63348
+
63349
+ const text = String(item.content_text || '').replace(/\\s+/g, ' ').trim();
63350
+ if (!text) {
63351
+ return '';
63352
+ }
63353
+
63354
+ const normalizedText = normalizeText(text);
63355
+ const terms = tokenize(query);
63356
+ const firstMatch = terms.map((term) => normalizedText.indexOf(term)).filter((index) => index >= 0).sort((a, b) => a - b)[0];
63357
+ const start = Math.max(0, (firstMatch || 0) - 80);
63358
+ const end = Math.min(text.length, start + 180);
63359
+ const prefix = start > 0 ? '...' : '';
63360
+ const suffix = end < text.length ? '...' : '';
63361
+ return prefix + text.slice(start, end).trim() + suffix;
63362
+ }
63363
+
63364
+ function countTerms(tokens) {
63365
+ const counts = new Map();
63366
+ for (const token of tokens) {
63367
+ counts.set(token, (counts.get(token) || 0) + 1);
63368
+ }
63369
+ return counts;
63370
+ }
63371
+
63372
+ function tokenize(value) {
63373
+ const text = normalizeText(value);
63374
+ if (!text) {
63375
+ return [];
63376
+ }
63377
+
63378
+ const tokens = [];
63379
+ if (typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function') {
63380
+ try {
63381
+ const segmenter = new Intl.Segmenter(undefined, { granularity: 'word' });
63382
+ for (const part of segmenter.segment(text)) {
63383
+ if (part.isWordLike && isUsefulToken(part.segment)) {
63384
+ tokens.push(part.segment);
63385
+ }
63386
+ }
63387
+ } catch {
63388
+ // Fall through to regex tokenization.
63389
+ }
63390
+ }
63391
+
63392
+ for (const match of text.matchAll(/[\\p{Letter}\\p{Number}]+/gu)) {
63393
+ if (isUsefulToken(match[0])) {
63394
+ tokens.push(match[0]);
63395
+ }
63396
+ }
63397
+
63398
+ for (const match of text.matchAll(/[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]+/gu)) {
63399
+ tokens.push(...buildNgrams(match[0], 2));
63400
+ }
63401
+
63402
+ return tokens;
63403
+ }
63404
+
63405
+ function buildNgrams(value, size) {
63406
+ const normalized = Array.from(value);
63407
+ if (normalized.length <= size) {
63408
+ return isUsefulToken(value) ? [value] : [];
63409
+ }
63410
+
63411
+ const tokens = [];
63412
+ for (let index = 0; index <= normalized.length - size; index += 1) {
63413
+ tokens.push(normalized.slice(index, index + size).join(''));
63414
+ }
63415
+ tokens.push(value);
63416
+ return tokens;
63417
+ }
63418
+
63419
+ function isUsefulToken(value) {
63420
+ const token = String(value || '').trim();
63421
+ return token.length > 1 || /^\\d$/.test(token);
63422
+ }
63423
+
63424
+ function normalizeText(value) {
63425
+ return String(value || '')
63426
+ .normalize('NFKC')
63427
+ .toLowerCase()
63428
+ .replace(/[\\u2018\\u2019]/g, "'")
63429
+ .replace(/[_-]+/g, ' ')
63430
+ .replace(/\\s+/g, ' ')
63431
+ .trim();
63432
+ }
63433
+
63434
+ function normalizeLimit(value) {
63435
+ return Number.isInteger(value) && value > 0 ? Math.min(value, 100) : DEFAULT_LIMIT;
63436
+ }
63437
+ `;
63438
+ }
63439
+ function buildSitemapXml(site, emitted, generatedAt, stylesheetHref = "") {
62247
63440
  const entries = [
62248
- ...emitted.frontPage ? [{
63441
+ ...emitted.frontPage && emitted.frontPage.includeInSitemap !== false ? [{
62249
63442
  url: emitted.frontPage.url,
62250
63443
  changefreq: "daily",
62251
63444
  priority: 1
@@ -62277,7 +63470,10 @@ function buildSitemapXml(site, emitted, generatedAt) {
62277
63470
  <priority>${entry.priority.toFixed(1)}</priority>
62278
63471
  </url>`;
62279
63472
  }).join("\n");
62280
- return `<?xml version="1.0" encoding="UTF-8"?>
63473
+ const normalizedStylesheetHref = normalizeOptionalString(stylesheetHref);
63474
+ const stylesheet = normalizedStylesheetHref ? `
63475
+ <?xml-stylesheet type="text/xsl" href="${escapeXml(normalizedStylesheetHref)}"?>` : "";
63476
+ return `<?xml version="1.0" encoding="UTF-8"?>${stylesheet}
62281
63477
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
62282
63478
  ${body}
62283
63479
  </urlset>`;
@@ -62400,7 +63596,7 @@ async function loadThemePackageFromDir(themeDir) {
62400
63596
  await readThemeDir(fs4, path4, themeDir, themeDir, fileMap);
62401
63597
  const validation = await validateThemeFiles(fileMap);
62402
63598
  if (!validation.ok) {
62403
- throw new Error(`Theme validation failed: ${validation.errors[0]?.message || "Unknown error"}`);
63599
+ throw new Error(formatThemeValidationFailure2(validation));
62404
63600
  }
62405
63601
  const rawThemeJson = String(fileMap.get("theme.json"));
62406
63602
  const themeJson = JSON.parse(rawThemeJson);
@@ -62440,6 +63636,55 @@ async function loadThemePackageFromDir(themeDir) {
62440
63636
  assets
62441
63637
  };
62442
63638
  }
63639
+ function formatThemeValidationFailure2(validation) {
63640
+ const blocks = [
63641
+ [
63642
+ "Theme validation failed",
63643
+ `Errors: ${validation.errors.length}`,
63644
+ `Checked files: ${validation.checkedFiles}`
63645
+ ].join("\n"),
63646
+ ...validation.errors.map((issue3) => formatThemeValidationIssue2(issue3))
63647
+ ];
63648
+ return blocks.join("\n\n");
63649
+ }
63650
+ function formatThemeValidationIssue2(issue3) {
63651
+ if (!issue3) {
63652
+ return "Reason: Unknown error";
63653
+ }
63654
+ const lines = [`ERROR ${issue3.code || "THEME_VALIDATION_ERROR"}`];
63655
+ const location = splitIssuePath2(issue3.path);
63656
+ if (location.file) {
63657
+ lines.push(`File: ${location.file}`);
63658
+ }
63659
+ if (location.path) {
63660
+ lines.push(`Path: ${location.path}`);
63661
+ }
63662
+ if (Number.isInteger(issue3.line) && Number.isInteger(issue3.column)) {
63663
+ lines.push(`Line: ${issue3.line}, Column: ${issue3.column}`);
63664
+ }
63665
+ if (issue3.category) {
63666
+ lines.push(`Category: ${issue3.category}`);
63667
+ }
63668
+ lines.push(`Reason: ${issue3.message || "Unknown error"}`);
63669
+ if (issue3.snippet) {
63670
+ const lineLabel = Number.isInteger(issue3.line) ? String(issue3.line) : "";
63671
+ lines.push("", `${lineLabel} | ${issue3.snippet.line}`, `${" ".repeat(lineLabel.length)} | ${issue3.snippet.pointer}`);
63672
+ }
63673
+ if (issue3.hint) {
63674
+ lines.push("", "Hint:", issue3.hint);
63675
+ }
63676
+ return lines.join("\n");
63677
+ }
63678
+ function splitIssuePath2(issuePath) {
63679
+ const normalizedPath = String(issuePath || "");
63680
+ if (normalizedPath.startsWith("theme.json.")) {
63681
+ return {
63682
+ file: "theme.json",
63683
+ path: normalizedPath.slice("theme.json.".length)
63684
+ };
63685
+ }
63686
+ return { file: normalizedPath, path: "" };
63687
+ }
62443
63688
  async function readThemeDir(fs4, path4, rootDir, currentDir, fileMap) {
62444
63689
  const entries = await fs4.readdir(currentDir, { withFileTypes: true });
62445
63690
  for (const entry of entries) {
@@ -62486,6 +63731,7 @@ var PUBLIC_FAVICON_FILES = Object.freeze({
62486
63731
  png: "favicon.png",
62487
63732
  apple_touch_icon: "apple-touch-icon.png"
62488
63733
  });
63734
+ var PUBLIC_SITEMAP_STYLESHEET_FILE = "sitemap.xsl";
62489
63735
  async function assertThemeDirectory(themeDir) {
62490
63736
  let stat;
62491
63737
  try {
@@ -62500,14 +63746,15 @@ async function assertThemeDirectory(themeDir) {
62500
63746
  throw new Error(`Theme path is not a directory: ${themeDir}`);
62501
63747
  }
62502
63748
  }
62503
- async function runBuild(themeDir, previewData, outDir) {
62504
- assertPublicPathDoesNotOverlap("Theme directory", themeDir);
62505
- assertPublicPathDoesNotOverlap("Output directory", outDir);
63749
+ async function runBuild(themeDir, previewData, outDir, options2 = {}) {
63750
+ const publicDir = resolvePublicDir(process.cwd(), options2.publicDir);
63751
+ assertPublicPathDoesNotOverlap("Theme directory", themeDir, process.cwd(), publicDir);
63752
+ assertPublicPathDoesNotOverlap("Output directory", outDir, process.cwd(), publicDir);
62506
63753
  await assertThemeDirectory(themeDir);
62507
63754
  await assertEmptyOutputDirectory(outDir);
62508
- const publicDir = resolvePublicDir();
62509
63755
  const hasPublicRobotsTxt = await publicRobotsTxtExists(publicDir);
62510
63756
  const publicFavicon = await discoverPublicFavicon(publicDir);
63757
+ const sitemapStylesheetHref = await discoverPublicSitemapStylesheet(publicDir);
62511
63758
  await copyPublicDirectory(publicDir, outDir);
62512
63759
  const writer = new GeneratedOutputWriter({ outDir });
62513
63760
  return buildSiteFromThemeDir({
@@ -62516,6 +63763,7 @@ async function runBuild(themeDir, previewData, outDir) {
62516
63763
  writer,
62517
63764
  options: {
62518
63765
  favicon: publicFavicon,
63766
+ sitemapStylesheetHref,
62519
63767
  generateRobotsTxt: !hasPublicRobotsTxt
62520
63768
  }
62521
63769
  });
@@ -62555,7 +63803,10 @@ async function assertEmptyOutputDirectory(outDir) {
62555
63803
  throw error;
62556
63804
  }
62557
63805
  }
62558
- function resolvePublicDir(cwd = process.cwd()) {
63806
+ function resolvePublicDir(cwd = process.cwd(), publicDir) {
63807
+ if (publicDir) {
63808
+ return path.resolve(cwd, publicDir);
63809
+ }
62559
63810
  const envValue = process.env[PUBLIC_DIR_ENV_NAME]?.trim();
62560
63811
  return path.resolve(cwd, envValue || DEFAULT_PUBLIC_DIR_NAME);
62561
63812
  }
@@ -62604,6 +63855,18 @@ async function discoverPublicFavicon(publicDir) {
62604
63855
  }
62605
63856
  return Object.keys(favicon).length ? favicon : void 0;
62606
63857
  }
63858
+ async function discoverPublicSitemapStylesheet(publicDir) {
63859
+ let stat;
63860
+ try {
63861
+ stat = await fs.lstat(path.join(publicDir, PUBLIC_SITEMAP_STYLESHEET_FILE));
63862
+ } catch (error) {
63863
+ if (error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
63864
+ return void 0;
63865
+ }
63866
+ throw error;
63867
+ }
63868
+ return stat.isFile() ? `/${PUBLIC_SITEMAP_STYLESHEET_FILE}` : void 0;
63869
+ }
62607
63870
  async function copyPublicEntries(sourceDir, targetDir) {
62608
63871
  const entries = await fs.readdir(sourceDir, { withFileTypes: true });
62609
63872
  for (const entry of entries) {
@@ -62628,8 +63891,7 @@ function shouldIgnorePublicEntry(name) {
62628
63891
  const lowerName = basename.toLowerCase();
62629
63892
  return basename.startsWith(".") || lowerName === "node_modules" || lowerName === "thumbs.db" || lowerName.endsWith(".key") || lowerName.endsWith(".pem");
62630
63893
  }
62631
- function assertPublicPathDoesNotOverlap(label, candidatePath, cwd = process.cwd()) {
62632
- const publicDir = resolvePublicDir(cwd);
63894
+ function assertPublicPathDoesNotOverlap(label, candidatePath, cwd = process.cwd(), publicDir = resolvePublicDir(cwd)) {
62633
63895
  const resolvedCandidate = path.resolve(cwd, candidatePath);
62634
63896
  if (!pathsOverlap(publicDir, resolvedCandidate)) {
62635
63897
  return;
@@ -62940,7 +64202,7 @@ try {
62940
64202
  process.exitCode = 1;
62941
64203
  }
62942
64204
  function input(name) {
62943
- return process.env[`INPUT_${name.toUpperCase().replace(/-/g, "_")}`]?.trim() || "";
64205
+ return process.env[`INPUT_${name.toUpperCase()}`]?.trim() || "";
62944
64206
  }
62945
64207
  function booleanInput(name, fallback) {
62946
64208
  const value = input(name);