@getquick/content 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,7 +38,7 @@ confirmed absence returns missing, and successful reads return found.
38
38
 
39
39
  The root entry contains pure helpers and public structural types. The server
40
40
  entry exports `createContentRuntime`, `ContentConfig`, `ContentExtension`,
41
- `ExtensionNode`, `ContentBindings`, `ContentRuntime` and `WordPressClient`. Runtime properties contain languages,
41
+ `ExtensionNode`, `CollectionConfig`, `ContentBindings`, `ContentRuntime` and `WordPressClient`. Runtime properties contain languages,
42
42
  routing functions and the WordPress methods; credentials/configuration are kept
43
43
  in closures. Language configuration retains the source's normalization policy,
44
44
  and routing returns the same language objects held in that instance's array.
@@ -50,7 +50,9 @@ The reusable client methods retain their argument order:
50
50
  - `getDesignPresets()`
51
51
  - `getPublishedEntries()`
52
52
  - `getPublishedRoutes()`
53
- - `getSiteChrome(siteOrigin?, { location?, language? }?)`
53
+ - `getArchives()`
54
+ - `getSiteChrome(siteOrigin?, { location?, language? }?)` (menu, logo, icon,
55
+ header and footer patterns)
54
56
 
55
57
  From the repository root, use `vp install` and `vp run validate`. Validation
56
58
  builds compiled ESM/declarations through Vite+ Pack, checks formatting/lint/types,
@@ -109,6 +111,116 @@ Rows stored before the Site declared it serve without it; a refresh stores it.
109
111
  A read throwing fails as WordPress's answer not matching the schema, and a read
110
112
  without its blocks stores nothing, so the stored page stays served. See ADR 0004.
111
113
 
