@sandboxsonny/ace-kit 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.
@@ -0,0 +1,41 @@
1
+ # Dev sites
2
+
3
+ When your site's dev server works against a hosted CMS, it doesn't touch the live site. It works on a **dev site**: a private copy of the site that only you can see. You can change section types, try new fields and edit test content freely; the live site, and everyone editing it, never notice.
4
+
5
+ ## How it works
6
+
7
+ On start, the dev server (the Astro integration, or the `ace()` Vite plugin on React Router):
8
+
9
+ 1. checks you are signed in (`npx ace-kit login`), and finds the site `ACE_SITE_DOMAIN` names;
10
+ 2. asks the CMS for your dev site of it, which the CMS makes the first time as a copy of the live site: its content types, section types, menus and the **text** of its entries, with every link between entries pointing at the copy. Images are not copied yet, so image fields start empty;
11
+ 3. points the site at it: `ACE_SITE_DOMAIN` becomes the dev site's domain for as long as the dev server runs (your `.env` is not changed), so the site reads its content from the dev site, and section sync goes there;
12
+ 4. tells the CMS where the dev server is running, so the live preview in the admin app shows your dev server.
13
+
14
+ ```
15
+ [ace-kit] Made you a dev site, "Acme (dev)", as a copy of the live site (dev-3.acme.com). This dev server reads and syncs to it; the live site is not touched.
16
+ ```
17
+
18
+ In the admin app the dev site appears in your Sites list with a **Dev** badge. Its Settings page has **Reset dev site**, which throws away what is in it and copies the live site again, for when you want to go back after trying something.
19
+
20
+ ## Who can have one
21
+
22
+ Partner accounts and super admins, for sites they own. Ask for a partner account if the dev server says your account can't have a dev site.
23
+
24
+ ## Going to the live site
25
+
26
+ A dev site is never merged into the live site. When your change is ready, deploy your site, then run `npx ace-kit sync` against the live site (`ACE_SITE_DOMAIN` set to the live domain, as in your `.env`). Deploy first: the live CMS then has the section types the deployed code expects.
27
+
28
+ ## Settings
29
+
30
+ | Setting | Meaning |
31
+ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
32
+ | `ACE_DEV_SITE` | `false` stops the dev server using a dev site. `true` uses one even against a CMS on your own machine. Default: on for a hosted CMS only. |
33
+ | `ACE_SYNC_SECTIONS` | See [sections](./sections.md). |
34
+
35
+ ## If something stops it
36
+
37
+ The dev server says so once, and carries on showing the **live site**, read-only, with section sync off: you aren't signed in (run `npx ace-kit login --api-url <address>`), your account can't have a dev site, the site isn't one you can see, or the CMS can't be reached.
38
+
39
+ ## Housekeeping
40
+
41
+ A dev site nobody has used for 30 days (no one working in it, no website reading from it) is removed, with everything in it. It is also removed with the live site it was copied from. Dev sites don't count toward a partner's site limit.
@@ -22,7 +22,7 @@ npm install @sandboxsonny/ace-kit
22
22
  | `@sandboxsonny/ace-kit/react-router` | React Router 7, browser-safe: `AceRichText` (router links), `AceImage`, `AceNotice`, `createMeta`, `useAceMenu`, `useAcePreview` |
23
23
  | `@sandboxsonny/ace-kit/react-router/server` | React Router 7, server only: `createPageLoader`, `createRootLoader`, `handleRequest` (the server entry), `getEntry`, `getEntries`, `getMenuLinks`, `getAceClient` |
24
24
  | `@sandboxsonny/ace-kit/vite` | A Vite plugin: reads `ACE_` settings from `.env` in development and keeps one copy of React |
25
- | `ace-kit` (command) | `ace-kit generate`, which writes TypeScript types for your content |
25
+ | `ace-kit` (command) | `ace-kit generate` (TypeScript types for your content), `login`, `logout`, `whoami` (see [the command line](./command-line.md)) |
26
26
 
27
27
  ## The client
28
28
 
