@the-smithy/hearth 0.0.0-stage → 0.13.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/llms.md ADDED
@@ -0,0 +1,79 @@
1
+ # @the-smithy/hearth: reference for AI assistants
2
+
3
+ Hearth is a headless CMS. This package is a server-only client for its Delivery API. Never import it in
4
+ client components: the API key is secret.
5
+
6
+ ## Setup
7
+ - `pnpm add @the-smithy/hearth` (public on npm; no registry config or token needed). It's useless without a
8
+ Hearth site's API key. The full guide is in Hearth under API docs (Markdown download).
9
+ - env: `HEARTH_SITE`, `HEARTH_API_KEY`, optionally `HEARTH_WEBHOOK_SECRET`
10
+ - `lib/hearth.ts`:
11
+ `export const hearth = createHearthClient({ site: process.env.HEARTH_SITE!, apiKey: process.env.HEARTH_API_KEY! })`
12
+ `export const hearthNext = createHearthNext(hearth)` (from `@the-smithy/hearth/next`)
13
+
14
+ ## Client (`@the-smithy/hearth`)
15
+ - `site.get(): Site`
16
+ - `pages.list(): PageSummary[]`
17
+ - `pages.get(slug | string[] | undefined): Page | null` (empty means "home")
18
+ - `events.list({ when?: "upcoming" | "past", limit? }): HearthEvent[]`
19
+ - `events.get(slug): HearthEventDetail | null`
20
+ - `sitemap(): SitemapEntry[]`
21
+ - `forms.get(formKey): HearthForm | null`: the site's form (`{ key, name, fields, submitLabel, successMessage }`), to render outside a Form block, e.g. in a footer. Each site has one form, `contact`, and every Form block shows it. Fields are `{ name, label, type, required, options?, placeholder? }`; render `placeholder` in the input (for a select, as the prompt option). A Form block can instead name an Ember form (`data.emberFormId`, `frm_…`): render it with `EmberForm` from `@the-smithy/ember/react`, with the site's form as the fallback.
22
+ - `forms.submit(formKey, FormData | object, { ip? }): { ok: true, message } | { ok: false, message, fieldErrors }`
23
+ - `request<T>(path, opts)`: the raw call for anything else under `/api/v1/sites/{site}`
24
+ - Errors: `HearthError` with `.code` and `.status`. `isHearthError(e)`.
25
+ - `preview.verify(token): { path, expiresAt, version }` checks a Hearth preview token; `version` changes whenever the site's drafts or content do.
26
+ - Tickets: `events.register(slug, { ticket, name, email, phone?, teamName?, attendees?, notes?, returnUrl }, { ip? })`:
27
+ `{ ok: true, status: "confirmed" }`, `{ ok: true, status: "pending", checkoutUrl }` or `{ ok: false, message, fieldErrors }`.
28
+ Events with on-site registration have `registration: { open, closesAt, tickets[], fundraiser }`.
29
+ - Giving (sites with the feature): `fundraisers.list({ status?, category?, limit? }): Fundraiser[]`,
30
+ `fundraisers.get(slug): FundraiserDetail | null`, `donations.checkout({ amountCents, fundraiser?, name?, email?,
31
+ anonymous?, message?, returnUrl }, { ip? })`: `{ ok: true, checkoutUrl }` or `{ ok: false, message, fieldErrors }`.
32
+ - Every method takes a last `CallOptions` argument: `{ signal?, cache?, next?: { revalidate?, tags? }, preview? }`.
33
+ `preview` (a token) makes page and event reads return saved drafts, never cached.
34
+
35
+ ## Page shape
36
+ `Page = { slug, path, title, seo, publishedAt, blocks: ResolvedBlock[], meta: PageMeta }`
37
+ - `PageMeta`: `fullTitle`, `title`, `description`, `canonical`, `robots { index, follow }`,
38
+ `openGraph`, `twitter`, `image { url, width, height, alt }`, `jsonLd[]`.
39
+ - Each block is `{ id, type, data }`; fields live on `data`. Narrow by `type`; use `BlockOf<"hero">` for one type.
40
+ - Unknown future types arrive as `UnknownBlock`. Always handle a fallback.
41
+
42
+ ## Next.js (`@the-smithy/hearth/next`)
43
+ - `hearthNext.getPage(slug)`, `getPages()`, `getSite()`, `getEvents(opts)`, `getEvent(slug)`:
44
+ tagged for revalidation. Call `notFound()` on null.
45
+ - `generateMetadata`: `return hearthNext.generatePageMetadata((await params).slug)`
46
+ - Events: `metadataFromMeta(event.meta)`
47
+ - `app/sitemap.ts`: `export default hearthNext.sitemap`
48
+ - `app/api/revalidate/route.ts`: `export const POST = hearthNext.revalidateHandler()`
49
+ - Giving: `getFundraisers(opts)`, `getFundraiser(slug)`, and a donation form's server action:
50
+ `export const donate = async (prev: FormResult | null, fd: FormData) => hearthNext.donateAction()(prev, fd)`
51
+ (fields `amount` or `amount=custom` + `customAmount` in dollars, `fundraiser`, `name`, `email`, `anonymous`,
52
+ `message`; it redirects to the payment page, which returns to `/donate/thank-you`). Ask staff to reserve
53
+ `donate` and `fundraisers` on the site's Plan card.
54
+ - Tickets: `registerAction(slug)` for a registration form (`ticket`, `name`, `email`, `phone`, `teamName`, `notes`,
55
+ one `attendee` per name); free tickets resolve to `{ ok: true, message }`, paid ones redirect to checkout and return
56
+ to `/registered`.
57
+ - Draft preview: `app/api/preview/route.ts`: `export const GET = hearthNext.previewHandler()` and
58
+ `app/api/preview/exit/route.ts`: `export const GET = hearthNext.exitPreviewHandler()`. While previewing,
59
+ `getPage`, `getEvent` and `getFundraiser` return drafts. Every site renders the SDK's live preview in its root
60
+ layout: `import { HearthPreview } from "@the-smithy/hearth/preview"` then
61
+ `{(await hearthNext.isPreview()) ? <HearthPreview /> : null}`. Hearth's side-by-side preview frames the site, so
62
+ never send `X-Frame-Options` and allow `frame-ancestors https://hearth.thesmithy.io` in any CSP. It refreshes the page as editors save drafts
63
+ (Hearth autosaves them); don't build a preview banner of your own.
64
+ - Forms: in a "use server" file:
65
+ `export async function contact(_: FormResult | null, fd: FormData) { return hearthNext.submitForm("contact", fd) }`
66
+ Then use it with `useActionState(contact, null)`.
67
+
68
+ ## React (`@the-smithy/hearth/react`)
69
+ - `<HearthBlocks blocks={page.blocks} components={{ hero: Hero, ... }} fallback={...} />`
70
+ Each component receives `{ block }`. The map is typed by `BlockComponents`.
71
+ - `<HearthEmbed block={block} />`: renders an Embed block's `html` in a sandboxed iframe. Never inject `html` into the page.
72
+ - Custom blocks: `{ type: "custom", data: { block: "<key>", values } }`, the site's own block types; map keys to components, skip unknown ones. `customBlocks.list()` / `hearthNext.getCustomBlocks()` describe their fields.
73
+ - `<JsonLd data={page.meta.jsonLd} />`
74
+
75
+ ## Other entry points
76
+ - `@the-smithy/hearth/webhooks`: `verifyWebhook(raw, header, secret)`, `parseWebhook(request, secret)`,
77
+ `tagsForWebhook(payload)`
78
+ - `@the-smithy/hearth/testing`: `createMockHearth({ site, pages, events, sitemap, onForm })` returns
79
+ `{ fetch, requests }`. Pass `fetch` to `createHearthClient`.
package/package.json CHANGED
@@ -1,6 +1,77 @@
1
1
  {
2
2
  "name": "@the-smithy/hearth",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.13.0",
4
+ "description": "Typed client for the Hearth Delivery API: content, SEO, forms and publish webhooks.",
5
+ "type": "module",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/The-Smithy/hearth.git",
10
+ "directory": "packages/sdk"
11
+ },
12
+ "engines": {
13
+ "node": ">=20"
14
+ },
15
+ "sideEffects": false,
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./next": {
22
+ "types": "./dist/next.d.ts",
23
+ "import": "./dist/next.js"
24
+ },
25
+ "./react": {
26
+ "types": "./dist/react.d.ts",
27
+ "import": "./dist/react.js"
28
+ },
29
+ "./preview": {
30
+ "types": "./dist/preview.d.ts",
31
+ "import": "./dist/preview.js"
32
+ },
33
+ "./webhooks": {
34
+ "types": "./dist/webhooks.d.ts",
35
+ "import": "./dist/webhooks.js"
36
+ },
37
+ "./testing": {
38
+ "types": "./dist/testing.d.ts",
39
+ "import": "./dist/testing.js"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md",
45
+ "llms.md"
46
+ ],
47
+ "dependencies": {
48
+ "@the-smithy/hearth-schema": "^0.13.0"
49
+ },
50
+ "peerDependencies": {
51
+ "next": ">=15",
52
+ "react": ">=19"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "next": {
56
+ "optional": true
57
+ },
58
+ "react": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "devDependencies": {
63
+ "@types/node": "^22.20.5",
64
+ "@types/react": "^19.3.0",
65
+ "next": "16.3.8",
66
+ "react": "19.2.8",
67
+ "react-dom": "19.2.8",
68
+ "tsup": "^8.5.1",
69
+ "typescript": "^5.9.3",
70
+ "vitest": "^5.0.3"
71
+ },
72
+ "scripts": {
73
+ "build": "tsup",
74
+ "test": "vitest run",
75
+ "typecheck": "tsc --noEmit"
76
+ }
6
77
  }