114
+ ## Header and footer patterns
115
+
116
+ The chrome also carries `header` and `footer`: the synced patterns GQ Design's
117
+ Header & Footer selects for the design route `designTemplate` (default
118
+ `"page"`; also `"front-page"`, `"home"`, `"archive"`, `"single-post"` or a
119
+ Site-defined route). Each is `{ title, blocks }`, or null when the region uses
120
+ a template part or nothing published is selected: keep the Site's own header
121
+ or footer then. They are stored, refreshed and reconciled with the chrome, so
122
+ visitors keep the last-known-good regions through CMS outages. A selected
123
+ pattern GQ Design resolves to no blocks fails the chrome read and keeps what
124
+ is stored. A `patterns` settings event refreshes the chrome. See
125
+ [ADR 0006](../docs/adr/0006-serve-cms-selected-header-and-footer-patterns-with-site-chrome.md).
126
+
127
+ The configured route is shared globally, including the homepage and posts;
128
+ Content does not choose a different Design route for each visitor URL. Use a
129
+ key registered in Design's manifest: an unknown key also returns null, and
130
+ Content never retries another key.
131
+
132
+ **Required GQ Design: 0.4.1 or newer** for the default `page` (`single` and
133
+ `search` need 0.4.2). GQ Design 0.2.17 to 0.4.0 answer null for `page`, so the
134
+ Site keeps its own regions. A GQ Design without `designPattern` at all (before
135
+ 0.2.17) doesn't fail the chrome: it is read without patterns, both null, and a
136
+ warning names GQ Design and the version it needs (`WordPress: GQ Design has no
137
+ designPattern field …`). Public selection excludes unpublished
138
+ route/part sources and unpublished, protected or unsynced final patterns.
139
+ Authoritative null removes a previous selection on a successful refresh;
140
+ failed or malformed reads retain it.
141
+
142
+ The ordinary menu and publication-store row remain language-specific, but
143
+ `designPattern` takes no language. Every language receives the same unqualified
144
+ pattern selection and its serialized data. Translated pattern trees are blocked
145
+ by [gq-design #53](https://github.com/Quick-Release/gq-design/issues/53).
146
+
147
+ Render a pattern with the Site's block renderers. Blocks with saved HTML
148
+ render it with their children inside; `core/site-logo` has a built-in
149
+ renderer; any other block without HTML (a dynamic or Site block) needs a
150
+ renderer. Containers receive a basic WordPress wrapper, and GQ Design's
151
+ `getquick-design/container` its layout (width, gap, padding) as content's
152
+ containers do; unresolved dynamic leaves render nothing and are reported to
153
+ `onUnrendered`:
154
+
155
+ ```ts
156
+ import { designMenuItems, escapeHtml, type DesignBlockRenderer } from "@getquick/content";
157
+
158
+ const headerNavigation: DesignBlockRenderer = (block, { href }) =>
159
+ `<nav aria-label="Primary"><ul>${designMenuItems(block.attributes.menuItems, href)
160
+ .map((item) => `<li><a href="${escapeHtml(item.href)}">${escapeHtml(item.label)}</a></li>`)
161
+ .join("")}</ul></nav>`;
162
+
163
+ const header = content.renderDesignPattern(chrome?.header, {
164
+ siteOrigin: Astro.site?.origin,
165
+ home: language.home,
166
+ blocks: { "getquick/header-navigation": headerNavigation },
167
+ onUnrendered: (block) => console.warn(`No renderer for ${block.name}`),
168
+ });
169
+ ```
170
+
171
+ A renderer receives the block (`name`, `attributes`, `content`, `children`)
172
+ and a context: `children` (rendered on demand), `render(blocks)`,
173
+ `ancestors`, `home` and `href(url)`, which serves CMS and Site links as paths
174
+ and replaces unsafe schemes with `#`. Its output is inserted as HTML: escape
175
+ CMS text with `escapeHtml`. Saved HTML is trusted published CMS markup,
176
+ not sanitized by this helper; full custom spacing/layout styling needs a Site
177
+ renderer. With `consent` configured, the saved HTML in served patterns has its
178
+ embeds rewritten into consent placeholders, as bodies do; a renderer building
179
+ an embed from attributes uses `embedPlaceholder` (`@getquick/content/consent`)
180
+ itself. The pure `renderDesignPattern` from the root entry
181
+ takes `internalOrigins` instead of `siteOrigin`.
182
+
183
+ ## Collections and archive Pages
184
+
185
+ A Site stores its custom post types' items as collections, and lists them from
186
+ the publication store alone (never WordPress), for an archive Page's listing
187
+ block:
188
+
189
+ ```ts
190
+ const content = createContentRuntime({
191
+ // …the configuration above…
192
+ collections: {
193
+ biblioteca: {
194
+ postType: "biblioteca", // WordPress's name
195
+ graphqlType: "Biblioteca", // its graphql_single_name
196
+ taxonomies: ["categoria_biblioteca"], // each show_in_graphql
197
+ },
198
+ },
199
+ });
200
+ const page = await content.list({
201
+ collection: "biblioteca",
202
+ terms: { categoria_biblioteca: ["legislacao"] }, // any slug; every taxonomy given slugs
203
+ search: "lei", // title and excerpt, ignoring case and accents
204
+ page: 1, // a number (or numeric string); anything else throws
205
+ perPage: 12,
206
+ });
207
+ // { kind: "found", items: [{ uri, title, date, modifiedAt, excerpt,
208
+ // featuredImage, terms, extension?, language? }], total, page, pages }
209
+ // or { kind: "unavailable", failure }
210
+ const filters = await content.terms("biblioteca", "categoria_biblioteca");
211
+ // { kind: "found", terms: [{ slug, name, count }] }
212
+ const archive = await content.archiveUri("biblioteca"); // "/biblioteca/" or null
213
+ ```
214
+
215
+ Items are entries: events, withdrawals and reconciliation keep them, and
216
+ `publishedEntry` serves them with `entry.collection` (`name`, `terms`). The
217
+ extension's `fields.collections[<name>]` selects an item's own fields (`read`
218
+ gets `kind: "item"` and `collection`). In WordPress, GETQUICK → Archives
219
+ assigns each type a Page, which takes over the type's archive address; saving
220
+ it reads every archive address again. A type or taxonomy WPGraphQL doesn't
221
+ show, a `graphqlType` that isn't the type's, or a taxonomy not connected to
222
+ it fails the archives read, by name. See ADR 0005.
223
+
112
224
  Infrastructure imports the Node-only migration entry separately:
113
225
 
114
226
  ```ts
@@ -1,6 +1,6 @@
1
1
  import { S as SERVICE_CATALOG, T as copyLanguage, c as grantedCategories, d as promptReason, f as readConsentCookie, x as OPTIONAL_CATEGORIES } from "./state-BYWc5TUB.js";
2
2
  import { a as CONSENT_COPY, c as legalRevisions, r as escapeHtml } from "./embeds-p9SyGNZI.js";
3
- import { t as CONSENT_UI_VERSION } from "./version-DyyNhsuU.js";
3
+ import { t as CONSENT_UI_VERSION } from "./version-ChwS3fUu.js";
4
4
  import { t as CONSENT_CONFIG_ID } from "./client-config-B3LNSOxl.js";
5
5
  import { t as CONSENT_PATH } from "./paths-Cmzzof3v.js";
6
6
  //#region frontend/src/consent/astro.ts
package/dist/consent.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as SERVICE_IDS, D as hostMatches, E as embedService, O as hostService, S as SERVICE_CATALOG, T as copyLanguage, _ as consentConfiguration, a as consentCookie, b as COPY_LANGUAGES, c as grantedCategories, d as promptReason, f as readConsentCookie, g as MEASUREMENT_ID, h as CONSENT_COPY_REVISION, i as DECISION_VALIDITY_MS, k as isServiceId, l as isConsentId, m as serviceGranted, n as CONSENT_COOKIE, o as decide, p as serializeConsentState, r as CONSENT_COOKIE_MAX_AGE_SECONDS, s as denyAll, t as CONSENT_ACTIONS, u as parseConsentState, v as needsBanner, w as catalogService, x as OPTIONAL_CATEGORIES, y as CONSENT_CATEGORIES } from "./state-BYWc5TUB.js";
2
2
  import { a as parseConsentRecordRequest, i as isRecordToken, n as consentRecordRequest, r as isLegalRoleName, t as MAX_RECORD_BYTES } from "./records-Dumrz759.js";
3
3
  import { a as CONSENT_COPY, c as legalRevisions, i as rewriteEmbeds, l as parseLegalLanguage, n as embedPlaceholder, o as LEGAL_ROLES, r as escapeHtml, s as REQUIRED_LEGAL_ROLES, t as blockedPlaceholder } from "./embeds-p9SyGNZI.js";
4
- import { t as CONSENT_UI_VERSION } from "./version-DyyNhsuU.js";
4
+ import { t as CONSENT_UI_VERSION } from "./version-ChwS3fUu.js";
5
5
  export { CONSENT_ACTIONS, CONSENT_CATEGORIES, CONSENT_COOKIE, CONSENT_COOKIE_MAX_AGE_SECONDS, CONSENT_COPY, CONSENT_COPY_REVISION, CONSENT_UI_VERSION, COPY_LANGUAGES, DECISION_VALIDITY_MS, LEGAL_ROLES, MAX_RECORD_BYTES, MEASUREMENT_ID, OPTIONAL_CATEGORIES, REQUIRED_LEGAL_ROLES, SERVICE_CATALOG, SERVICE_IDS, blockedPlaceholder, catalogService, consentConfiguration, consentCookie, consentRecordRequest, copyLanguage, decide, denyAll, embedPlaceholder, embedService, escapeHtml, grantedCategories, hostMatches, hostService, isConsentId, isLegalRoleName, isRecordToken, isServiceId, legalRevisions, needsBanner, parseConsentRecordRequest, parseConsentState, parseLegalLanguage, promptReason, readConsentCookie, rewriteEmbeds, serializeConsentState, serviceGranted };