@@ -0,0 +1,104 @@
1
+ # Sections
2
+
3
+ A **section** is a part of a page that an editor adds, fills in and reorders in the CMS: a hero, an FAQ, a gallery. You define what each kind can hold in a small file next to the component that draws it. Ace Kit sends those definitions to the CMS while your dev server runs, and types the component's props from them.
4
+
5
+ ```
6
+ src/components/sections/
7
+ Faq.astro
8
+ Faq.schema.ts <- what an FAQ can hold
9
+ Hero.astro
10
+ Hero.schema.ts
11
+ ```
12
+
13
+ ## Describing a section
14
+
15
+ ```ts
16
+ // Faq.schema.ts
17
+ import { defineSection } from "@sandboxsonny/ace-kit";
18
+
19
+ export default defineSection({
20
+ handle: "faq",
21
+ name: "FAQ",
22
+ fields: [{ handle: "heading", label: "Heading", type: "text" }],
23
+ blocks: {
24
+ question: {
25
+ name: "Question",
26
+ fields: [
27
+ { handle: "question", label: "Question", type: "text", required: true },
28
+ { handle: "answer", label: "Answer", type: "richtext" },
29
+ ],
30
+ },
31
+ },
32
+ });
33
+ ```
34
+
35
+ - **`handle`** is the section's kind in your code (`section.type`) and in the CMS: lowercase letters, numbers and hyphens. **`name`** is what the editor sees.
36
+ - **`fields`** are the section's own values. Each has a `handle`, a `label` and a `type`: `text`, `textarea`, `number`, `checkbox`, `select` (also give `options: [...]`), `image`, `richtext`, `date` or `link`. Add `required: true` to make the editor fill it in.
37
+ - **`blocks`** are the repeatable items inside a section, by handle, each with a `name` and its own `fields`. An FAQ's questions are blocks: the editor adds as many as they like and reorders them. A section without `blocks` is just fields.
38
+ - The file's **default export** must be the result of `defineSection`. Everything else in the file is ignored.
39
+
40
+ A section can't hold other sections; use blocks for a list inside one.
41
+
42
+ ## Drawing sections
43
+
44
+ The component's props follow its schema, with nothing written twice:
45
+
46
+ ```astro
47
+ ---
48
+ // Faq.astro
49
+ import type { SectionOf } from "@sandboxsonny/ace-kit";
50
+ import type faq from "./Faq.schema";
51
+
52
+ type Props = { section: SectionOf<typeof faq> };
53
+ const { section } = Astro.props;
54
+ ---
55
+
56
+ <h2>{section.values.heading}</h2>
57
+ {section.blocks.map((block) => (
58
+ <details>
59
+ <summary>{block.values.question}</summary>
60
+ </details>
61
+ ))}
62
+ ```
63
+
64
+ `section.values.heading` is a `string | undefined` (it isn't required); `block.values.question` is a `string` (it is). An `image` is `AceImageData`, a `link` is `AceLinkData`, a `select` is the union of its options, and a block's `values` depend on its `type` when a section has more than one kind of block.
65
+
66
+ Then draw a whole sections field with the component for each kind:
67
+
68
+ ```astro
69
+ ---
70
+ import AceSections from "@sandboxsonny/ace-kit/astro/AceSections.astro";
71
+ import Faq from "~/components/sections/Faq.astro";
72
+ import Hero from "~/components/sections/Hero.astro";
73
+ ---
74
+
75
+ <AceSections sections={entry.values.content} components={{ hero: Hero, faq: Faq }} />
76
+ ```
77
+
78
+ On React and React Router the same component comes from `@sandboxsonny/ace-kit/react` and `@sandboxsonny/ace-kit/react-router`. A section whose kind has no component is left out (and noted in the dev server's log), or drawn by a `fallback` component if you give one (React). Sections are drawn in the order the editor put them.
79
+
80
+ With `ace-kit generate`, a sections field is typed `AceSection[]`: a list of sections of any kind, which is what `AceSections` takes.
81
+
82
+ ## Syncing to the CMS
83
+
84
+ While the dev server runs, Ace Kit watches for `*.schema.ts` files (in `src`) and syncs them to the CMS whenever one is added, changed or removed, and once at start. It prints one line each time:
85
+
86
+ ```
87
+ 10:41:07 [ace-kit] Synced 3 section types (faq, gallery, hero) to localhost (http://localhost:8080).
88
+ ```
89
+
90
+ You don't run anything. You do need to be signed in once: `npx ace-kit login` (see [the command line](./command-line.md)). The sync goes to the site named by `ACE_SITE_DOMAIN` at `ACE_API_URL`, the same settings the site reads its content with, and only an owner of that site (or a super admin) can sync to it.
91
+
92
+ **Your dev site, not the live one.** Against a _hosted_ CMS the dev server works on a [dev site](./dev-sites.md): your own private copy of the site, made for you the first time, so a half-written field never reaches editors or visitors of the live site. The sync goes there. Against a CMS on your own machine there is no need for one, and the sync goes to the site itself.
93
+
94
+ **If there is no dev site to sync to** (you aren't signed in, or your account can't have one), automatic sync stays off against a hosted CMS, and the dev server says why. To allow it against the live site anyway, set `ACE_SYNC_SECTIONS=remote`; to turn it off, `ACE_SYNC_SECTIONS=false`. Running `npx ace-kit sync` yourself always syncs to the site `ACE_SITE_DOMAIN` names, which is how a change reaches the live site: deploy first, then run it.
95
+
96
+ **Nothing is deleted, and a partial picture is never sent.** The CMS treats what is sent as the whole list. A section, block or field you take out of your code is kept there, hidden, so the content already written in it isn't lost (put it back and it returns). A field changing kind (say `text` to `number`) is refused, because its saved values are of the old kind: use a new handle. If any schema file can't be loaded, or none are found, **nothing is synced**; the error says which file and why.
97
+
98
+ ### On a build server
99
+
100
+ `npx ace-kit sync` does the same once, in a command. With `ACE_TOKEN` set (see [the command line](./command-line.md)) it needs no browser. Schema files are TypeScript, which Node runs as it is from **Node 22.18** (use `.js` files on older versions).
101
+
102
+ ## What the CMS needs
103
+
104
+ An API with sections (its migration 00019), command-line sign-in (00020) and, for dev sites, 00022.
@@ -45,6 +45,7 @@ cms.getEntry("pagez", "about"); // type error: no such content type
45
45
  | Rich text | `AceRichTextDocument` |
46
46
  | Date | `string`, a day written `YYYY-MM-DD` (see `formatAceDate`) |
47
47
  | Link | `AceLinkData`: `{ href, label? }` |
48
+ | Sections | `AceSection[]` (see [Sections](./sections.md)) |
48
49
  | a kind this version doesn't know | `unknown` |
49
50
 
50
51
  A field is optional unless it is marked required in the CMS. Before you generate, handles are plain `string` and values are `Record<string, unknown>`, so nothing breaks.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sandboxsonny/ace-kit",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Everything a website needs to use CMS Ace: a typed client, addressing, images, menus, rich text, live preview, and Astro and React adapters.",
5
5
  "keywords": [
6
6
  "cms",