@weatherboard/cms 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,12 +19,21 @@ The additive fields in 0.2.0 are:
19
19
  including when the local fallback selects rows.
20
20
  - `taglines`: `{ key: string, text: string }[]`. Keys are site-defined lowercase
21
21
  identifiers, 1–32 characters, starting with a letter; remaining characters
22
- may be letters, digits, short dashes or underscores. Duplicate keys refuse
23
- the whole field. Text uses the heading copy rules and is limited to
24
- `SITE_HERO_TAGLINE_MAX_LENGTH` (provisionally 36 Unicode code points).
22
+ may be letters, digits, short dashes or underscores. Text uses the heading
23
+ copy rules and is limited to `SITE_HERO_TAGLINE_MAX_LENGTH` (40 Unicode code
24
+ points). A tagline with at least two letters and no lowercase letter is
25
+ refused: `WARM HOMES ALL YEAR` fails; `HVAC for every room` and `A` pass.
25
26
  An empty array deliberately clears taglines; the site can continue using
26
- heading. A missing or invalid array uses the reviewed local array, or `[]`
27
- for a legacy fallback.
27
+ heading. A missing or non-list value uses the reviewed local array, or `[]`
28
+ for a legacy fallback. In a published list, invalid entries are dropped and
29
+ every valid sibling is kept, in order. The first valid occurrence of a key
30
+ wins; later duplicates are dropped. An invalid first occurrence does not
31
+ reserve its key. If no entry is valid, the parsed list is `[]`.
32
+
33
+ The 40-character limit is a CMS **safety cap**, not a two-line fit guarantee.
34
+ Each site enforces its own measured two-line limit in its own font (one Figtree
35
+ site measured 27 characters). The dashboard's separate live fit check validates
36
+ the rendered result with the site's font and layout.
28
37
 
29
38
  `SiteHero` accepts existing heading/intro-only fallbacks. `parseSiteHero` returns
30
39
  normalized `ParsedSiteHero` data with both new fields present. Invalid CMS fields
@@ -38,6 +47,13 @@ A defaulted CMS layout records `SITE_HERO_LAYOUT_WARNING`, whose value is
38
47
  Supply `onDiagnostic` to log diagnostics; without it the reader logs safe JSON
39
48
  through `console.warn`. Diagnostics contain paths and warning names, never copy.
40
49
 
50
+ Tagline drops record `SITE_HERO_TAGLINE_DROPPED_WARNING`
51
+ (`siteHero.tagline.dropped`) and indexed `fallbackFields`, such as `taglines[1]`.
52
+ A non-list or missing field records `SITE_HERO_TAGLINES_DEFAULTED_WARNING`
53
+ (`siteHero.taglines.defaulted`) and `taglines` in `fallbackFields`. These warnings
54
+ travel through the same safe reader diagnostics. Authoring/schema validation and
55
+ local fallback configuration remain strict: any invalid entry refuses the list.
56
+
41
57
  ```ts
42
58
  import { parseSiteHero } from '@weatherboard/cms/site-content';
43
59
 
@@ -50,6 +66,57 @@ const resolved = parseSiteHero({
50
66
  }, fallback);
51
67
  ```
52
68
 
69
+ ## Page sections (0.3.0)
70
+
71
+ `parseContentPage` (and `contentPageQuery`, `contentPagesQuery`) read the
72
+ optional `page.sections[]` that `@weatherboard/sanity-schema-base` declares
73
+ (A-1889). Each entry maps to a `kind`, listed in `READER_SECTION_KINDS`:
74
+
75
+ | Sanity `_type` | `kind` | composition | default layout |
76
+ | --- | --- | --- | --- |
77
+ | `featureStoriesBlock`, `surfaceCardBlock`, `accentBandBlock`, `locationsBlock` | `feature_stories`, `cta`, `accent_band`, `locations` | none | none |
78
+ | `textMediaBlock` | `text_media` | Split | `media-end` |
79
+ | `faqBlock` (items `faqItem`) | `faq` | HeadingList | `beside` |
80
+ | `galleryBlock` | `gallery` | StaggerRow | `staggered` |
81
+ | `contactBlock` | `contact` | Split | `media-end` |
82
+
83
+ The four home kinds read exactly as they do on the home page. The new kinds
84
+ return every key, `null` where there is nothing, with images as
85
+ `SectionImageInput` (`asset` id, `alt`, `caption`, `hotspot` `{ x, y }`).
86
+
87
+ The layout values are the A-1876 variant names, exported as `SPLIT_LAYOUTS`
88
+ (`media-end`, `media-start`, `media-below`), `HEADING_LIST_LAYOUTS` (`beside`,
89
+ `stacked`) and `STAGGER_ROW_LAYOUTS` (`staggered`, `aligned`), with the defaults
90
+ in `SECTION_LAYOUT_DEFAULTS`. A later contract test can compare them with the UI
91
+ package's lists.
92
+
93
+ `PageContent.sections` and `PageContent.warnings` are present only when the
94
+ stored page has a `sections` list, so a page without one reads exactly as it did
95
+ in 0.2.0. Each warning is `{ code, where }`, a code from `SECTION_WARNINGS` and
96
+ the path in the stored document, never copy:
97
+
98
+ - `section.layout.defaulted`: missing or unknown `layout`; the composition
99
+ default renders. It never throws and never yields an empty section.
100
+ - `section.type.unknown`: a section `_type` this reader does not know is
101
+ skipped; the rest of the page reads.
102
+ - `section.image.missing-alt`: an image without alt text is dropped, never
103
+ returned without it. `text_media.image` is then `null`; a gallery keeps its
104
+ other photos.
105
+ - `section.image.missing-asset`: an image with no uploaded asset is dropped.
106
+ - `section.empty.skipped`: a gallery whose every photo was dropped, or a contact
107
+ section with email, phone, text and button all off, is skipped.
108
+
109
+ Everything else stays a named refusal, as for the rest of the reader: missing
110
+ required copy, a FAQ with no questions, a gallery outside 2 to 4 photos, a
111
+ repeated jump link or a button jumping to a section that is not on the page
112
+ throws `MalformedContentError` naming the path. Page fallbacks that carry
113
+ `sections` are validated against the same kinds.
114
+
115
+ The warnings travel in the page data, not in the server reader's
116
+ `CmsResolution.warnings`, which names the hero layout and tagline warnings.
117
+
118
+ 0.3.0 is an additive minor under the pilot semver policy: a new reader kind.
119
+
53
120
  ## Schema and release delivery
54
121
 
55
122
  `@weatherboard/sanity-schema-base` derives the hero schema from these rules:
@@ -69,3 +136,6 @@ authorized content write; existing documents remain readable with defaults.
69
136
  merge through the registered release workflow; clients then need an exact package
70
137
  bump and deploy. The CI publish and trusted-publisher proof are release evidence
71
138
  that this source change alone cannot supply.
139
+
140
+ 0.4.0 raises the tagline safety cap to 40, refuses all-caps taglines and preserves
141
+ valid siblings in published lists. It is one minor above the npm 0.3.0 release.
@@ -7,7 +7,7 @@ export type CmsDependencies = {
7
7
  export type CmsResolution<T> = {
8
8
  readonly data: T;
9
9
  readonly fallbackFields: readonly string[];
10
- readonly warnings?: readonly "siteHero.layout.defaulted"[];
10
+ readonly warnings?: readonly ("siteHero.layout.defaulted" | "siteHero.tagline.dropped" | "siteHero.taglines.defaulted")[];
11
11
  };
12
12
  export type CmsQuery<T> = {
13
13
  readonly name: string;
@@ -7,7 +7,7 @@ export type CmsDependencies = {
7
7
  export type CmsResolution<T> = {
8
8
  readonly data: T;
9
9
  readonly fallbackFields: readonly string[];
10
- readonly warnings?: readonly "siteHero.layout.defaulted"[];
10
+ readonly warnings?: readonly ("siteHero.layout.defaulted" | "siteHero.tagline.dropped" | "siteHero.taglines.defaulted")[];
11
11
  };
12
12
  export type CmsQuery<T> = {
13
13
  readonly name: string;
@@ -7,7 +7,7 @@ export type CmsDependencies = {
7
7
  export type CmsResolution<T> = {
8
8
  readonly data: T;
9
9
  readonly fallbackFields: readonly string[];
10
- readonly warnings?: readonly "siteHero.layout.defaulted"[];
10
+ readonly warnings?: readonly ("siteHero.layout.defaulted" | "siteHero.tagline.dropped" | "siteHero.taglines.defaulted")[];
11
11
  };
12
12
  export type CmsQuery<T> = {
13
13
  readonly name: string;
@@ -1,7 +1,9 @@
1
1
  import { type CmsQuery, type CmsResolution } from "./query.cjs";
2
2
  export declare const SITE_HERO_LAYOUTS: readonly ["columns", "rows"];
3
- export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 36;
3
+ export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 40;
4
4
  export declare const SITE_HERO_LAYOUT_WARNING: "siteHero.layout.defaulted";
5
+ export declare const SITE_HERO_TAGLINE_DROPPED_WARNING: "siteHero.tagline.dropped";
6
+ export declare const SITE_HERO_TAGLINES_DEFAULTED_WARNING: "siteHero.taglines.defaulted";
5
7
  export type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];
6
8
  export type SiteHeroTagline = {
7
9
  readonly key: string;
@@ -52,7 +54,7 @@ export type TextValidation = {
52
54
  export declare const CMS_CONTRACT_VERSION = 1;
53
55
  export declare const SITE_HERO: TextDocumentDefinition;
54
56
  export declare function validateTextDocument(definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>): TextValidation;
55
- /** Shared with the schema adapter; no provider metadata is returned. */
57
+ /** Shared with the schema adapter; invalid authoring input is refused as a whole. */
56
58
  export declare function validateSiteHeroTaglines(raw: unknown): {
57
59
  readonly ok: true;
58
60
  readonly value: readonly SiteHeroTagline[];
@@ -60,6 +62,6 @@ export declare function validateSiteHeroTaglines(raw: unknown): {
60
62
  readonly ok: false;
61
63
  readonly error: string;
62
64
  };
63
- /** Strict per-field resolution: an invalid list never partially changes keyed copy. */
65
+ /** Invalid published entries are dropped without discarding valid keyed siblings. */
64
66
  export declare function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero>;
65
67
  export declare function siteHeroQuery(): CmsQuery<SiteHero>;
@@ -1,7 +1,9 @@
1
1
  import { type CmsQuery, type CmsResolution } from "./query.mjs";
2
2
  export declare const SITE_HERO_LAYOUTS: readonly ["columns", "rows"];
3
- export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 36;
3
+ export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 40;
4
4
  export declare const SITE_HERO_LAYOUT_WARNING: "siteHero.layout.defaulted";
5
+ export declare const SITE_HERO_TAGLINE_DROPPED_WARNING: "siteHero.tagline.dropped";
6
+ export declare const SITE_HERO_TAGLINES_DEFAULTED_WARNING: "siteHero.taglines.defaulted";
5
7
  export type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];
6
8
  export type SiteHeroTagline = {
7
9
  readonly key: string;
@@ -52,7 +54,7 @@ export type TextValidation = {
52
54
  export declare const CMS_CONTRACT_VERSION = 1;
53
55
  export declare const SITE_HERO: TextDocumentDefinition;
54
56
  export declare function validateTextDocument(definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>): TextValidation;
55
- /** Shared with the schema adapter; no provider metadata is returned. */
57
+ /** Shared with the schema adapter; invalid authoring input is refused as a whole. */
56
58
  export declare function validateSiteHeroTaglines(raw: unknown): {
57
59
  readonly ok: true;
58
60
  readonly value: readonly SiteHeroTagline[];
@@ -60,6 +62,6 @@ export declare function validateSiteHeroTaglines(raw: unknown): {
60
62
  readonly ok: false;
61
63
  readonly error: string;
62
64
  };
63
- /** Strict per-field resolution: an invalid list never partially changes keyed copy. */
65
+ /** Invalid published entries are dropped without discarding valid keyed siblings. */
64
66
  export declare function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero>;
65
67
  export declare function siteHeroQuery(): CmsQuery<SiteHero>;
@@ -1,7 +1,9 @@
1
1
  import { type CmsQuery, type CmsResolution } from "./query.js";
2
2
  export declare const SITE_HERO_LAYOUTS: readonly ["columns", "rows"];
3
- export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 36;
3
+ export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 40;
4
4
  export declare const SITE_HERO_LAYOUT_WARNING: "siteHero.layout.defaulted";
5
+ export declare const SITE_HERO_TAGLINE_DROPPED_WARNING: "siteHero.tagline.dropped";
6
+ export declare const SITE_HERO_TAGLINES_DEFAULTED_WARNING: "siteHero.taglines.defaulted";
5
7
  export type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];
6
8
  export type SiteHeroTagline = {
7
9
  readonly key: string;
@@ -52,7 +54,7 @@ export type TextValidation = {
52
54
  export declare const CMS_CONTRACT_VERSION = 1;
53
55
  export declare const SITE_HERO: TextDocumentDefinition;
54
56
  export declare function validateTextDocument(definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>): TextValidation;
55
- /** Shared with the schema adapter; no provider metadata is returned. */
57
+ /** Shared with the schema adapter; invalid authoring input is refused as a whole. */
56
58
  export declare function validateSiteHeroTaglines(raw: unknown): {
57
59
  readonly ok: true;
58
60
  readonly value: readonly SiteHeroTagline[];
@@ -60,6 +62,6 @@ export declare function validateSiteHeroTaglines(raw: unknown): {
60
62
  readonly ok: false;
61
63
  readonly error: string;
62
64
  };
63
- /** Strict per-field resolution: an invalid list never partially changes keyed copy. */
65
+ /** Invalid published entries are dropped without discarding valid keyed siblings. */
64
66
  export declare function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero>;
65
67
  export declare function siteHeroQuery(): CmsQuery<SiteHero>;
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var f=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var _=(e,t)=>{for(var o in t)f(e,o,{get:t[o],enumerable:!0})},A=(e,t,o,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of R(t))!v.call(e,n)&&n!==o&&f(e,n,{get:()=>t[n],enumerable:!(r=C(t,n))||r.enumerable});return e};var I=e=>A(f({},"__esModule",{value:!0}),e);var z={};_(z,{CMS_CONTRACT_VERSION:()=>k,SITE_HERO:()=>l,SITE_HERO_LAYOUTS:()=>m,SITE_HERO_LAYOUT_WARNING:()=>h,SITE_HERO_TAGLINE_MAX_LENGTH:()=>T,cmsTags:()=>H,defineCmsQuery:()=>c,parseSiteHero:()=>S,siteHeroQuery:()=>E,validateSiteHeroTaglines:()=>p,validateTextDocument:()=>y});module.exports=I(z);var L=/^[A-Za-z][A-Za-z0-9_]{0,63}$/,j=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,D=/^[a-z0-9]{6,12}\.[a-z][a-z0-9_-]{0,63}$/;function x(e){if(!e||!Array.isArray(e.types)||e.types.length===0)throw new TypeError("CMS dependencies require document types");let t=[...e.types];for(let r of t)if(typeof r!="string"||!L.test(r))throw new TypeError("Invalid CMS document type");if(e.ids!==void 0&&!Array.isArray(e.ids))throw new TypeError("Invalid CMS document IDs");let o=e.ids===void 0?void 0:[...e.ids];for(let r of o??[])if(typeof r!="string"||!j.test(r)||/^(drafts|versions)\./.test(r))throw new TypeError("Invalid CMS document ID");if(new Set(t).size+new Set(o).size>128)throw new TypeError("Too many CMS tags");return Object.freeze({types:Object.freeze(t),...o===void 0?{}:{ids:Object.freeze(o)}})}function c(e){if(!e||typeof e.name!="string"||!/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(e.name)||typeof e.query!="string"||e.query.trim()===""||typeof e.resolve!="function")throw new TypeError("Invalid CMS query configuration");let t=e.params;if(!t||typeof t!="object"||Object.getPrototypeOf(t)!==Object.prototype&&Object.getPrototypeOf(t)!==null)throw new TypeError("CMS params must be plain JSON scalars");let o={};for(let r of Reflect.ownKeys(t)){let n=Object.getOwnPropertyDescriptor(t,r);if(typeof r!="string"||!n||!Object.hasOwn(n,"value")||!n.enumerable)throw new TypeError("CMS params must be plain JSON scalars");let i=n.value;if(!(i===null||typeof i=="string"||typeof i=="boolean"||typeof i=="number"&&Number.isFinite(i)))throw new TypeError("CMS params must be plain JSON scalars");Object.defineProperty(o,r,{value:i,enumerable:!0})}return Object.freeze({name:e.name,query:e.query,params:Object.freeze(o),dependencies:x(e.dependencies),resolve:e.resolve})}function H(e,t){if(typeof e!="string"||e.length>77||!D.test(e))throw new TypeError("Invalid CMS scope");let o=x(t),r=[...[...new Set(o.types)].sort().map(n=>`cms:${e}:type:${n}`),...[...new Set(o.ids??[])].sort().map(n=>`cms:${e}:id:${n}`)];return Object.freeze(r)}var m=["columns","rows"],T=36,h="siteHero.layout.defaulted",k=1,l={key:"hero",documentId:"siteHero",documentType:"siteHero",fields:[{name:"heading",label:"Heading",noun:{article:"a",word:"heading"},kind:"single-line",required:!0,minLength:1,maxLength:80,noTrailingFullStop:!0},{name:"intro",label:"Introduction",noun:{article:"an",word:"introduction"},kind:"multi-line",required:!0,minLength:1,maxLength:320}],copy:{currentTitle:"On your site now",editTitle:"Edit and publish",empty:"Your site has no hero text yet. Whatever you publish below becomes its first version."}};function $(e,t){let{article:o,word:r}=e.noun;if(t.length===0)return e.required?`Enter ${o} ${r}.`:null;let n=[...t].length;return n<e.minLength?`Make the ${r} at least ${String(e.minLength)} characters. It is ${String(n)} now.`:n>e.maxLength?`Keep the ${r} to ${String(e.maxLength)} characters or fewer. It is ${String(n)} now.`:t.includes("\u2014")?`The ${r} contains a long dash. Use a comma, a colon or a short dash instead.`:e.kind==="single-line"&&/[\r\n]/.test(t)?`The ${r} must be a single line.`:e.noTrailingFullStop===!0&&t.endsWith(".")?`${r.charAt(0).toUpperCase()}${r.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`:null}function y(e,t){let o={},r={};for(let n of e.fields){let i=Object.hasOwn(t,n.name)?t[n.name]:void 0,a=typeof i=="string"?i.trim():"",s=$(n,a);Object.defineProperty(s===null?o:r,n.name,{value:s??a,enumerable:!0})}return Object.keys(r).length>0?{ok:!1,errors:r}:{ok:!0,value:o}}function p(e){if(!Array.isArray(e))return{ok:!1,error:"Enter a list of taglines."};let t=l.fields[0];if(!t)throw new TypeError("Missing siteHero heading definition");let o={...t,name:"text",noun:{article:"a",word:"tagline"},maxLength:T},r=[],n=new Set;for(let i of e){if(!i||typeof i!="object"||Array.isArray(i))return{ok:!1,error:"Enter a key and text for each tagline."};let a=i,s=Object.hasOwn(a,"key")?a.key:void 0;if(typeof s!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(s))return{ok:!1,error:"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter."};if(n.has(s))return{ok:!1,error:"Use each tagline key only once."};let d=y({...l,fields:[o]},a);if(!d.ok)return{ok:!1,error:d.errors.text??"Invalid tagline."};n.add(s),r.push({key:s,text:d.value.text??""})}return{ok:!0,value:r}}function O(e){return m.some(t=>e===t)}function S(e,t){let o=y(l,t),r=p(t.taglines??[]);if(!o.ok||!r.ok||t.layout!==void 0&&!O(t.layout))throw new TypeError("Invalid siteHero fallback");let n={heading:o.value.heading??"",intro:o.value.intro??"",layout:t.layout??"columns",taglines:r.value};if(e===null||typeof e!="object"||Array.isArray(e))return{data:n,fallbackFields:["$"]};let i=e,a=y(l,i),s=a.ok?[]:Object.keys(a.errors);for(let u of l.fields)if(!s.includes(u.name)){let b=a.ok?a.value[u.name]:i[u.name];typeof b=="string"&&(u.name==="heading"||u.name==="intro")&&(n[u.name]=b.trim())}let d=Object.hasOwn(i,"layout")?i.layout:void 0,g=[];O(d)?n.layout=d:(n.layout="columns",s.push("layout"),g.push(h));let w=p(Object.hasOwn(i,"taglines")?i.taglines:void 0);return w.ok?n.taglines=w.value:s.push("taglines"),{data:n,fallbackFields:s.length===l.fields.length+2?["$"]:s,...g.length>0?{warnings:g}:{}}}function E(){return c({name:"siteHero.v1",query:'*[_id == $id && _type == "siteHero"][0]{heading,intro,layout,taglines}',params:{id:"siteHero"},dependencies:{types:["siteHero"],ids:["siteHero"]},resolve:S})}0&&(module.exports={CMS_CONTRACT_VERSION,SITE_HERO,SITE_HERO_LAYOUTS,SITE_HERO_LAYOUT_WARNING,SITE_HERO_TAGLINE_MAX_LENGTH,cmsTags,defineCmsQuery,parseSiteHero,siteHeroQuery,validateSiteHeroTaglines,validateTextDocument});
1
+ "use strict";var m=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var D=(e,t)=>{for(var o in t)m(e,o,{get:t[o],enumerable:!0})},N=(e,t,o,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of A(t))!L.call(e,n)&&n!==o&&m(e,n,{get:()=>t[n],enumerable:!(r=v(t,n))||r.enumerable});return e};var $=e=>N(m({},"__esModule",{value:!0}),e);var F={};D(F,{CMS_CONTRACT_VERSION:()=>R,SITE_HERO:()=>y,SITE_HERO_LAYOUTS:()=>T,SITE_HERO_LAYOUT_WARNING:()=>S,SITE_HERO_TAGLINES_DEFAULTED_WARNING:()=>E,SITE_HERO_TAGLINE_DROPPED_WARNING:()=>H,SITE_HERO_TAGLINE_MAX_LENGTH:()=>h,cmsTags:()=>k,defineCmsQuery:()=>f,parseSiteHero:()=>x,siteHeroQuery:()=>I,validateSiteHeroTaglines:()=>w,validateTextDocument:()=>g});module.exports=$(F);var j=/^[A-Za-z][A-Za-z0-9_]{0,63}$/,z=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,P=/^[a-z0-9]{6,12}\.[a-z][a-z0-9_-]{0,63}$/;function O(e){if(!e||!Array.isArray(e.types)||e.types.length===0)throw new TypeError("CMS dependencies require document types");let t=[...e.types];for(let r of t)if(typeof r!="string"||!j.test(r))throw new TypeError("Invalid CMS document type");if(e.ids!==void 0&&!Array.isArray(e.ids))throw new TypeError("Invalid CMS document IDs");let o=e.ids===void 0?void 0:[...e.ids];for(let r of o??[])if(typeof r!="string"||!z.test(r)||/^(drafts|versions)\./.test(r))throw new TypeError("Invalid CMS document ID");if(new Set(t).size+new Set(o).size>128)throw new TypeError("Too many CMS tags");return Object.freeze({types:Object.freeze(t),...o===void 0?{}:{ids:Object.freeze(o)}})}function f(e){if(!e||typeof e.name!="string"||!/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(e.name)||typeof e.query!="string"||e.query.trim()===""||typeof e.resolve!="function")throw new TypeError("Invalid CMS query configuration");let t=e.params;if(!t||typeof t!="object"||Object.getPrototypeOf(t)!==Object.prototype&&Object.getPrototypeOf(t)!==null)throw new TypeError("CMS params must be plain JSON scalars");let o={};for(let r of Reflect.ownKeys(t)){let n=Object.getOwnPropertyDescriptor(t,r);if(typeof r!="string"||!n||!Object.hasOwn(n,"value")||!n.enumerable)throw new TypeError("CMS params must be plain JSON scalars");let i=n.value;if(!(i===null||typeof i=="string"||typeof i=="boolean"||typeof i=="number"&&Number.isFinite(i)))throw new TypeError("CMS params must be plain JSON scalars");Object.defineProperty(o,r,{value:i,enumerable:!0})}return Object.freeze({name:e.name,query:e.query,params:Object.freeze(o),dependencies:O(e.dependencies),resolve:e.resolve})}function k(e,t){if(typeof e!="string"||e.length>77||!P.test(e))throw new TypeError("Invalid CMS scope");let o=O(t),r=[...[...new Set(o.types)].sort().map(n=>`cms:${e}:type:${n}`),...[...new Set(o.ids??[])].sort().map(n=>`cms:${e}:id:${n}`)];return Object.freeze(r)}var T=["columns","rows"],h=40,S="siteHero.layout.defaulted",H="siteHero.tagline.dropped",E="siteHero.taglines.defaulted",R=1,y={key:"hero",documentId:"siteHero",documentType:"siteHero",fields:[{name:"heading",label:"Heading",noun:{article:"a",word:"heading"},kind:"single-line",required:!0,minLength:1,maxLength:80,noTrailingFullStop:!0},{name:"intro",label:"Introduction",noun:{article:"an",word:"introduction"},kind:"multi-line",required:!0,minLength:1,maxLength:320}],copy:{currentTitle:"On your site now",editTitle:"Edit and publish",empty:"Your site has no hero text yet. Whatever you publish below becomes its first version."}};function M(e,t){let{article:o,word:r}=e.noun;if(t.length===0)return e.required?`Enter ${o} ${r}.`:null;let n=[...t].length;return n<e.minLength?`Make the ${r} at least ${String(e.minLength)} characters. It is ${String(n)} now.`:n>e.maxLength?`Keep the ${r} to ${String(e.maxLength)} characters or fewer. It is ${String(n)} now.`:t.includes("\u2014")?`The ${r} contains a long dash. Use a comma, a colon or a short dash instead.`:e.kind==="single-line"&&/[\r\n]/.test(t)?`The ${r} must be a single line.`:e.noTrailingFullStop===!0&&t.endsWith(".")?`${r.charAt(0).toUpperCase()}${r.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`:null}function g(e,t){let o={},r={};for(let n of e.fields){let i=Object.hasOwn(t,n.name)?t[n.name]:void 0,a=typeof i=="string"?i.trim():"",s=M(n,a);Object.defineProperty(s===null?o:r,n.name,{value:s??a,enumerable:!0})}return Object.keys(r).length>0?{ok:!1,errors:r}:{ok:!0,value:o}}function C(e){if(!Array.isArray(e))return{ok:!1,error:"Enter a list of taglines."};let t=y.fields[0];if(!t)throw new TypeError("Missing siteHero heading definition");let o={...t,name:"text",noun:{article:"a",word:"tagline"},maxLength:h},r=[],n=[],i=new Set;for(let[a,s]of e.entries()){if(!s||typeof s!="object"||Array.isArray(s)){n.push({index:a,error:"Enter a key and text for each tagline."});continue}let c=s,d=Object.hasOwn(c,"key")?c.key:void 0;if(typeof d!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(d)){n.push({index:a,error:"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter."});continue}if(i.has(d)){n.push({index:a,error:"Use each tagline key only once."});continue}let u=g({...y,fields:[o]},c);if(!u.ok){n.push({index:a,error:u.errors.text??"Invalid tagline."});continue}let p=u.value.text??"";if((p.match(/\p{L}/gu)?.length??0)>=2&&!/\p{Ll}/u.test(p)){n.push({index:a,error:"Use sentence case for the tagline, rather than all capitals."});continue}i.add(d),r.push({key:d,text:p})}return{ok:!0,value:r,dropped:n}}function w(e){let t=C(e);if(!t.ok)return t;let o=t.dropped[0];return o?{ok:!1,error:o.error}:{ok:!0,value:t.value}}function _(e){return T.some(t=>e===t)}function x(e,t){let o=g(y,t),r=w(t.taglines??[]);if(!o.ok||!r.ok||t.layout!==void 0&&!_(t.layout))throw new TypeError("Invalid siteHero fallback");let n={heading:o.value.heading??"",intro:o.value.intro??"",layout:t.layout??"columns",taglines:r.value};if(e===null||typeof e!="object"||Array.isArray(e))return{data:n,fallbackFields:["$"]};let i=e,a=g(y,i),s=a.ok?[]:Object.keys(a.errors);for(let l of y.fields)if(!s.includes(l.name)){let b=a.ok?a.value[l.name]:i[l.name];typeof b=="string"&&(l.name==="heading"||l.name==="intro")&&(n[l.name]=b.trim())}let c=Object.hasOwn(i,"layout")?i.layout:void 0,d=[];_(c)?n.layout=c:(n.layout="columns",s.push("layout"),d.push(S));let u=C(Object.hasOwn(i,"taglines")?i.taglines:void 0);if(u.ok){n.taglines=u.value;for(let{index:l}of u.dropped)s.push(`taglines[${String(l)}]`);u.dropped.length>0&&d.push(H)}else s.push("taglines"),d.push(E);let p=[...y.fields.map(l=>l.name),"layout","taglines"].every(l=>s.includes(l));return{data:n,fallbackFields:p?["$"]:s,...d.length>0?{warnings:d}:{}}}function I(){return f({name:"siteHero.v1",query:'*[_id == $id && _type == "siteHero"][0]{heading,intro,layout,taglines}',params:{id:"siteHero"},dependencies:{types:["siteHero"],ids:["siteHero"]},resolve:x})}0&&(module.exports={CMS_CONTRACT_VERSION,SITE_HERO,SITE_HERO_LAYOUTS,SITE_HERO_LAYOUT_WARNING,SITE_HERO_TAGLINES_DEFAULTED_WARNING,SITE_HERO_TAGLINE_DROPPED_WARNING,SITE_HERO_TAGLINE_MAX_LENGTH,cmsTags,defineCmsQuery,parseSiteHero,siteHeroQuery,validateSiteHeroTaglines,validateTextDocument});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/contracts/query.ts", "../src/contracts/text.ts"],
4
- "sourcesContent": ["export { defineCmsQuery, cmsTags } from \"./contracts/query.js\";\nexport type { CmsParams, CmsDependencies, CmsResolution, CmsQuery } from \"./contracts/query.js\";\nexport { SITE_HERO, CMS_CONTRACT_VERSION, validateTextDocument, siteHeroQuery, SITE_HERO_LAYOUTS, SITE_HERO_TAGLINE_MAX_LENGTH, SITE_HERO_LAYOUT_WARNING, parseSiteHero, validateSiteHeroTaglines } from \"./contracts/text.js\";\nexport type { SiteHero, ParsedSiteHero, SiteHeroLayout, SiteHeroTagline, TextFieldDefinition, TextDocumentDefinition, TextValidation } from \"./contracts/text.js\";\n", "/** A-1822 S1 owns this implementation. S0 registers the public signatures. */\nexport type CmsParams = Readonly<Record<string, string | number | boolean | null>>;\nexport type CmsDependencies = {\n readonly types: readonly string[];\n readonly ids?: readonly string[];\n};\nexport type CmsResolution<T> = {\n readonly data: T;\n readonly fallbackFields: readonly string[];\n readonly warnings?: readonly \"siteHero.layout.defaulted\"[];\n};\nexport type CmsQuery<T> = {\n readonly name: string;\n readonly query: string;\n readonly params: CmsParams;\n readonly dependencies: CmsDependencies;\n readonly resolve: (raw: unknown, fallback: T) => CmsResolution<T>;\n};\n\nconst TYPE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;\nconst ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\nconst SCOPE = /^[a-z0-9]{6,12}\\.[a-z][a-z0-9_-]{0,63}$/;\n\nfunction dependenciesCopy(dependencies: CmsDependencies): CmsDependencies {\n if (!dependencies || !Array.isArray(dependencies.types) || dependencies.types.length === 0) {\n throw new TypeError(\"CMS dependencies require document types\");\n }\n const types = [...dependencies.types];\n for (const type of types) {\n if (typeof type !== \"string\" || !TYPE.test(type)) throw new TypeError(\"Invalid CMS document type\");\n }\n if (dependencies.ids !== undefined && !Array.isArray(dependencies.ids)) throw new TypeError(\"Invalid CMS document IDs\");\n const ids = dependencies.ids === undefined ? undefined : [...dependencies.ids];\n for (const id of ids ?? []) {\n if (typeof id !== \"string\" || !ID.test(id) || /^(drafts|versions)\\./.test(id)) {\n throw new TypeError(\"Invalid CMS document ID\");\n }\n }\n if (new Set(types).size + new Set(ids).size > 128) throw new TypeError(\"Too many CMS tags\");\n return Object.freeze({ types: Object.freeze(types), ...(ids === undefined ? {} : { ids: Object.freeze(ids) }) });\n}\n\nexport function defineCmsQuery<T>(query: CmsQuery<T>): CmsQuery<T> {\n if (!query || typeof query.name !== \"string\" || !/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(query.name)\n || typeof query.query !== \"string\" || query.query.trim() === \"\" || typeof query.resolve !== \"function\") {\n throw new TypeError(\"Invalid CMS query configuration\");\n }\n const params = query.params;\n if (!params || typeof params !== \"object\" || (Object.getPrototypeOf(params) !== Object.prototype && Object.getPrototypeOf(params) !== null)) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n const copied: Record<string, string | number | boolean | null> = {};\n for (const key of Reflect.ownKeys(params)) {\n const descriptor = Object.getOwnPropertyDescriptor(params, key);\n if (typeof key !== \"string\" || !descriptor || !Object.hasOwn(descriptor, \"value\") || !descriptor.enumerable) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n const value: unknown = descriptor.value;\n if (!(value === null || typeof value === \"string\" || typeof value === \"boolean\" || (typeof value === \"number\" && Number.isFinite(value)))) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n Object.defineProperty(copied, key, { value, enumerable: true });\n }\n return Object.freeze({ name: query.name, query: query.query, params: Object.freeze(copied), dependencies: dependenciesCopy(query.dependencies), resolve: query.resolve });\n}\n\nexport function cmsTags(scope: string, dependencies: CmsDependencies): readonly string[] {\n if (typeof scope !== \"string\" || scope.length > 77 || !SCOPE.test(scope)) throw new TypeError(\"Invalid CMS scope\");\n const copied = dependenciesCopy(dependencies);\n const tags = [\n ...[...new Set(copied.types)].sort().map((type) => `cms:${scope}:type:${type}`),\n ...[...new Set(copied.ids ?? [])].sort().map((id) => `cms:${scope}:id:${id}`),\n ];\n return Object.freeze(tags);\n}\n", "import { defineCmsQuery, type CmsQuery, type CmsResolution } from \"./query.js\";\n\nexport const SITE_HERO_LAYOUTS = [\"columns\", \"rows\"] as const;\nexport const SITE_HERO_TAGLINE_MAX_LENGTH = 36;\nexport const SITE_HERO_LAYOUT_WARNING = \"siteHero.layout.defaulted\" as const;\nexport type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];\nexport type SiteHeroTagline = { readonly key: string; readonly text: string };\n/** Optional additions preserve existing heading/intro-only consumer fallbacks. */\nexport type SiteHero = {\n readonly heading: string;\n readonly intro: string;\n readonly layout?: SiteHeroLayout;\n readonly taglines?: readonly SiteHeroTagline[];\n};\nexport type ParsedSiteHero = SiteHero & {\n readonly layout: SiteHeroLayout;\n readonly taglines: readonly SiteHeroTagline[];\n};\nexport type TextFieldDefinition = {\n readonly name: string;\n readonly label: string;\n readonly noun: { readonly article: \"a\" | \"an\"; readonly word: string };\n readonly kind: \"single-line\" | \"multi-line\";\n readonly required: boolean;\n readonly minLength: number;\n readonly maxLength: number;\n readonly noTrailingFullStop?: boolean;\n};\nexport type TextDocumentDefinition = {\n readonly key: string;\n readonly documentId: string;\n readonly documentType: string;\n readonly fields: readonly TextFieldDefinition[];\n readonly copy: {\n readonly currentTitle: string;\n readonly editTitle: string;\n readonly empty: string;\n };\n};\nexport type TextValidation =\n | { readonly ok: true; readonly value: Readonly<Record<string, string>> }\n | { readonly ok: false; readonly errors: Readonly<Record<string, string>> };\n\nexport const CMS_CONTRACT_VERSION = 1;\n\nexport const SITE_HERO: TextDocumentDefinition = {\n key: \"hero\",\n documentId: \"siteHero\",\n documentType: \"siteHero\",\n fields: [\n {\n name: \"heading\",\n label: \"Heading\",\n noun: { article: \"a\", word: \"heading\" },\n kind: \"single-line\",\n required: true,\n minLength: 1,\n maxLength: 80,\n noTrailingFullStop: true,\n },\n {\n name: \"intro\",\n label: \"Introduction\",\n noun: { article: \"an\", word: \"introduction\" },\n kind: \"multi-line\",\n required: true,\n minLength: 1,\n maxLength: 320,\n },\n ],\n copy: {\n currentTitle: \"On your site now\",\n editTitle: \"Edit and publish\",\n empty: \"Your site has no hero text yet. Whatever you publish below becomes its first version.\",\n },\n};\n\nfunction fieldError(field: TextFieldDefinition, text: string): string | null {\n const { article, word } = field.noun;\n if (text.length === 0) return field.required ? `Enter ${article} ${word}.` : null;\n const count = [...text].length;\n if (count < field.minLength) return `Make the ${word} at least ${String(field.minLength)} characters. It is ${String(count)} now.`;\n if (count > field.maxLength) return `Keep the ${word} to ${String(field.maxLength)} characters or fewer. It is ${String(count)} now.`;\n if (text.includes(\"\\u2014\")) return `The ${word} contains a long dash. Use a comma, a colon or a short dash instead.`;\n if (field.kind === \"single-line\" && /[\\r\\n]/.test(text)) return `The ${word} must be a single line.`;\n if (field.noTrailingFullStop === true && text.endsWith(\".\")) {\n return `${word.charAt(0).toUpperCase()}${word.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`;\n }\n return null;\n}\n\nexport function validateTextDocument(\n definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>,\n): TextValidation {\n const value: Record<string, string> = {};\n const errors: Record<string, string> = {};\n for (const field of definition.fields) {\n const submitted = Object.hasOwn(raw, field.name) ? raw[field.name] : undefined;\n const text = typeof submitted === \"string\" ? submitted.trim() : \"\";\n const error = fieldError(field, text);\n Object.defineProperty(error === null ? value : errors, field.name, { value: error ?? text, enumerable: true });\n }\n return Object.keys(errors).length > 0 ? { ok: false, errors } : { ok: true, value };\n}\n\n/** Shared with the schema adapter; no provider metadata is returned. */\nexport function validateSiteHeroTaglines(raw: unknown):\n | { readonly ok: true; readonly value: readonly SiteHeroTagline[] }\n | { readonly ok: false; readonly error: string } {\n if (!Array.isArray(raw)) return { ok: false, error: \"Enter a list of taglines.\" };\n const heading = SITE_HERO.fields[0];\n if (!heading) throw new TypeError(\"Missing siteHero heading definition\");\n const field = { ...heading, name: \"text\", noun: { article: \"a\", word: \"tagline\" } as const,\n maxLength: SITE_HERO_TAGLINE_MAX_LENGTH };\n const value: SiteHeroTagline[] = [];\n const keys = new Set<string>();\n for (const item of raw as unknown[]) {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) return { ok: false, error: \"Enter a key and text for each tagline.\" };\n const record = item as Record<string, unknown>;\n const key = Object.hasOwn(record, \"key\") ? record.key : undefined;\n if (typeof key !== \"string\" || !/^[a-z][a-z0-9_-]{0,31}$/.test(key)) {\n return { ok: false, error: \"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter.\" };\n }\n if (keys.has(key)) return { ok: false, error: \"Use each tagline key only once.\" };\n const text = validateTextDocument({ ...SITE_HERO, fields: [field] }, record);\n if (!text.ok) return { ok: false, error: text.errors.text ?? \"Invalid tagline.\" };\n keys.add(key);\n value.push({ key, text: text.value.text ?? \"\" });\n }\n return { ok: true, value };\n}\n\nfunction isLayout(value: unknown): value is SiteHeroLayout {\n return SITE_HERO_LAYOUTS.some((layout) => value === layout);\n}\n\n/** Strict per-field resolution: an invalid list never partially changes keyed copy. */\nexport function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero> {\n const local = validateTextDocument(SITE_HERO, fallback);\n const localTaglines = validateSiteHeroTaglines(fallback.taglines ?? []);\n if (!local.ok || !localTaglines.ok || (fallback.layout !== undefined && !isLayout(fallback.layout))) {\n throw new TypeError(\"Invalid siteHero fallback\");\n }\n const data: { heading: string; intro: string; layout: SiteHeroLayout; taglines: readonly SiteHeroTagline[] } = {\n heading: local.value.heading ?? \"\", intro: local.value.intro ?? \"\",\n layout: fallback.layout ?? \"columns\", taglines: localTaglines.value,\n };\n if (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) return { data, fallbackFields: [\"$\"] };\n const record = raw as Readonly<Record<string, unknown>>;\n const text = validateTextDocument(SITE_HERO, record);\n const fallbackFields: string[] = text.ok ? [] : Object.keys(text.errors);\n for (const field of SITE_HERO.fields) {\n if (!fallbackFields.includes(field.name)) {\n const value = text.ok ? text.value[field.name] : record[field.name];\n if (typeof value === \"string\" && (field.name === \"heading\" || field.name === \"intro\")) data[field.name] = value.trim();\n }\n }\n const layout = Object.hasOwn(record, \"layout\") ? record.layout : undefined;\n const warnings: (typeof SITE_HERO_LAYOUT_WARNING)[] = [];\n if (isLayout(layout)) data.layout = layout;\n else {\n data.layout = \"columns\";\n fallbackFields.push(\"layout\");\n warnings.push(SITE_HERO_LAYOUT_WARNING);\n }\n const taglines = validateSiteHeroTaglines(Object.hasOwn(record, \"taglines\") ? record.taglines : undefined);\n if (taglines.ok) data.taglines = taglines.value;\n else fallbackFields.push(\"taglines\");\n return { data, fallbackFields: fallbackFields.length === SITE_HERO.fields.length + 2 ? [\"$\"] : fallbackFields,\n ...(warnings.length > 0 ? { warnings } : {}) };\n}\n\nexport function siteHeroQuery(): CmsQuery<SiteHero> {\n return defineCmsQuery<SiteHero>({\n name: \"siteHero.v1\", query: '*[_id == $id && _type == \"siteHero\"][0]{heading,intro,layout,taglines}',\n params: { id: \"siteHero\" }, dependencies: { types: [\"siteHero\"], ids: [\"siteHero\"] },\n resolve: parseSiteHero,\n });\n}\n"],
5
- "mappings": "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",
6
- "names": ["index_exports", "__export", "CMS_CONTRACT_VERSION", "SITE_HERO", "SITE_HERO_LAYOUTS", "SITE_HERO_LAYOUT_WARNING", "SITE_HERO_TAGLINE_MAX_LENGTH", "cmsTags", "defineCmsQuery", "parseSiteHero", "siteHeroQuery", "validateSiteHeroTaglines", "validateTextDocument", "__toCommonJS", "TYPE", "ID", "SCOPE", "dependenciesCopy", "dependencies", "types", "type", "ids", "id", "defineCmsQuery", "query", "params", "copied", "key", "descriptor", "value", "cmsTags", "scope", "tags", "SITE_HERO_LAYOUTS", "SITE_HERO_TAGLINE_MAX_LENGTH", "SITE_HERO_LAYOUT_WARNING", "CMS_CONTRACT_VERSION", "SITE_HERO", "fieldError", "field", "text", "article", "word", "count", "validateTextDocument", "definition", "raw", "value", "errors", "submitted", "error", "validateSiteHeroTaglines", "heading", "keys", "item", "record", "key", "isLayout", "layout", "parseSiteHero", "fallback", "local", "localTaglines", "data", "fallbackFields", "warnings", "taglines", "siteHeroQuery", "defineCmsQuery"]
4
+ "sourcesContent": ["export { defineCmsQuery, cmsTags } from \"./contracts/query.js\";\nexport type { CmsParams, CmsDependencies, CmsResolution, CmsQuery } from \"./contracts/query.js\";\nexport { SITE_HERO, CMS_CONTRACT_VERSION, validateTextDocument, siteHeroQuery, SITE_HERO_LAYOUTS, SITE_HERO_TAGLINE_MAX_LENGTH, SITE_HERO_LAYOUT_WARNING, SITE_HERO_TAGLINE_DROPPED_WARNING, SITE_HERO_TAGLINES_DEFAULTED_WARNING, parseSiteHero, validateSiteHeroTaglines } from \"./contracts/text.js\";\nexport type { SiteHero, ParsedSiteHero, SiteHeroLayout, SiteHeroTagline, TextFieldDefinition, TextDocumentDefinition, TextValidation } from \"./contracts/text.js\";\n", "/** A-1822 S1 owns this implementation. S0 registers the public signatures. */\nexport type CmsParams = Readonly<Record<string, string | number | boolean | null>>;\nexport type CmsDependencies = {\n readonly types: readonly string[];\n readonly ids?: readonly string[];\n};\nexport type CmsResolution<T> = {\n readonly data: T;\n readonly fallbackFields: readonly string[];\n readonly warnings?: readonly (\"siteHero.layout.defaulted\" | \"siteHero.tagline.dropped\" | \"siteHero.taglines.defaulted\")[];\n};\nexport type CmsQuery<T> = {\n readonly name: string;\n readonly query: string;\n readonly params: CmsParams;\n readonly dependencies: CmsDependencies;\n readonly resolve: (raw: unknown, fallback: T) => CmsResolution<T>;\n};\n\nconst TYPE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;\nconst ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\nconst SCOPE = /^[a-z0-9]{6,12}\\.[a-z][a-z0-9_-]{0,63}$/;\n\nfunction dependenciesCopy(dependencies: CmsDependencies): CmsDependencies {\n if (!dependencies || !Array.isArray(dependencies.types) || dependencies.types.length === 0) {\n throw new TypeError(\"CMS dependencies require document types\");\n }\n const types = [...dependencies.types];\n for (const type of types) {\n if (typeof type !== \"string\" || !TYPE.test(type)) throw new TypeError(\"Invalid CMS document type\");\n }\n if (dependencies.ids !== undefined && !Array.isArray(dependencies.ids)) throw new TypeError(\"Invalid CMS document IDs\");\n const ids = dependencies.ids === undefined ? undefined : [...dependencies.ids];\n for (const id of ids ?? []) {\n if (typeof id !== \"string\" || !ID.test(id) || /^(drafts|versions)\\./.test(id)) {\n throw new TypeError(\"Invalid CMS document ID\");\n }\n }\n if (new Set(types).size + new Set(ids).size > 128) throw new TypeError(\"Too many CMS tags\");\n return Object.freeze({ types: Object.freeze(types), ...(ids === undefined ? {} : { ids: Object.freeze(ids) }) });\n}\n\nexport function defineCmsQuery<T>(query: CmsQuery<T>): CmsQuery<T> {\n if (!query || typeof query.name !== \"string\" || !/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(query.name)\n || typeof query.query !== \"string\" || query.query.trim() === \"\" || typeof query.resolve !== \"function\") {\n throw new TypeError(\"Invalid CMS query configuration\");\n }\n const params = query.params;\n if (!params || typeof params !== \"object\" || (Object.getPrototypeOf(params) !== Object.prototype && Object.getPrototypeOf(params) !== null)) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n const copied: Record<string, string | number | boolean | null> = {};\n for (const key of Reflect.ownKeys(params)) {\n const descriptor = Object.getOwnPropertyDescriptor(params, key);\n if (typeof key !== \"string\" || !descriptor || !Object.hasOwn(descriptor, \"value\") || !descriptor.enumerable) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n const value: unknown = descriptor.value;\n if (!(value === null || typeof value === \"string\" || typeof value === \"boolean\" || (typeof value === \"number\" && Number.isFinite(value)))) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n Object.defineProperty(copied, key, { value, enumerable: true });\n }\n return Object.freeze({ name: query.name, query: query.query, params: Object.freeze(copied), dependencies: dependenciesCopy(query.dependencies), resolve: query.resolve });\n}\n\nexport function cmsTags(scope: string, dependencies: CmsDependencies): readonly string[] {\n if (typeof scope !== \"string\" || scope.length > 77 || !SCOPE.test(scope)) throw new TypeError(\"Invalid CMS scope\");\n const copied = dependenciesCopy(dependencies);\n const tags = [\n ...[...new Set(copied.types)].sort().map((type) => `cms:${scope}:type:${type}`),\n ...[...new Set(copied.ids ?? [])].sort().map((id) => `cms:${scope}:id:${id}`),\n ];\n return Object.freeze(tags);\n}\n", "import { defineCmsQuery, type CmsQuery, type CmsResolution } from \"./query.js\";\n\nexport const SITE_HERO_LAYOUTS = [\"columns\", \"rows\"] as const;\nexport const SITE_HERO_TAGLINE_MAX_LENGTH = 40;\nexport const SITE_HERO_LAYOUT_WARNING = \"siteHero.layout.defaulted\" as const;\nexport const SITE_HERO_TAGLINE_DROPPED_WARNING = \"siteHero.tagline.dropped\" as const;\nexport const SITE_HERO_TAGLINES_DEFAULTED_WARNING = \"siteHero.taglines.defaulted\" as const;\nexport type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];\nexport type SiteHeroTagline = { readonly key: string; readonly text: string };\n/** Optional additions preserve existing heading/intro-only consumer fallbacks. */\nexport type SiteHero = {\n readonly heading: string;\n readonly intro: string;\n readonly layout?: SiteHeroLayout;\n readonly taglines?: readonly SiteHeroTagline[];\n};\nexport type ParsedSiteHero = SiteHero & {\n readonly layout: SiteHeroLayout;\n readonly taglines: readonly SiteHeroTagline[];\n};\nexport type TextFieldDefinition = {\n readonly name: string;\n readonly label: string;\n readonly noun: { readonly article: \"a\" | \"an\"; readonly word: string };\n readonly kind: \"single-line\" | \"multi-line\";\n readonly required: boolean;\n readonly minLength: number;\n readonly maxLength: number;\n readonly noTrailingFullStop?: boolean;\n};\nexport type TextDocumentDefinition = {\n readonly key: string;\n readonly documentId: string;\n readonly documentType: string;\n readonly fields: readonly TextFieldDefinition[];\n readonly copy: {\n readonly currentTitle: string;\n readonly editTitle: string;\n readonly empty: string;\n };\n};\nexport type TextValidation =\n | { readonly ok: true; readonly value: Readonly<Record<string, string>> }\n | { readonly ok: false; readonly errors: Readonly<Record<string, string>> };\n\nexport const CMS_CONTRACT_VERSION = 1;\n\nexport const SITE_HERO: TextDocumentDefinition = {\n key: \"hero\",\n documentId: \"siteHero\",\n documentType: \"siteHero\",\n fields: [\n {\n name: \"heading\",\n label: \"Heading\",\n noun: { article: \"a\", word: \"heading\" },\n kind: \"single-line\",\n required: true,\n minLength: 1,\n maxLength: 80,\n noTrailingFullStop: true,\n },\n {\n name: \"intro\",\n label: \"Introduction\",\n noun: { article: \"an\", word: \"introduction\" },\n kind: \"multi-line\",\n required: true,\n minLength: 1,\n maxLength: 320,\n },\n ],\n copy: {\n currentTitle: \"On your site now\",\n editTitle: \"Edit and publish\",\n empty: \"Your site has no hero text yet. Whatever you publish below becomes its first version.\",\n },\n};\n\nfunction fieldError(field: TextFieldDefinition, text: string): string | null {\n const { article, word } = field.noun;\n if (text.length === 0) return field.required ? `Enter ${article} ${word}.` : null;\n const count = [...text].length;\n if (count < field.minLength) return `Make the ${word} at least ${String(field.minLength)} characters. It is ${String(count)} now.`;\n if (count > field.maxLength) return `Keep the ${word} to ${String(field.maxLength)} characters or fewer. It is ${String(count)} now.`;\n if (text.includes(\"\\u2014\")) return `The ${word} contains a long dash. Use a comma, a colon or a short dash instead.`;\n if (field.kind === \"single-line\" && /[\\r\\n]/.test(text)) return `The ${word} must be a single line.`;\n if (field.noTrailingFullStop === true && text.endsWith(\".\")) {\n return `${word.charAt(0).toUpperCase()}${word.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`;\n }\n return null;\n}\n\nexport function validateTextDocument(\n definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>,\n): TextValidation {\n const value: Record<string, string> = {};\n const errors: Record<string, string> = {};\n for (const field of definition.fields) {\n const submitted = Object.hasOwn(raw, field.name) ? raw[field.name] : undefined;\n const text = typeof submitted === \"string\" ? submitted.trim() : \"\";\n const error = fieldError(field, text);\n Object.defineProperty(error === null ? value : errors, field.name, { value: error ?? text, enumerable: true });\n }\n return Object.keys(errors).length > 0 ? { ok: false, errors } : { ok: true, value };\n}\n\n/** One rule set for strict authoring validation and resilient published reads. */\nfunction scanSiteHeroTaglines(raw: unknown):\n | { readonly ok: true; readonly value: readonly SiteHeroTagline[]; readonly dropped: readonly { index: number; error: string }[] }\n | { readonly ok: false; readonly error: string } {\n if (!Array.isArray(raw)) return { ok: false, error: \"Enter a list of taglines.\" };\n const heading = SITE_HERO.fields[0];\n if (!heading) throw new TypeError(\"Missing siteHero heading definition\");\n const field = { ...heading, name: \"text\", noun: { article: \"a\", word: \"tagline\" } as const,\n maxLength: SITE_HERO_TAGLINE_MAX_LENGTH };\n const value: SiteHeroTagline[] = [];\n const dropped: { index: number; error: string }[] = [];\n const keys = new Set<string>();\n for (const [index, item] of (raw as unknown[]).entries()) {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) {\n dropped.push({ index, error: \"Enter a key and text for each tagline.\" });\n continue;\n }\n const record = item as Record<string, unknown>;\n const key = Object.hasOwn(record, \"key\") ? record.key : undefined;\n if (typeof key !== \"string\" || !/^[a-z][a-z0-9_-]{0,31}$/.test(key)) {\n dropped.push({ index, error: \"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter.\" });\n continue;\n }\n if (keys.has(key)) {\n dropped.push({ index, error: \"Use each tagline key only once.\" });\n continue;\n }\n const text = validateTextDocument({ ...SITE_HERO, fields: [field] }, record);\n if (!text.ok) {\n dropped.push({ index, error: text.errors.text ?? \"Invalid tagline.\" });\n continue;\n }\n const copy = text.value.text ?? \"\";\n if ((copy.match(/\\p{L}/gu)?.length ?? 0) >= 2 && !/\\p{Ll}/u.test(copy)) {\n dropped.push({ index, error: \"Use sentence case for the tagline, rather than all capitals.\" });\n continue;\n }\n keys.add(key);\n value.push({ key, text: copy });\n }\n return { ok: true, value, dropped };\n}\n\n/** Shared with the schema adapter; invalid authoring input is refused as a whole. */\nexport function validateSiteHeroTaglines(raw: unknown):\n | { readonly ok: true; readonly value: readonly SiteHeroTagline[] }\n | { readonly ok: false; readonly error: string } {\n const result = scanSiteHeroTaglines(raw);\n if (!result.ok) return result;\n const first = result.dropped[0];\n return first ? { ok: false, error: first.error } : { ok: true, value: result.value };\n}\n\nfunction isLayout(value: unknown): value is SiteHeroLayout {\n return SITE_HERO_LAYOUTS.some((layout) => value === layout);\n}\n\n/** Invalid published entries are dropped without discarding valid keyed siblings. */\nexport function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero> {\n const local = validateTextDocument(SITE_HERO, fallback);\n const localTaglines = validateSiteHeroTaglines(fallback.taglines ?? []);\n if (!local.ok || !localTaglines.ok || (fallback.layout !== undefined && !isLayout(fallback.layout))) {\n throw new TypeError(\"Invalid siteHero fallback\");\n }\n const data: { heading: string; intro: string; layout: SiteHeroLayout; taglines: readonly SiteHeroTagline[] } = {\n heading: local.value.heading ?? \"\", intro: local.value.intro ?? \"\",\n layout: fallback.layout ?? \"columns\", taglines: localTaglines.value,\n };\n if (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) return { data, fallbackFields: [\"$\"] };\n const record = raw as Readonly<Record<string, unknown>>;\n const text = validateTextDocument(SITE_HERO, record);\n const fallbackFields: string[] = text.ok ? [] : Object.keys(text.errors);\n for (const field of SITE_HERO.fields) {\n if (!fallbackFields.includes(field.name)) {\n const value = text.ok ? text.value[field.name] : record[field.name];\n if (typeof value === \"string\" && (field.name === \"heading\" || field.name === \"intro\")) data[field.name] = value.trim();\n }\n }\n const layout = Object.hasOwn(record, \"layout\") ? record.layout : undefined;\n const warnings: NonNullable<CmsResolution<ParsedSiteHero>[\"warnings\"]>[number][] = [];\n if (isLayout(layout)) data.layout = layout;\n else {\n data.layout = \"columns\";\n fallbackFields.push(\"layout\");\n warnings.push(SITE_HERO_LAYOUT_WARNING);\n }\n const taglines = scanSiteHeroTaglines(Object.hasOwn(record, \"taglines\") ? record.taglines : undefined);\n if (taglines.ok) {\n data.taglines = taglines.value;\n for (const { index } of taglines.dropped) fallbackFields.push(`taglines[${String(index)}]`);\n if (taglines.dropped.length > 0) warnings.push(SITE_HERO_TAGLINE_DROPPED_WARNING);\n } else {\n fallbackFields.push(\"taglines\");\n warnings.push(SITE_HERO_TAGLINES_DEFAULTED_WARNING);\n }\n const wholeFallback = [...SITE_HERO.fields.map((field) => field.name), \"layout\", \"taglines\"].every((field) => fallbackFields.includes(field));\n return { data, fallbackFields: wholeFallback ? [\"$\"] : fallbackFields,\n ...(warnings.length > 0 ? { warnings } : {}) };\n}\n\nexport function siteHeroQuery(): CmsQuery<SiteHero> {\n return defineCmsQuery<SiteHero>({\n name: \"siteHero.v1\", query: '*[_id == $id && _type == \"siteHero\"][0]{heading,intro,layout,taglines}',\n params: { id: \"siteHero\" }, dependencies: { types: [\"siteHero\"], ids: [\"siteHero\"] },\n resolve: parseSiteHero,\n });\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,0BAAAE,EAAA,cAAAC,EAAA,sBAAAC,EAAA,6BAAAC,EAAA,yCAAAC,EAAA,sCAAAC,EAAA,iCAAAC,EAAA,YAAAC,EAAA,mBAAAC,EAAA,kBAAAC,EAAA,kBAAAC,EAAA,6BAAAC,EAAA,yBAAAC,IAAA,eAAAC,EAAAf,GCmBA,IAAMgB,EAAO,+BACPC,EAAK,qCACLC,EAAQ,0CAEd,SAASC,EAAiBC,EAAgD,CACxE,GAAI,CAACA,GAAgB,CAAC,MAAM,QAAQA,EAAa,KAAK,GAAKA,EAAa,MAAM,SAAW,EACvF,MAAM,IAAI,UAAU,yCAAyC,EAE/D,IAAMC,EAAQ,CAAC,GAAGD,EAAa,KAAK,EACpC,QAAWE,KAAQD,EACjB,GAAI,OAAOC,GAAS,UAAY,CAACN,EAAK,KAAKM,CAAI,EAAG,MAAM,IAAI,UAAU,2BAA2B,EAEnG,GAAIF,EAAa,MAAQ,QAAa,CAAC,MAAM,QAAQA,EAAa,GAAG,EAAG,MAAM,IAAI,UAAU,0BAA0B,EACtH,IAAMG,EAAMH,EAAa,MAAQ,OAAY,OAAY,CAAC,GAAGA,EAAa,GAAG,EAC7E,QAAWI,KAAMD,GAAO,CAAC,EACvB,GAAI,OAAOC,GAAO,UAAY,CAACP,EAAG,KAAKO,CAAE,GAAK,uBAAuB,KAAKA,CAAE,EAC1E,MAAM,IAAI,UAAU,yBAAyB,EAGjD,GAAI,IAAI,IAAIH,CAAK,EAAE,KAAO,IAAI,IAAIE,CAAG,EAAE,KAAO,IAAK,MAAM,IAAI,UAAU,mBAAmB,EAC1F,OAAO,OAAO,OAAO,CAAE,MAAO,OAAO,OAAOF,CAAK,EAAG,GAAIE,IAAQ,OAAY,CAAC,EAAI,CAAE,IAAK,OAAO,OAAOA,CAAG,CAAE,CAAG,CAAC,CACjH,CAEO,SAASE,EAAkBC,EAAiC,CACjE,GAAI,CAACA,GAAS,OAAOA,EAAM,MAAS,UAAY,CAAC,kCAAkC,KAAKA,EAAM,IAAI,GAC7F,OAAOA,EAAM,OAAU,UAAYA,EAAM,MAAM,KAAK,IAAM,IAAM,OAAOA,EAAM,SAAY,WAC5F,MAAM,IAAI,UAAU,iCAAiC,EAEvD,IAAMC,EAASD,EAAM,OACrB,GAAI,CAACC,GAAU,OAAOA,GAAW,UAAa,OAAO,eAAeA,CAAM,IAAM,OAAO,WAAa,OAAO,eAAeA,CAAM,IAAM,KACpI,MAAM,IAAI,UAAU,uCAAuC,EAE7D,IAAMC,EAA2D,CAAC,EAClE,QAAWC,KAAO,QAAQ,QAAQF,CAAM,EAAG,CACzC,IAAMG,EAAa,OAAO,yBAAyBH,EAAQE,CAAG,EAC9D,GAAI,OAAOA,GAAQ,UAAY,CAACC,GAAc,CAAC,OAAO,OAAOA,EAAY,OAAO,GAAK,CAACA,EAAW,WAC/F,MAAM,IAAI,UAAU,uCAAuC,EAE7D,IAAMC,EAAiBD,EAAW,MAClC,GAAI,EAAEC,IAAU,MAAQ,OAAOA,GAAU,UAAY,OAAOA,GAAU,WAAc,OAAOA,GAAU,UAAY,OAAO,SAASA,CAAK,GACpI,MAAM,IAAI,UAAU,uCAAuC,EAE7D,OAAO,eAAeH,EAAQC,EAAK,CAAE,MAAAE,EAAO,WAAY,EAAK,CAAC,CAChE,CACA,OAAO,OAAO,OAAO,CAAE,KAAML,EAAM,KAAM,MAAOA,EAAM,MAAO,OAAQ,OAAO,OAAOE,CAAM,EAAG,aAAcT,EAAiBO,EAAM,YAAY,EAAG,QAASA,EAAM,OAAQ,CAAC,CAC1K,CAEO,SAASM,EAAQC,EAAeb,EAAkD,CACvF,GAAI,OAAOa,GAAU,UAAYA,EAAM,OAAS,IAAM,CAACf,EAAM,KAAKe,CAAK,EAAG,MAAM,IAAI,UAAU,mBAAmB,EACjH,IAAML,EAAST,EAAiBC,CAAY,EACtCc,EAAO,CACX,GAAG,CAAC,GAAG,IAAI,IAAIN,EAAO,KAAK,CAAC,EAAE,KAAK,EAAE,IAAKN,GAAS,OAAOW,CAAK,SAASX,CAAI,EAAE,EAC9E,GAAG,CAAC,GAAG,IAAI,IAAIM,EAAO,KAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAKJ,GAAO,OAAOS,CAAK,OAAOT,CAAE,EAAE,CAC9E,EACA,OAAO,OAAO,OAAOU,CAAI,CAC3B,CCxEO,IAAMC,EAAoB,CAAC,UAAW,MAAM,EACtCC,EAA+B,GAC/BC,EAA2B,4BAC3BC,EAAoC,2BACpCC,EAAuC,8BAuCvCC,EAAuB,EAEvBC,EAAoC,CAC/C,IAAK,OACL,WAAY,WACZ,aAAc,WACd,OAAQ,CACN,CACE,KAAM,UACN,MAAO,UACP,KAAM,CAAE,QAAS,IAAK,KAAM,SAAU,EACtC,KAAM,cACN,SAAU,GACV,UAAW,EACX,UAAW,GACX,mBAAoB,EACtB,EACA,CACE,KAAM,QACN,MAAO,eACP,KAAM,CAAE,QAAS,KAAM,KAAM,cAAe,EAC5C,KAAM,aACN,SAAU,GACV,UAAW,EACX,UAAW,GACb,CACF,EACA,KAAM,CACJ,aAAc,mBACd,UAAW,mBACX,MAAO,uFACT,CACF,EAEA,SAASC,EAAWC,EAA4BC,EAA6B,CAC3E,GAAM,CAAE,QAAAC,EAAS,KAAAC,CAAK,EAAIH,EAAM,KAChC,GAAIC,EAAK,SAAW,EAAG,OAAOD,EAAM,SAAW,SAASE,CAAO,IAAIC,CAAI,IAAM,KAC7E,IAAMC,EAAQ,CAAC,GAAGH,CAAI,EAAE,OACxB,OAAIG,EAAQJ,EAAM,UAAkB,YAAYG,CAAI,aAAa,OAAOH,EAAM,SAAS,CAAC,sBAAsB,OAAOI,CAAK,CAAC,QACvHA,EAAQJ,EAAM,UAAkB,YAAYG,CAAI,OAAO,OAAOH,EAAM,SAAS,CAAC,+BAA+B,OAAOI,CAAK,CAAC,QAC1HH,EAAK,SAAS,QAAQ,EAAU,OAAOE,CAAI,uEAC3CH,EAAM,OAAS,eAAiB,SAAS,KAAKC,CAAI,EAAU,OAAOE,CAAI,0BACvEH,EAAM,qBAAuB,IAAQC,EAAK,SAAS,GAAG,EACjD,GAAGE,EAAK,OAAO,CAAC,EAAE,YAAY,CAAC,GAAGA,EAAK,MAAM,CAAC,CAAC,yEAEjD,IACT,CAEO,SAASE,EACdC,EAAoCC,EACpB,CAChB,IAAMC,EAAgC,CAAC,EACjCC,EAAiC,CAAC,EACxC,QAAWT,KAASM,EAAW,OAAQ,CACrC,IAAMI,EAAY,OAAO,OAAOH,EAAKP,EAAM,IAAI,EAAIO,EAAIP,EAAM,IAAI,EAAI,OAC/DC,EAAO,OAAOS,GAAc,SAAWA,EAAU,KAAK,EAAI,GAC1DC,EAAQZ,EAAWC,EAAOC,CAAI,EACpC,OAAO,eAAeU,IAAU,KAAOH,EAAQC,EAAQT,EAAM,KAAM,CAAE,MAAOW,GAASV,EAAM,WAAY,EAAK,CAAC,CAC/G,CACA,OAAO,OAAO,KAAKQ,CAAM,EAAE,OAAS,EAAI,CAAE,GAAI,GAAO,OAAAA,CAAO,EAAI,CAAE,GAAI,GAAM,MAAAD,CAAM,CACpF,CAGA,SAASI,EAAqBL,EAEqB,CACjD,GAAI,CAAC,MAAM,QAAQA,CAAG,EAAG,MAAO,CAAE,GAAI,GAAO,MAAO,2BAA4B,EAChF,IAAMM,EAAUf,EAAU,OAAO,CAAC,EAClC,GAAI,CAACe,EAAS,MAAM,IAAI,UAAU,qCAAqC,EACvE,IAAMb,EAAQ,CAAE,GAAGa,EAAS,KAAM,OAAQ,KAAM,CAAE,QAAS,IAAK,KAAM,SAAU,EAC9E,UAAWpB,CAA6B,EACpCe,EAA2B,CAAC,EAC5BM,EAA8C,CAAC,EAC/CC,EAAO,IAAI,IACjB,OAAW,CAACC,EAAOC,CAAI,IAAMV,EAAkB,QAAQ,EAAG,CACxD,GAAI,CAACU,GAAQ,OAAOA,GAAS,UAAY,MAAM,QAAQA,CAAI,EAAG,CAC5DH,EAAQ,KAAK,CAAE,MAAAE,EAAO,MAAO,wCAAyC,CAAC,EACvE,QACF,CACA,IAAME,EAASD,EACTE,EAAM,OAAO,OAAOD,EAAQ,KAAK,EAAIA,EAAO,IAAM,OACxD,GAAI,OAAOC,GAAQ,UAAY,CAAC,0BAA0B,KAAKA,CAAG,EAAG,CACnEL,EAAQ,KAAK,CAAE,MAAAE,EAAO,MAAO,uGAAwG,CAAC,EACtI,QACF,CACA,GAAID,EAAK,IAAII,CAAG,EAAG,CACjBL,EAAQ,KAAK,CAAE,MAAAE,EAAO,MAAO,iCAAkC,CAAC,EAChE,QACF,CACA,IAAMf,EAAOI,EAAqB,CAAE,GAAGP,EAAW,OAAQ,CAACE,CAAK,CAAE,EAAGkB,CAAM,EAC3E,GAAI,CAACjB,EAAK,GAAI,CACZa,EAAQ,KAAK,CAAE,MAAAE,EAAO,MAAOf,EAAK,OAAO,MAAQ,kBAAmB,CAAC,EACrE,QACF,CACA,IAAMmB,EAAOnB,EAAK,MAAM,MAAQ,GAChC,IAAKmB,EAAK,MAAM,SAAS,GAAG,QAAU,IAAM,GAAK,CAAC,UAAU,KAAKA,CAAI,EAAG,CACtEN,EAAQ,KAAK,CAAE,MAAAE,EAAO,MAAO,8DAA+D,CAAC,EAC7F,QACF,CACAD,EAAK,IAAII,CAAG,EACZX,EAAM,KAAK,CAAE,IAAAW,EAAK,KAAMC,CAAK,CAAC,CAChC,CACA,MAAO,CAAE,GAAI,GAAM,MAAAZ,EAAO,QAAAM,CAAQ,CACpC,CAGO,SAASO,EAAyBd,EAEU,CACjD,IAAMe,EAASV,EAAqBL,CAAG,EACvC,GAAI,CAACe,EAAO,GAAI,OAAOA,EACvB,IAAMC,EAAQD,EAAO,QAAQ,CAAC,EAC9B,OAAOC,EAAQ,CAAE,GAAI,GAAO,MAAOA,EAAM,KAAM,EAAI,CAAE,GAAI,GAAM,MAAOD,EAAO,KAAM,CACrF,CAEA,SAASE,EAAShB,EAAyC,CACzD,OAAOhB,EAAkB,KAAMiC,GAAWjB,IAAUiB,CAAM,CAC5D,CAGO,SAASC,EAAcnB,EAAcoB,EAAmD,CAC7F,IAAMC,EAAQvB,EAAqBP,EAAW6B,CAAQ,EAChDE,EAAgBR,EAAyBM,EAAS,UAAY,CAAC,CAAC,EACtE,GAAI,CAACC,EAAM,IAAM,CAACC,EAAc,IAAOF,EAAS,SAAW,QAAa,CAACH,EAASG,EAAS,MAAM,EAC/F,MAAM,IAAI,UAAU,2BAA2B,EAEjD,IAAMG,EAAyG,CAC7G,QAASF,EAAM,MAAM,SAAW,GAAI,MAAOA,EAAM,MAAM,OAAS,GAChE,OAAQD,EAAS,QAAU,UAAW,SAAUE,EAAc,KAChE,EACA,GAAItB,IAAQ,MAAQ,OAAOA,GAAQ,UAAY,MAAM,QAAQA,CAAG,EAAG,MAAO,CAAE,KAAAuB,EAAM,eAAgB,CAAC,GAAG,CAAE,EACxG,IAAMZ,EAASX,EACTN,EAAOI,EAAqBP,EAAWoB,CAAM,EAC7Ca,EAA2B9B,EAAK,GAAK,CAAC,EAAI,OAAO,KAAKA,EAAK,MAAM,EACvE,QAAWD,KAASF,EAAU,OAC5B,GAAI,CAACiC,EAAe,SAAS/B,EAAM,IAAI,EAAG,CACxC,IAAMQ,EAAQP,EAAK,GAAKA,EAAK,MAAMD,EAAM,IAAI,EAAIkB,EAAOlB,EAAM,IAAI,EAC9D,OAAOQ,GAAU,WAAaR,EAAM,OAAS,WAAaA,EAAM,OAAS,WAAU8B,EAAK9B,EAAM,IAAI,EAAIQ,EAAM,KAAK,EACvH,CAEF,IAAMiB,EAAS,OAAO,OAAOP,EAAQ,QAAQ,EAAIA,EAAO,OAAS,OAC3Dc,EAA6E,CAAC,EAChFR,EAASC,CAAM,EAAGK,EAAK,OAASL,GAElCK,EAAK,OAAS,UACdC,EAAe,KAAK,QAAQ,EAC5BC,EAAS,KAAKtC,CAAwB,GAExC,IAAMuC,EAAWrB,EAAqB,OAAO,OAAOM,EAAQ,UAAU,EAAIA,EAAO,SAAW,MAAS,EACrG,GAAIe,EAAS,GAAI,CACfH,EAAK,SAAWG,EAAS,MACzB,OAAW,CAAE,MAAAjB,CAAM,IAAKiB,EAAS,QAASF,EAAe,KAAK,YAAY,OAAOf,CAAK,CAAC,GAAG,EACtFiB,EAAS,QAAQ,OAAS,GAAGD,EAAS,KAAKrC,CAAiC,CAClF,MACEoC,EAAe,KAAK,UAAU,EAC9BC,EAAS,KAAKpC,CAAoC,EAEpD,IAAMsC,EAAgB,CAAC,GAAGpC,EAAU,OAAO,IAAKE,GAAUA,EAAM,IAAI,EAAG,SAAU,UAAU,EAAE,MAAOA,GAAU+B,EAAe,SAAS/B,CAAK,CAAC,EAC5I,MAAO,CAAE,KAAA8B,EAAM,eAAgBI,EAAgB,CAAC,GAAG,EAAIH,EACrD,GAAIC,EAAS,OAAS,EAAI,CAAE,SAAAA,CAAS,EAAI,CAAC,CAAG,CACjD,CAEO,SAASG,GAAoC,CAClD,OAAOC,EAAyB,CAC9B,KAAM,cAAe,MAAO,yEAC5B,OAAQ,CAAE,GAAI,UAAW,EAAG,aAAc,CAAE,MAAO,CAAC,UAAU,EAAG,IAAK,CAAC,UAAU,CAAE,EACnF,QAASV,CACX,CAAC,CACH",
6
+ "names": ["index_exports", "__export", "CMS_CONTRACT_VERSION", "SITE_HERO", "SITE_HERO_LAYOUTS", "SITE_HERO_LAYOUT_WARNING", "SITE_HERO_TAGLINES_DEFAULTED_WARNING", "SITE_HERO_TAGLINE_DROPPED_WARNING", "SITE_HERO_TAGLINE_MAX_LENGTH", "cmsTags", "defineCmsQuery", "parseSiteHero", "siteHeroQuery", "validateSiteHeroTaglines", "validateTextDocument", "__toCommonJS", "TYPE", "ID", "SCOPE", "dependenciesCopy", "dependencies", "types", "type", "ids", "id", "defineCmsQuery", "query", "params", "copied", "key", "descriptor", "value", "cmsTags", "scope", "tags", "SITE_HERO_LAYOUTS", "SITE_HERO_TAGLINE_MAX_LENGTH", "SITE_HERO_LAYOUT_WARNING", "SITE_HERO_TAGLINE_DROPPED_WARNING", "SITE_HERO_TAGLINES_DEFAULTED_WARNING", "CMS_CONTRACT_VERSION", "SITE_HERO", "fieldError", "field", "text", "article", "word", "count", "validateTextDocument", "definition", "raw", "value", "errors", "submitted", "error", "scanSiteHeroTaglines", "heading", "dropped", "keys", "index", "item", "record", "key", "copy", "validateSiteHeroTaglines", "result", "first", "isLayout", "layout", "parseSiteHero", "fallback", "local", "localTaglines", "data", "fallbackFields", "warnings", "taglines", "wholeFallback", "siteHeroQuery", "defineCmsQuery"]
7
7
  }
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { defineCmsQuery, cmsTags } from "./contracts/query.cjs";
2
2
  export type { CmsParams, CmsDependencies, CmsResolution, CmsQuery } from "./contracts/query.cjs";
3
- export { SITE_HERO, CMS_CONTRACT_VERSION, validateTextDocument, siteHeroQuery, SITE_HERO_LAYOUTS, SITE_HERO_TAGLINE_MAX_LENGTH, SITE_HERO_LAYOUT_WARNING, parseSiteHero, validateSiteHeroTaglines } from "./contracts/text.cjs";
3
+ export { SITE_HERO, CMS_CONTRACT_VERSION, validateTextDocument, siteHeroQuery, SITE_HERO_LAYOUTS, SITE_HERO_TAGLINE_MAX_LENGTH, SITE_HERO_LAYOUT_WARNING, SITE_HERO_TAGLINE_DROPPED_WARNING, SITE_HERO_TAGLINES_DEFAULTED_WARNING, parseSiteHero, validateSiteHeroTaglines } from "./contracts/text.cjs";
4
4
  export type { SiteHero, ParsedSiteHero, SiteHeroLayout, SiteHeroTagline, TextFieldDefinition, TextDocumentDefinition, TextValidation } from "./contracts/text.cjs";
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { defineCmsQuery, cmsTags } from "./contracts/query.mjs";
2
2
  export type { CmsParams, CmsDependencies, CmsResolution, CmsQuery } from "./contracts/query.mjs";
3
- export { SITE_HERO, CMS_CONTRACT_VERSION, validateTextDocument, siteHeroQuery, SITE_HERO_LAYOUTS, SITE_HERO_TAGLINE_MAX_LENGTH, SITE_HERO_LAYOUT_WARNING, parseSiteHero, validateSiteHeroTaglines } from "./contracts/text.mjs";
3
+ export { SITE_HERO, CMS_CONTRACT_VERSION, validateTextDocument, siteHeroQuery, SITE_HERO_LAYOUTS, SITE_HERO_TAGLINE_MAX_LENGTH, SITE_HERO_LAYOUT_WARNING, SITE_HERO_TAGLINE_DROPPED_WARNING, SITE_HERO_TAGLINES_DEFAULTED_WARNING, parseSiteHero, validateSiteHeroTaglines } from "./contracts/text.mjs";
4
4
  export type { SiteHero, ParsedSiteHero, SiteHeroLayout, SiteHeroTagline, TextFieldDefinition, TextDocumentDefinition, TextValidation } from "./contracts/text.mjs";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { defineCmsQuery, cmsTags } from "./contracts/query.js";
2
2
  export type { CmsParams, CmsDependencies, CmsResolution, CmsQuery } from "./contracts/query.js";
3
- export { SITE_HERO, CMS_CONTRACT_VERSION, validateTextDocument, siteHeroQuery, SITE_HERO_LAYOUTS, SITE_HERO_TAGLINE_MAX_LENGTH, SITE_HERO_LAYOUT_WARNING, parseSiteHero, validateSiteHeroTaglines } from "./contracts/text.js";
3
+ export { SITE_HERO, CMS_CONTRACT_VERSION, validateTextDocument, siteHeroQuery, SITE_HERO_LAYOUTS, SITE_HERO_TAGLINE_MAX_LENGTH, SITE_HERO_LAYOUT_WARNING, SITE_HERO_TAGLINE_DROPPED_WARNING, SITE_HERO_TAGLINES_DEFAULTED_WARNING, parseSiteHero, validateSiteHeroTaglines } from "./contracts/text.js";
4
4
  export type { SiteHero, ParsedSiteHero, SiteHeroLayout, SiteHeroTagline, TextFieldDefinition, TextDocumentDefinition, TextValidation } from "./contracts/text.js";
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var H=/^[A-Za-z][A-Za-z0-9_]{0,63}$/,O=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,k=/^[a-z0-9]{6,12}\.[a-z][a-z0-9_-]{0,63}$/;function T(e){if(!e||!Array.isArray(e.types)||e.types.length===0)throw new TypeError("CMS dependencies require document types");let t=[...e.types];for(let n of t)if(typeof n!="string"||!H.test(n))throw new TypeError("Invalid CMS document type");if(e.ids!==void 0&&!Array.isArray(e.ids))throw new TypeError("Invalid CMS document IDs");let o=e.ids===void 0?void 0:[...e.ids];for(let n of o??[])if(typeof n!="string"||!O.test(n)||/^(drafts|versions)\./.test(n))throw new TypeError("Invalid CMS document ID");if(new Set(t).size+new Set(o).size>128)throw new TypeError("Too many CMS tags");return Object.freeze({types:Object.freeze(t),...o===void 0?{}:{ids:Object.freeze(o)}})}function p(e){if(!e||typeof e.name!="string"||!/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(e.name)||typeof e.query!="string"||e.query.trim()===""||typeof e.resolve!="function")throw new TypeError("Invalid CMS query configuration");let t=e.params;if(!t||typeof t!="object"||Object.getPrototypeOf(t)!==Object.prototype&&Object.getPrototypeOf(t)!==null)throw new TypeError("CMS params must be plain JSON scalars");let o={};for(let n of Reflect.ownKeys(t)){let r=Object.getOwnPropertyDescriptor(t,n);if(typeof n!="string"||!r||!Object.hasOwn(r,"value")||!r.enumerable)throw new TypeError("CMS params must be plain JSON scalars");let i=r.value;if(!(i===null||typeof i=="string"||typeof i=="boolean"||typeof i=="number"&&Number.isFinite(i)))throw new TypeError("CMS params must be plain JSON scalars");Object.defineProperty(o,n,{value:i,enumerable:!0})}return Object.freeze({name:e.name,query:e.query,params:Object.freeze(o),dependencies:T(e.dependencies),resolve:e.resolve})}function E(e,t){if(typeof e!="string"||e.length>77||!k.test(e))throw new TypeError("Invalid CMS scope");let o=T(t),n=[...[...new Set(o.types)].sort().map(r=>`cms:${e}:type:${r}`),...[...new Set(o.ids??[])].sort().map(r=>`cms:${e}:id:${r}`)];return Object.freeze(n)}var S=["columns","rows"],w=36,b="siteHero.layout.defaulted",C=1,l={key:"hero",documentId:"siteHero",documentType:"siteHero",fields:[{name:"heading",label:"Heading",noun:{article:"a",word:"heading"},kind:"single-line",required:!0,minLength:1,maxLength:80,noTrailingFullStop:!0},{name:"intro",label:"Introduction",noun:{article:"an",word:"introduction"},kind:"multi-line",required:!0,minLength:1,maxLength:320}],copy:{currentTitle:"On your site now",editTitle:"Edit and publish",empty:"Your site has no hero text yet. Whatever you publish below becomes its first version."}};function R(e,t){let{article:o,word:n}=e.noun;if(t.length===0)return e.required?`Enter ${o} ${n}.`:null;let r=[...t].length;return r<e.minLength?`Make the ${n} at least ${String(e.minLength)} characters. It is ${String(r)} now.`:r>e.maxLength?`Keep the ${n} to ${String(e.maxLength)} characters or fewer. It is ${String(r)} now.`:t.includes("\u2014")?`The ${n} contains a long dash. Use a comma, a colon or a short dash instead.`:e.kind==="single-line"&&/[\r\n]/.test(t)?`The ${n} must be a single line.`:e.noTrailingFullStop===!0&&t.endsWith(".")?`${n.charAt(0).toUpperCase()}${n.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`:null}function y(e,t){let o={},n={};for(let r of e.fields){let i=Object.hasOwn(t,r.name)?t[r.name]:void 0,a=typeof i=="string"?i.trim():"",s=R(r,a);Object.defineProperty(s===null?o:n,r.name,{value:s??a,enumerable:!0})}return Object.keys(n).length>0?{ok:!1,errors:n}:{ok:!0,value:o}}function g(e){if(!Array.isArray(e))return{ok:!1,error:"Enter a list of taglines."};let t=l.fields[0];if(!t)throw new TypeError("Missing siteHero heading definition");let o={...t,name:"text",noun:{article:"a",word:"tagline"},maxLength:w},n=[],r=new Set;for(let i of e){if(!i||typeof i!="object"||Array.isArray(i))return{ok:!1,error:"Enter a key and text for each tagline."};let a=i,s=Object.hasOwn(a,"key")?a.key:void 0;if(typeof s!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(s))return{ok:!1,error:"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter."};if(r.has(s))return{ok:!1,error:"Use each tagline key only once."};let d=y({...l,fields:[o]},a);if(!d.ok)return{ok:!1,error:d.errors.text??"Invalid tagline."};r.add(s),n.push({key:s,text:d.value.text??""})}return{ok:!0,value:n}}function h(e){return S.some(t=>e===t)}function x(e,t){let o=y(l,t),n=g(t.taglines??[]);if(!o.ok||!n.ok||t.layout!==void 0&&!h(t.layout))throw new TypeError("Invalid siteHero fallback");let r={heading:o.value.heading??"",intro:o.value.intro??"",layout:t.layout??"columns",taglines:n.value};if(e===null||typeof e!="object"||Array.isArray(e))return{data:r,fallbackFields:["$"]};let i=e,a=y(l,i),s=a.ok?[]:Object.keys(a.errors);for(let u of l.fields)if(!s.includes(u.name)){let m=a.ok?a.value[u.name]:i[u.name];typeof m=="string"&&(u.name==="heading"||u.name==="intro")&&(r[u.name]=m.trim())}let d=Object.hasOwn(i,"layout")?i.layout:void 0,c=[];h(d)?r.layout=d:(r.layout="columns",s.push("layout"),c.push(b));let f=g(Object.hasOwn(i,"taglines")?i.taglines:void 0);return f.ok?r.taglines=f.value:s.push("taglines"),{data:r,fallbackFields:s.length===l.fields.length+2?["$"]:s,...c.length>0?{warnings:c}:{}}}function v(){return p({name:"siteHero.v1",query:'*[_id == $id && _type == "siteHero"][0]{heading,intro,layout,taglines}',params:{id:"siteHero"},dependencies:{types:["siteHero"],ids:["siteHero"]},resolve:x})}export{C as CMS_CONTRACT_VERSION,l as SITE_HERO,S as SITE_HERO_LAYOUTS,b as SITE_HERO_LAYOUT_WARNING,w as SITE_HERO_TAGLINE_MAX_LENGTH,E as cmsTags,p as defineCmsQuery,x as parseSiteHero,v as siteHeroQuery,g as validateSiteHeroTaglines,y as validateTextDocument};
1
+ var _=/^[A-Za-z][A-Za-z0-9_]{0,63}$/,R=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,C=/^[a-z0-9]{6,12}\.[a-z][a-z0-9_-]{0,63}$/;function T(e){if(!e||!Array.isArray(e.types)||e.types.length===0)throw new TypeError("CMS dependencies require document types");let t=[...e.types];for(let r of t)if(typeof r!="string"||!_.test(r))throw new TypeError("Invalid CMS document type");if(e.ids!==void 0&&!Array.isArray(e.ids))throw new TypeError("Invalid CMS document IDs");let o=e.ids===void 0?void 0:[...e.ids];for(let r of o??[])if(typeof r!="string"||!R.test(r)||/^(drafts|versions)\./.test(r))throw new TypeError("Invalid CMS document ID");if(new Set(t).size+new Set(o).size>128)throw new TypeError("Too many CMS tags");return Object.freeze({types:Object.freeze(t),...o===void 0?{}:{ids:Object.freeze(o)}})}function f(e){if(!e||typeof e.name!="string"||!/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(e.name)||typeof e.query!="string"||e.query.trim()===""||typeof e.resolve!="function")throw new TypeError("Invalid CMS query configuration");let t=e.params;if(!t||typeof t!="object"||Object.getPrototypeOf(t)!==Object.prototype&&Object.getPrototypeOf(t)!==null)throw new TypeError("CMS params must be plain JSON scalars");let o={};for(let r of Reflect.ownKeys(t)){let n=Object.getOwnPropertyDescriptor(t,r);if(typeof r!="string"||!n||!Object.hasOwn(n,"value")||!n.enumerable)throw new TypeError("CMS params must be plain JSON scalars");let i=n.value;if(!(i===null||typeof i=="string"||typeof i=="boolean"||typeof i=="number"&&Number.isFinite(i)))throw new TypeError("CMS params must be plain JSON scalars");Object.defineProperty(o,r,{value:i,enumerable:!0})}return Object.freeze({name:e.name,query:e.query,params:Object.freeze(o),dependencies:T(e.dependencies),resolve:e.resolve})}function I(e,t){if(typeof e!="string"||e.length>77||!C.test(e))throw new TypeError("Invalid CMS scope");let o=T(t),r=[...[...new Set(o.types)].sort().map(n=>`cms:${e}:type:${n}`),...[...new Set(o.ids??[])].sort().map(n=>`cms:${e}:id:${n}`)];return Object.freeze(r)}var S=["columns","rows"],H=40,E="siteHero.layout.defaulted",w="siteHero.tagline.dropped",x="siteHero.taglines.defaulted",v=1,y={key:"hero",documentId:"siteHero",documentType:"siteHero",fields:[{name:"heading",label:"Heading",noun:{article:"a",word:"heading"},kind:"single-line",required:!0,minLength:1,maxLength:80,noTrailingFullStop:!0},{name:"intro",label:"Introduction",noun:{article:"an",word:"introduction"},kind:"multi-line",required:!0,minLength:1,maxLength:320}],copy:{currentTitle:"On your site now",editTitle:"Edit and publish",empty:"Your site has no hero text yet. Whatever you publish below becomes its first version."}};function A(e,t){let{article:o,word:r}=e.noun;if(t.length===0)return e.required?`Enter ${o} ${r}.`:null;let n=[...t].length;return n<e.minLength?`Make the ${r} at least ${String(e.minLength)} characters. It is ${String(n)} now.`:n>e.maxLength?`Keep the ${r} to ${String(e.maxLength)} characters or fewer. It is ${String(n)} now.`:t.includes("\u2014")?`The ${r} contains a long dash. Use a comma, a colon or a short dash instead.`:e.kind==="single-line"&&/[\r\n]/.test(t)?`The ${r} must be a single line.`:e.noTrailingFullStop===!0&&t.endsWith(".")?`${r.charAt(0).toUpperCase()}${r.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`:null}function g(e,t){let o={},r={};for(let n of e.fields){let i=Object.hasOwn(t,n.name)?t[n.name]:void 0,a=typeof i=="string"?i.trim():"",s=A(n,a);Object.defineProperty(s===null?o:r,n.name,{value:s??a,enumerable:!0})}return Object.keys(r).length>0?{ok:!1,errors:r}:{ok:!0,value:o}}function b(e){if(!Array.isArray(e))return{ok:!1,error:"Enter a list of taglines."};let t=y.fields[0];if(!t)throw new TypeError("Missing siteHero heading definition");let o={...t,name:"text",noun:{article:"a",word:"tagline"},maxLength:H},r=[],n=[],i=new Set;for(let[a,s]of e.entries()){if(!s||typeof s!="object"||Array.isArray(s)){n.push({index:a,error:"Enter a key and text for each tagline."});continue}let c=s,d=Object.hasOwn(c,"key")?c.key:void 0;if(typeof d!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(d)){n.push({index:a,error:"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter."});continue}if(i.has(d)){n.push({index:a,error:"Use each tagline key only once."});continue}let u=g({...y,fields:[o]},c);if(!u.ok){n.push({index:a,error:u.errors.text??"Invalid tagline."});continue}let p=u.value.text??"";if((p.match(/\p{L}/gu)?.length??0)>=2&&!/\p{Ll}/u.test(p)){n.push({index:a,error:"Use sentence case for the tagline, rather than all capitals."});continue}i.add(d),r.push({key:d,text:p})}return{ok:!0,value:r,dropped:n}}function O(e){let t=b(e);if(!t.ok)return t;let o=t.dropped[0];return o?{ok:!1,error:o.error}:{ok:!0,value:t.value}}function h(e){return S.some(t=>e===t)}function k(e,t){let o=g(y,t),r=O(t.taglines??[]);if(!o.ok||!r.ok||t.layout!==void 0&&!h(t.layout))throw new TypeError("Invalid siteHero fallback");let n={heading:o.value.heading??"",intro:o.value.intro??"",layout:t.layout??"columns",taglines:r.value};if(e===null||typeof e!="object"||Array.isArray(e))return{data:n,fallbackFields:["$"]};let i=e,a=g(y,i),s=a.ok?[]:Object.keys(a.errors);for(let l of y.fields)if(!s.includes(l.name)){let m=a.ok?a.value[l.name]:i[l.name];typeof m=="string"&&(l.name==="heading"||l.name==="intro")&&(n[l.name]=m.trim())}let c=Object.hasOwn(i,"layout")?i.layout:void 0,d=[];h(c)?n.layout=c:(n.layout="columns",s.push("layout"),d.push(E));let u=b(Object.hasOwn(i,"taglines")?i.taglines:void 0);if(u.ok){n.taglines=u.value;for(let{index:l}of u.dropped)s.push(`taglines[${String(l)}]`);u.dropped.length>0&&d.push(w)}else s.push("taglines"),d.push(x);let p=[...y.fields.map(l=>l.name),"layout","taglines"].every(l=>s.includes(l));return{data:n,fallbackFields:p?["$"]:s,...d.length>0?{warnings:d}:{}}}function L(){return f({name:"siteHero.v1",query:'*[_id == $id && _type == "siteHero"][0]{heading,intro,layout,taglines}',params:{id:"siteHero"},dependencies:{types:["siteHero"],ids:["siteHero"]},resolve:k})}export{v as CMS_CONTRACT_VERSION,y as SITE_HERO,S as SITE_HERO_LAYOUTS,E as SITE_HERO_LAYOUT_WARNING,x as SITE_HERO_TAGLINES_DEFAULTED_WARNING,w as SITE_HERO_TAGLINE_DROPPED_WARNING,H as SITE_HERO_TAGLINE_MAX_LENGTH,I as cmsTags,f as defineCmsQuery,k as parseSiteHero,L as siteHeroQuery,O as validateSiteHeroTaglines,g as validateTextDocument};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/contracts/query.ts", "../src/contracts/text.ts"],
4
- "sourcesContent": ["/** A-1822 S1 owns this implementation. S0 registers the public signatures. */\nexport type CmsParams = Readonly<Record<string, string | number | boolean | null>>;\nexport type CmsDependencies = {\n readonly types: readonly string[];\n readonly ids?: readonly string[];\n};\nexport type CmsResolution<T> = {\n readonly data: T;\n readonly fallbackFields: readonly string[];\n readonly warnings?: readonly \"siteHero.layout.defaulted\"[];\n};\nexport type CmsQuery<T> = {\n readonly name: string;\n readonly query: string;\n readonly params: CmsParams;\n readonly dependencies: CmsDependencies;\n readonly resolve: (raw: unknown, fallback: T) => CmsResolution<T>;\n};\n\nconst TYPE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;\nconst ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\nconst SCOPE = /^[a-z0-9]{6,12}\\.[a-z][a-z0-9_-]{0,63}$/;\n\nfunction dependenciesCopy(dependencies: CmsDependencies): CmsDependencies {\n if (!dependencies || !Array.isArray(dependencies.types) || dependencies.types.length === 0) {\n throw new TypeError(\"CMS dependencies require document types\");\n }\n const types = [...dependencies.types];\n for (const type of types) {\n if (typeof type !== \"string\" || !TYPE.test(type)) throw new TypeError(\"Invalid CMS document type\");\n }\n if (dependencies.ids !== undefined && !Array.isArray(dependencies.ids)) throw new TypeError(\"Invalid CMS document IDs\");\n const ids = dependencies.ids === undefined ? undefined : [...dependencies.ids];\n for (const id of ids ?? []) {\n if (typeof id !== \"string\" || !ID.test(id) || /^(drafts|versions)\\./.test(id)) {\n throw new TypeError(\"Invalid CMS document ID\");\n }\n }\n if (new Set(types).size + new Set(ids).size > 128) throw new TypeError(\"Too many CMS tags\");\n return Object.freeze({ types: Object.freeze(types), ...(ids === undefined ? {} : { ids: Object.freeze(ids) }) });\n}\n\nexport function defineCmsQuery<T>(query: CmsQuery<T>): CmsQuery<T> {\n if (!query || typeof query.name !== \"string\" || !/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(query.name)\n || typeof query.query !== \"string\" || query.query.trim() === \"\" || typeof query.resolve !== \"function\") {\n throw new TypeError(\"Invalid CMS query configuration\");\n }\n const params = query.params;\n if (!params || typeof params !== \"object\" || (Object.getPrototypeOf(params) !== Object.prototype && Object.getPrototypeOf(params) !== null)) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n const copied: Record<string, string | number | boolean | null> = {};\n for (const key of Reflect.ownKeys(params)) {\n const descriptor = Object.getOwnPropertyDescriptor(params, key);\n if (typeof key !== \"string\" || !descriptor || !Object.hasOwn(descriptor, \"value\") || !descriptor.enumerable) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n const value: unknown = descriptor.value;\n if (!(value === null || typeof value === \"string\" || typeof value === \"boolean\" || (typeof value === \"number\" && Number.isFinite(value)))) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n Object.defineProperty(copied, key, { value, enumerable: true });\n }\n return Object.freeze({ name: query.name, query: query.query, params: Object.freeze(copied), dependencies: dependenciesCopy(query.dependencies), resolve: query.resolve });\n}\n\nexport function cmsTags(scope: string, dependencies: CmsDependencies): readonly string[] {\n if (typeof scope !== \"string\" || scope.length > 77 || !SCOPE.test(scope)) throw new TypeError(\"Invalid CMS scope\");\n const copied = dependenciesCopy(dependencies);\n const tags = [\n ...[...new Set(copied.types)].sort().map((type) => `cms:${scope}:type:${type}`),\n ...[...new Set(copied.ids ?? [])].sort().map((id) => `cms:${scope}:id:${id}`),\n ];\n return Object.freeze(tags);\n}\n", "import { defineCmsQuery, type CmsQuery, type CmsResolution } from \"./query.js\";\n\nexport const SITE_HERO_LAYOUTS = [\"columns\", \"rows\"] as const;\nexport const SITE_HERO_TAGLINE_MAX_LENGTH = 36;\nexport const SITE_HERO_LAYOUT_WARNING = \"siteHero.layout.defaulted\" as const;\nexport type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];\nexport type SiteHeroTagline = { readonly key: string; readonly text: string };\n/** Optional additions preserve existing heading/intro-only consumer fallbacks. */\nexport type SiteHero = {\n readonly heading: string;\n readonly intro: string;\n readonly layout?: SiteHeroLayout;\n readonly taglines?: readonly SiteHeroTagline[];\n};\nexport type ParsedSiteHero = SiteHero & {\n readonly layout: SiteHeroLayout;\n readonly taglines: readonly SiteHeroTagline[];\n};\nexport type TextFieldDefinition = {\n readonly name: string;\n readonly label: string;\n readonly noun: { readonly article: \"a\" | \"an\"; readonly word: string };\n readonly kind: \"single-line\" | \"multi-line\";\n readonly required: boolean;\n readonly minLength: number;\n readonly maxLength: number;\n readonly noTrailingFullStop?: boolean;\n};\nexport type TextDocumentDefinition = {\n readonly key: string;\n readonly documentId: string;\n readonly documentType: string;\n readonly fields: readonly TextFieldDefinition[];\n readonly copy: {\n readonly currentTitle: string;\n readonly editTitle: string;\n readonly empty: string;\n };\n};\nexport type TextValidation =\n | { readonly ok: true; readonly value: Readonly<Record<string, string>> }\n | { readonly ok: false; readonly errors: Readonly<Record<string, string>> };\n\nexport const CMS_CONTRACT_VERSION = 1;\n\nexport const SITE_HERO: TextDocumentDefinition = {\n key: \"hero\",\n documentId: \"siteHero\",\n documentType: \"siteHero\",\n fields: [\n {\n name: \"heading\",\n label: \"Heading\",\n noun: { article: \"a\", word: \"heading\" },\n kind: \"single-line\",\n required: true,\n minLength: 1,\n maxLength: 80,\n noTrailingFullStop: true,\n },\n {\n name: \"intro\",\n label: \"Introduction\",\n noun: { article: \"an\", word: \"introduction\" },\n kind: \"multi-line\",\n required: true,\n minLength: 1,\n maxLength: 320,\n },\n ],\n copy: {\n currentTitle: \"On your site now\",\n editTitle: \"Edit and publish\",\n empty: \"Your site has no hero text yet. Whatever you publish below becomes its first version.\",\n },\n};\n\nfunction fieldError(field: TextFieldDefinition, text: string): string | null {\n const { article, word } = field.noun;\n if (text.length === 0) return field.required ? `Enter ${article} ${word}.` : null;\n const count = [...text].length;\n if (count < field.minLength) return `Make the ${word} at least ${String(field.minLength)} characters. It is ${String(count)} now.`;\n if (count > field.maxLength) return `Keep the ${word} to ${String(field.maxLength)} characters or fewer. It is ${String(count)} now.`;\n if (text.includes(\"\\u2014\")) return `The ${word} contains a long dash. Use a comma, a colon or a short dash instead.`;\n if (field.kind === \"single-line\" && /[\\r\\n]/.test(text)) return `The ${word} must be a single line.`;\n if (field.noTrailingFullStop === true && text.endsWith(\".\")) {\n return `${word.charAt(0).toUpperCase()}${word.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`;\n }\n return null;\n}\n\nexport function validateTextDocument(\n definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>,\n): TextValidation {\n const value: Record<string, string> = {};\n const errors: Record<string, string> = {};\n for (const field of definition.fields) {\n const submitted = Object.hasOwn(raw, field.name) ? raw[field.name] : undefined;\n const text = typeof submitted === \"string\" ? submitted.trim() : \"\";\n const error = fieldError(field, text);\n Object.defineProperty(error === null ? value : errors, field.name, { value: error ?? text, enumerable: true });\n }\n return Object.keys(errors).length > 0 ? { ok: false, errors } : { ok: true, value };\n}\n\n/** Shared with the schema adapter; no provider metadata is returned. */\nexport function validateSiteHeroTaglines(raw: unknown):\n | { readonly ok: true; readonly value: readonly SiteHeroTagline[] }\n | { readonly ok: false; readonly error: string } {\n if (!Array.isArray(raw)) return { ok: false, error: \"Enter a list of taglines.\" };\n const heading = SITE_HERO.fields[0];\n if (!heading) throw new TypeError(\"Missing siteHero heading definition\");\n const field = { ...heading, name: \"text\", noun: { article: \"a\", word: \"tagline\" } as const,\n maxLength: SITE_HERO_TAGLINE_MAX_LENGTH };\n const value: SiteHeroTagline[] = [];\n const keys = new Set<string>();\n for (const item of raw as unknown[]) {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) return { ok: false, error: \"Enter a key and text for each tagline.\" };\n const record = item as Record<string, unknown>;\n const key = Object.hasOwn(record, \"key\") ? record.key : undefined;\n if (typeof key !== \"string\" || !/^[a-z][a-z0-9_-]{0,31}$/.test(key)) {\n return { ok: false, error: \"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter.\" };\n }\n if (keys.has(key)) return { ok: false, error: \"Use each tagline key only once.\" };\n const text = validateTextDocument({ ...SITE_HERO, fields: [field] }, record);\n if (!text.ok) return { ok: false, error: text.errors.text ?? \"Invalid tagline.\" };\n keys.add(key);\n value.push({ key, text: text.value.text ?? \"\" });\n }\n return { ok: true, value };\n}\n\nfunction isLayout(value: unknown): value is SiteHeroLayout {\n return SITE_HERO_LAYOUTS.some((layout) => value === layout);\n}\n\n/** Strict per-field resolution: an invalid list never partially changes keyed copy. */\nexport function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero> {\n const local = validateTextDocument(SITE_HERO, fallback);\n const localTaglines = validateSiteHeroTaglines(fallback.taglines ?? []);\n if (!local.ok || !localTaglines.ok || (fallback.layout !== undefined && !isLayout(fallback.layout))) {\n throw new TypeError(\"Invalid siteHero fallback\");\n }\n const data: { heading: string; intro: string; layout: SiteHeroLayout; taglines: readonly SiteHeroTagline[] } = {\n heading: local.value.heading ?? \"\", intro: local.value.intro ?? \"\",\n layout: fallback.layout ?? \"columns\", taglines: localTaglines.value,\n };\n if (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) return { data, fallbackFields: [\"$\"] };\n const record = raw as Readonly<Record<string, unknown>>;\n const text = validateTextDocument(SITE_HERO, record);\n const fallbackFields: string[] = text.ok ? [] : Object.keys(text.errors);\n for (const field of SITE_HERO.fields) {\n if (!fallbackFields.includes(field.name)) {\n const value = text.ok ? text.value[field.name] : record[field.name];\n if (typeof value === \"string\" && (field.name === \"heading\" || field.name === \"intro\")) data[field.name] = value.trim();\n }\n }\n const layout = Object.hasOwn(record, \"layout\") ? record.layout : undefined;\n const warnings: (typeof SITE_HERO_LAYOUT_WARNING)[] = [];\n if (isLayout(layout)) data.layout = layout;\n else {\n data.layout = \"columns\";\n fallbackFields.push(\"layout\");\n warnings.push(SITE_HERO_LAYOUT_WARNING);\n }\n const taglines = validateSiteHeroTaglines(Object.hasOwn(record, \"taglines\") ? record.taglines : undefined);\n if (taglines.ok) data.taglines = taglines.value;\n else fallbackFields.push(\"taglines\");\n return { data, fallbackFields: fallbackFields.length === SITE_HERO.fields.length + 2 ? [\"$\"] : fallbackFields,\n ...(warnings.length > 0 ? { warnings } : {}) };\n}\n\nexport function siteHeroQuery(): CmsQuery<SiteHero> {\n return defineCmsQuery<SiteHero>({\n name: \"siteHero.v1\", query: '*[_id == $id && _type == \"siteHero\"][0]{heading,intro,layout,taglines}',\n params: { id: \"siteHero\" }, dependencies: { types: [\"siteHero\"], ids: [\"siteHero\"] },\n resolve: parseSiteHero,\n });\n}\n"],
5
- "mappings": "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",
6
- "names": ["TYPE", "ID", "SCOPE", "dependenciesCopy", "dependencies", "types", "type", "ids", "id", "defineCmsQuery", "query", "params", "copied", "key", "descriptor", "value", "cmsTags", "scope", "tags", "SITE_HERO_LAYOUTS", "SITE_HERO_TAGLINE_MAX_LENGTH", "SITE_HERO_LAYOUT_WARNING", "CMS_CONTRACT_VERSION", "SITE_HERO", "fieldError", "field", "text", "article", "word", "count", "validateTextDocument", "definition", "raw", "value", "errors", "submitted", "error", "validateSiteHeroTaglines", "heading", "keys", "item", "record", "key", "isLayout", "layout", "parseSiteHero", "fallback", "local", "localTaglines", "data", "fallbackFields", "warnings", "taglines", "siteHeroQuery", "defineCmsQuery"]
4
+ "sourcesContent": ["/** A-1822 S1 owns this implementation. S0 registers the public signatures. */\nexport type CmsParams = Readonly<Record<string, string | number | boolean | null>>;\nexport type CmsDependencies = {\n readonly types: readonly string[];\n readonly ids?: readonly string[];\n};\nexport type CmsResolution<T> = {\n readonly data: T;\n readonly fallbackFields: readonly string[];\n readonly warnings?: readonly (\"siteHero.layout.defaulted\" | \"siteHero.tagline.dropped\" | \"siteHero.taglines.defaulted\")[];\n};\nexport type CmsQuery<T> = {\n readonly name: string;\n readonly query: string;\n readonly params: CmsParams;\n readonly dependencies: CmsDependencies;\n readonly resolve: (raw: unknown, fallback: T) => CmsResolution<T>;\n};\n\nconst TYPE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;\nconst ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\nconst SCOPE = /^[a-z0-9]{6,12}\\.[a-z][a-z0-9_-]{0,63}$/;\n\nfunction dependenciesCopy(dependencies: CmsDependencies): CmsDependencies {\n if (!dependencies || !Array.isArray(dependencies.types) || dependencies.types.length === 0) {\n throw new TypeError(\"CMS dependencies require document types\");\n }\n const types = [...dependencies.types];\n for (const type of types) {\n if (typeof type !== \"string\" || !TYPE.test(type)) throw new TypeError(\"Invalid CMS document type\");\n }\n if (dependencies.ids !== undefined && !Array.isArray(dependencies.ids)) throw new TypeError(\"Invalid CMS document IDs\");\n const ids = dependencies.ids === undefined ? undefined : [...dependencies.ids];\n for (const id of ids ?? []) {\n if (typeof id !== \"string\" || !ID.test(id) || /^(drafts|versions)\\./.test(id)) {\n throw new TypeError(\"Invalid CMS document ID\");\n }\n }\n if (new Set(types).size + new Set(ids).size > 128) throw new TypeError(\"Too many CMS tags\");\n return Object.freeze({ types: Object.freeze(types), ...(ids === undefined ? {} : { ids: Object.freeze(ids) }) });\n}\n\nexport function defineCmsQuery<T>(query: CmsQuery<T>): CmsQuery<T> {\n if (!query || typeof query.name !== \"string\" || !/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(query.name)\n || typeof query.query !== \"string\" || query.query.trim() === \"\" || typeof query.resolve !== \"function\") {\n throw new TypeError(\"Invalid CMS query configuration\");\n }\n const params = query.params;\n if (!params || typeof params !== \"object\" || (Object.getPrototypeOf(params) !== Object.prototype && Object.getPrototypeOf(params) !== null)) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n const copied: Record<string, string | number | boolean | null> = {};\n for (const key of Reflect.ownKeys(params)) {\n const descriptor = Object.getOwnPropertyDescriptor(params, key);\n if (typeof key !== \"string\" || !descriptor || !Object.hasOwn(descriptor, \"value\") || !descriptor.enumerable) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n const value: unknown = descriptor.value;\n if (!(value === null || typeof value === \"string\" || typeof value === \"boolean\" || (typeof value === \"number\" && Number.isFinite(value)))) {\n throw new TypeError(\"CMS params must be plain JSON scalars\");\n }\n Object.defineProperty(copied, key, { value, enumerable: true });\n }\n return Object.freeze({ name: query.name, query: query.query, params: Object.freeze(copied), dependencies: dependenciesCopy(query.dependencies), resolve: query.resolve });\n}\n\nexport function cmsTags(scope: string, dependencies: CmsDependencies): readonly string[] {\n if (typeof scope !== \"string\" || scope.length > 77 || !SCOPE.test(scope)) throw new TypeError(\"Invalid CMS scope\");\n const copied = dependenciesCopy(dependencies);\n const tags = [\n ...[...new Set(copied.types)].sort().map((type) => `cms:${scope}:type:${type}`),\n ...[...new Set(copied.ids ?? [])].sort().map((id) => `cms:${scope}:id:${id}`),\n ];\n return Object.freeze(tags);\n}\n", "import { defineCmsQuery, type CmsQuery, type CmsResolution } from \"./query.js\";\n\nexport const SITE_HERO_LAYOUTS = [\"columns\", \"rows\"] as const;\nexport const SITE_HERO_TAGLINE_MAX_LENGTH = 40;\nexport const SITE_HERO_LAYOUT_WARNING = \"siteHero.layout.defaulted\" as const;\nexport const SITE_HERO_TAGLINE_DROPPED_WARNING = \"siteHero.tagline.dropped\" as const;\nexport const SITE_HERO_TAGLINES_DEFAULTED_WARNING = \"siteHero.taglines.defaulted\" as const;\nexport type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];\nexport type SiteHeroTagline = { readonly key: string; readonly text: string };\n/** Optional additions preserve existing heading/intro-only consumer fallbacks. */\nexport type SiteHero = {\n readonly heading: string;\n readonly intro: string;\n readonly layout?: SiteHeroLayout;\n readonly taglines?: readonly SiteHeroTagline[];\n};\nexport type ParsedSiteHero = SiteHero & {\n readonly layout: SiteHeroLayout;\n readonly taglines: readonly SiteHeroTagline[];\n};\nexport type TextFieldDefinition = {\n readonly name: string;\n readonly label: string;\n readonly noun: { readonly article: \"a\" | \"an\"; readonly word: string };\n readonly kind: \"single-line\" | \"multi-line\";\n readonly required: boolean;\n readonly minLength: number;\n readonly maxLength: number;\n readonly noTrailingFullStop?: boolean;\n};\nexport type TextDocumentDefinition = {\n readonly key: string;\n readonly documentId: string;\n readonly documentType: string;\n readonly fields: readonly TextFieldDefinition[];\n readonly copy: {\n readonly currentTitle: string;\n readonly editTitle: string;\n readonly empty: string;\n };\n};\nexport type TextValidation =\n | { readonly ok: true; readonly value: Readonly<Record<string, string>> }\n | { readonly ok: false; readonly errors: Readonly<Record<string, string>> };\n\nexport const CMS_CONTRACT_VERSION = 1;\n\nexport const SITE_HERO: TextDocumentDefinition = {\n key: \"hero\",\n documentId: \"siteHero\",\n documentType: \"siteHero\",\n fields: [\n {\n name: \"heading\",\n label: \"Heading\",\n noun: { article: \"a\", word: \"heading\" },\n kind: \"single-line\",\n required: true,\n minLength: 1,\n maxLength: 80,\n noTrailingFullStop: true,\n },\n {\n name: \"intro\",\n label: \"Introduction\",\n noun: { article: \"an\", word: \"introduction\" },\n kind: \"multi-line\",\n required: true,\n minLength: 1,\n maxLength: 320,\n },\n ],\n copy: {\n currentTitle: \"On your site now\",\n editTitle: \"Edit and publish\",\n empty: \"Your site has no hero text yet. Whatever you publish below becomes its first version.\",\n },\n};\n\nfunction fieldError(field: TextFieldDefinition, text: string): string | null {\n const { article, word } = field.noun;\n if (text.length === 0) return field.required ? `Enter ${article} ${word}.` : null;\n const count = [...text].length;\n if (count < field.minLength) return `Make the ${word} at least ${String(field.minLength)} characters. It is ${String(count)} now.`;\n if (count > field.maxLength) return `Keep the ${word} to ${String(field.maxLength)} characters or fewer. It is ${String(count)} now.`;\n if (text.includes(\"\\u2014\")) return `The ${word} contains a long dash. Use a comma, a colon or a short dash instead.`;\n if (field.kind === \"single-line\" && /[\\r\\n]/.test(text)) return `The ${word} must be a single line.`;\n if (field.noTrailingFullStop === true && text.endsWith(\".\")) {\n return `${word.charAt(0).toUpperCase()}${word.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`;\n }\n return null;\n}\n\nexport function validateTextDocument(\n definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>,\n): TextValidation {\n const value: Record<string, string> = {};\n const errors: Record<string, string> = {};\n for (const field of definition.fields) {\n const submitted = Object.hasOwn(raw, field.name) ? raw[field.name] : undefined;\n const text = typeof submitted === \"string\" ? submitted.trim() : \"\";\n const error = fieldError(field, text);\n Object.defineProperty(error === null ? value : errors, field.name, { value: error ?? text, enumerable: true });\n }\n return Object.keys(errors).length > 0 ? { ok: false, errors } : { ok: true, value };\n}\n\n/** One rule set for strict authoring validation and resilient published reads. */\nfunction scanSiteHeroTaglines(raw: unknown):\n | { readonly ok: true; readonly value: readonly SiteHeroTagline[]; readonly dropped: readonly { index: number; error: string }[] }\n | { readonly ok: false; readonly error: string } {\n if (!Array.isArray(raw)) return { ok: false, error: \"Enter a list of taglines.\" };\n const heading = SITE_HERO.fields[0];\n if (!heading) throw new TypeError(\"Missing siteHero heading definition\");\n const field = { ...heading, name: \"text\", noun: { article: \"a\", word: \"tagline\" } as const,\n maxLength: SITE_HERO_TAGLINE_MAX_LENGTH };\n const value: SiteHeroTagline[] = [];\n const dropped: { index: number; error: string }[] = [];\n const keys = new Set<string>();\n for (const [index, item] of (raw as unknown[]).entries()) {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) {\n dropped.push({ index, error: \"Enter a key and text for each tagline.\" });\n continue;\n }\n const record = item as Record<string, unknown>;\n const key = Object.hasOwn(record, \"key\") ? record.key : undefined;\n if (typeof key !== \"string\" || !/^[a-z][a-z0-9_-]{0,31}$/.test(key)) {\n dropped.push({ index, error: \"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter.\" });\n continue;\n }\n if (keys.has(key)) {\n dropped.push({ index, error: \"Use each tagline key only once.\" });\n continue;\n }\n const text = validateTextDocument({ ...SITE_HERO, fields: [field] }, record);\n if (!text.ok) {\n dropped.push({ index, error: text.errors.text ?? \"Invalid tagline.\" });\n continue;\n }\n const copy = text.value.text ?? \"\";\n if ((copy.match(/\\p{L}/gu)?.length ?? 0) >= 2 && !/\\p{Ll}/u.test(copy)) {\n dropped.push({ index, error: \"Use sentence case for the tagline, rather than all capitals.\" });\n continue;\n }\n keys.add(key);\n value.push({ key, text: copy });\n }\n return { ok: true, value, dropped };\n}\n\n/** Shared with the schema adapter; invalid authoring input is refused as a whole. */\nexport function validateSiteHeroTaglines(raw: unknown):\n | { readonly ok: true; readonly value: readonly SiteHeroTagline[] }\n | { readonly ok: false; readonly error: string } {\n const result = scanSiteHeroTaglines(raw);\n if (!result.ok) return result;\n const first = result.dropped[0];\n return first ? { ok: false, error: first.error } : { ok: true, value: result.value };\n}\n\nfunction isLayout(value: unknown): value is SiteHeroLayout {\n return SITE_HERO_LAYOUTS.some((layout) => value === layout);\n}\n\n/** Invalid published entries are dropped without discarding valid keyed siblings. */\nexport function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero> {\n const local = validateTextDocument(SITE_HERO, fallback);\n const localTaglines = validateSiteHeroTaglines(fallback.taglines ?? []);\n if (!local.ok || !localTaglines.ok || (fallback.layout !== undefined && !isLayout(fallback.layout))) {\n throw new TypeError(\"Invalid siteHero fallback\");\n }\n const data: { heading: string; intro: string; layout: SiteHeroLayout; taglines: readonly SiteHeroTagline[] } = {\n heading: local.value.heading ?? \"\", intro: local.value.intro ?? \"\",\n layout: fallback.layout ?? \"columns\", taglines: localTaglines.value,\n };\n if (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) return { data, fallbackFields: [\"$\"] };\n const record = raw as Readonly<Record<string, unknown>>;\n const text = validateTextDocument(SITE_HERO, record);\n const fallbackFields: string[] = text.ok ? [] : Object.keys(text.errors);\n for (const field of SITE_HERO.fields) {\n if (!fallbackFields.includes(field.name)) {\n const value = text.ok ? text.value[field.name] : record[field.name];\n if (typeof value === \"string\" && (field.name === \"heading\" || field.name === \"intro\")) data[field.name] = value.trim();\n }\n }\n const layout = Object.hasOwn(record, \"layout\") ? record.layout : undefined;\n const warnings: NonNullable<CmsResolution<ParsedSiteHero>[\"warnings\"]>[number][] = [];\n if (isLayout(layout)) data.layout = layout;\n else {\n data.layout = \"columns\";\n fallbackFields.push(\"layout\");\n warnings.push(SITE_HERO_LAYOUT_WARNING);\n }\n const taglines = scanSiteHeroTaglines(Object.hasOwn(record, \"taglines\") ? record.taglines : undefined);\n if (taglines.ok) {\n data.taglines = taglines.value;\n for (const { index } of taglines.dropped) fallbackFields.push(`taglines[${String(index)}]`);\n if (taglines.dropped.length > 0) warnings.push(SITE_HERO_TAGLINE_DROPPED_WARNING);\n } else {\n fallbackFields.push(\"taglines\");\n warnings.push(SITE_HERO_TAGLINES_DEFAULTED_WARNING);\n }\n const wholeFallback = [...SITE_HERO.fields.map((field) => field.name), \"layout\", \"taglines\"].every((field) => fallbackFields.includes(field));\n return { data, fallbackFields: wholeFallback ? [\"$\"] : fallbackFields,\n ...(warnings.length > 0 ? { warnings } : {}) };\n}\n\nexport function siteHeroQuery(): CmsQuery<SiteHero> {\n return defineCmsQuery<SiteHero>({\n name: \"siteHero.v1\", query: '*[_id == $id && _type == \"siteHero\"][0]{heading,intro,layout,taglines}',\n params: { id: \"siteHero\" }, dependencies: { types: [\"siteHero\"], ids: [\"siteHero\"] },\n resolve: parseSiteHero,\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["TYPE", "ID", "SCOPE", "dependenciesCopy", "dependencies", "types", "type", "ids", "id", "defineCmsQuery", "query", "params", "copied", "key", "descriptor", "value", "cmsTags", "scope", "tags", "SITE_HERO_LAYOUTS", "SITE_HERO_TAGLINE_MAX_LENGTH", "SITE_HERO_LAYOUT_WARNING", "SITE_HERO_TAGLINE_DROPPED_WARNING", "SITE_HERO_TAGLINES_DEFAULTED_WARNING", "CMS_CONTRACT_VERSION", "SITE_HERO", "fieldError", "field", "text", "article", "word", "count", "validateTextDocument", "definition", "raw", "value", "errors", "submitted", "error", "scanSiteHeroTaglines", "heading", "dropped", "keys", "index", "item", "record", "key", "copy", "validateSiteHeroTaglines", "result", "first", "isLayout", "layout", "parseSiteHero", "fallback", "local", "localTaglines", "data", "fallbackFields", "warnings", "taglines", "wholeFallback", "siteHeroQuery", "defineCmsQuery"]
7
7
  }