caracms 0.7.0 → 0.8.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.
Files changed (75) hide show
  1. package/README.md +108 -19
  2. package/bin/caracms.mjs +11 -3
  3. package/bin/init.mjs +332 -34
  4. package/bin/media.mjs +60 -6
  5. package/bin/templates/railway-cloud.ts.txt +73 -0
  6. package/package.json +1 -1
  7. package/skills/caracms-adopt/SKILL.md +30 -13
  8. package/skills/caracms-hosting/SKILL.md +240 -0
  9. package/skills/caracms-tina/SKILL.md +45 -15
  10. package/src/body.ts +55 -0
  11. package/src/cli/validate.ts +1 -1
  12. package/src/cloud-middleware.ts +51 -5
  13. package/src/config.ts +20 -1
  14. package/src/content.ts +15 -6
  15. package/src/document-problems.ts +48 -0
  16. package/src/editor/client/collections/CollectionGrid.tsx +2 -2
  17. package/src/editor/client/collections/CollectionTable.tsx +3 -3
  18. package/src/editor/client/documents/DocMenu.tsx +56 -1
  19. package/src/editor/client/documents/actions.ts +30 -1
  20. package/src/editor/client/documents/dialogs.tsx +61 -3
  21. package/src/editor/client/editor.ts +98 -2
  22. package/src/editor/client/fields/LockupField.tsx +4 -4
  23. package/src/editor/client/inbox/InboxStage.tsx +25 -1
  24. package/src/editor/client/live/notices.tsx +19 -0
  25. package/src/editor/client/live/publish.tsx +58 -2
  26. package/src/editor/client/nav/DocRow.tsx +4 -4
  27. package/src/editor/client/pane/levels/CollectionLevel.tsx +2 -1
  28. package/src/editor/client/pane/levels/DocumentLevel.tsx +2 -1
  29. package/src/editor/client/pane/levels/DocumentsLevel.tsx +2 -2
  30. package/src/editor/client/pane/levels/FormLevel.tsx +33 -6
  31. package/src/editor/client/pane/levels/SubmissionsLevel.tsx +19 -2
  32. package/src/editor/client/pane/parts.tsx +67 -4
  33. package/src/editor/client/pane/switcher.tsx +4 -4
  34. package/src/editor/client/shell/Bar.tsx +11 -0
  35. package/src/editor/client/state/keeper.ts +28 -4
  36. package/src/editor/client/state/live.ts +18 -1
  37. package/src/editor/routes/_live.ts +1 -1
  38. package/src/editor/routes/admin.astro +9 -0
  39. package/src/editor/routes/discard.ts +11 -0
  40. package/src/editor/server/admin-props.ts +18 -2
  41. package/src/editor/server/arrange.ts +4 -4
  42. package/src/editor/server/create.ts +4 -4
  43. package/src/editor/server/drafts-store.ts +2 -2
  44. package/src/editor/server/empty-site.ts +23 -0
  45. package/src/editor/server/media/cloud-mux.ts +4 -1
  46. package/src/editor/server/media/config.ts +12 -5
  47. package/src/editor/server/media/upload.ts +2 -41
  48. package/src/editor/server/mode.ts +3 -1
  49. package/src/editor/server/move.ts +32 -4
  50. package/src/editor/server/write.ts +4 -4
  51. package/src/editor/styles/levels/live.css +2 -0
  52. package/src/editor/types.ts +92 -4
  53. package/src/email.ts +4 -1
  54. package/src/forms/cloud-store.ts +4 -1
  55. package/src/forms/inbox.ts +10 -5
  56. package/src/forms/store.ts +21 -4
  57. package/src/index.ts +21 -6
  58. package/src/live/auth/login.ts +4 -3
  59. package/src/live/auth/views.ts +8 -1
  60. package/src/live/autosaves.ts +94 -6
  61. package/src/live/cloud.ts +64 -20
  62. package/src/live/drafts-store.ts +17 -6
  63. package/src/live/editor.ts +232 -16
  64. package/src/live/git.ts +492 -22
  65. package/src/live/github.ts +84 -17
  66. package/src/live/locks.ts +22 -0
  67. package/src/live/messages.ts +49 -2
  68. package/src/live/mode.ts +19 -8
  69. package/src/live/notices.ts +5 -1
  70. package/src/live/runtime.ts +2 -1
  71. package/src/live/services.ts +114 -0
  72. package/src/live/status.ts +5 -0
  73. package/src/live/sweep.ts +44 -1
  74. package/src/live/webhook.ts +8 -3
  75. package/src/media.ts +9 -3
package/README.md CHANGED
@@ -10,8 +10,17 @@ is, under the MIT license.
10
10
 
11
11
  ## Start
12
12
 
13
- caracms goes into an existing Astro site (Astro 7, on Node 22.12 or later). From the
14
- site's root, in a clean git checkout, install it and commit the install:
13
+ Two steps: create an Astro site, then add caracms to it with `npx caracms init`.
14
+ (`npm create caracms` says the same.)
15
+
16
+ **1. Create the Astro site** (Astro 7, on Node 22.12 or later), or use one you have:
17
+
18
+ ```sh
19
+ npm create astro@latest
20
+ ```
21
+
22
+ **2. In it, add caracms.** From the site's root, in a clean git checkout, install it and
23
+ commit the install:
15
24
 
16
25
  ```sh
17
26
  npm install caracms --save-exact
@@ -27,8 +36,10 @@ one diff to review. Then look at what `init` would do:
27
36
  npx caracms init
28
37
  ```
29
38
 
30
- It prints every file it would add and every edit it would make, as a diff, and changes
31
- nothing. When it looks right, apply it and install what it added:
39
+ It asks where the site will run, [CaraCMS Cloud](#caracms-cloud) or self-hosted, then
40
+ prints every file it would add and every edit it would make, as a diff, and changes
41
+ nothing. (It asks only in a terminal: an agent or a script passes `--cloud` or
42
+ `--self-hosted`.) When it looks right, apply it and install what it added:
32
43
 
33
44
  ```sh
34
45
  npx caracms init --write
@@ -45,9 +56,12 @@ job of the `caracms-adopt` skill, one page at a time.
45
56
 
46
57
  Run from the site's root:
47
58
 
48
- - `caracms init [--write] [--railway]`: add caracms to an Astro site, or bring a set-up
49
- site's skills, schemas and CLAUDE.md block up to date. `--railway` also writes a
50
- Railway setup; without it, `init` prints the variables any host needs (Hosting, below).
59
+ - `caracms init [--write] [--railway] [--cloud | --self-hosted]`: add caracms to an Astro
60
+ site, or bring a set-up site's skills, schemas and CLAUDE.md block up to date.
61
+ `--cloud` or `--self-hosted` says where the site runs, written into `caracms.config.ts`
62
+ as `services` ([Where the site runs](#where-the-site-runs-services)); `--cloud` on a
63
+ self-hosted site also prints the steps of the move. `--railway` also writes a Railway
64
+ setup; without it, `init` prints the variables any host needs (Hosting, below).
51
65
  - `caracms validate [--fix] [--json]`: check the content against `caracms.config.ts`.
52
66
  Exits 1 when there are errors. `--fix` runs `caracms schemas` first, then lays each
53
67
  content file out as the editor saves it (two-space JSON; only whitespace changes).
@@ -59,8 +73,8 @@ Run from the site's root:
59
73
  keeping every key ([Moving a site's images in](#moving-a-sites-images-in)).
60
74
  - `caracms start`: serve the built site (Hosting, below).
61
75
 
62
- The skills (`caracms-content`, `caracms-blocks` and `caracms-adopt`, in `skills/`) say how
63
- the rest works, matching the installed version.
76
+ The skills (`caracms-content`, `caracms-blocks`, `caracms-adopt`, `caracms-tina` and
77
+ `caracms-hosting`, in `skills/`) say how the rest works, matching the installed version.
64
78
 
65
79
  ## Collections and addresses
66
80
 
@@ -204,7 +218,8 @@ It prints where the IP comes from when it starts.
204
218
  - Commit: `RAILWAY_GIT_COMMIT_SHA`, which Railway sets.
205
219
  - `caracms init --railway` writes `.railway/railway.ts`, which describes the whole
206
220
  project (the site, Redis, the buckets, imgproxy). Railway's own variables cover the
207
- repo, so `GITHUB_REPO` isn't needed.
221
+ repo, so `GITHUB_REPO` isn't needed. With `--cloud` it writes only the site and Redis:
222
+ the cloud brings the rest.
208
223
  - Try again: `CARACMS_RAILWAY_TOKEN`, a project token. Railway has no deploy hook.
209
224
 
210
225
  ### Render
@@ -315,8 +330,78 @@ site, signs every commit, and isn't tied to a person. Use it for anyone running
315
330
  a handful of sites, or a repo that requires signed commits. Install it on the site's repo
316
331
  only, with the same permissions as the token.
317
332
 
333
+ **Protect `drafts` from deletion.** It holds every change saved since the last Publish,
334
+ and to a branch tidy-up it looks like any stale branch. In the repo's Settings → Rules →
335
+ Rulesets, add a branch ruleset that targets `drafts` and turns on "Restrict deletions"
336
+ only (leave "Block force pushes" off: Publish moves `drafts` with a forced update). If
337
+ `drafts` goes anyway, the next start makes it again from the last commit the server
338
+ recorded, while GitHub still has that commit; when it can't, the editor says what was lost.
339
+
340
+ **Roll the site back with `git revert`, never a force-push.** A revert is a new commit on
341
+ `main`, which `drafts` takes in like any push; resetting `main` to an older commit and
342
+ force-pushing leaves the rolled-back content waiting in the editor, so the next Publish puts
343
+ it back (the editor warns everyone when it sees one).
344
+
318
345
  ### CaraCMS Cloud
319
346
 
347
+ #### Where the site runs: `services`
348
+
349
+ `caracms.config.ts` says where the site's services (GitHub access, images, forms and
350
+ email, video) come from, and nothing else decides it:
351
+
352
+ ```ts
353
+ export default defineSite({
354
+ services: 'cloud', // or 'self-hosted'
355
+ collections: { … },
356
+ …
357
+ });
358
+ ```
359
+
360
+ - **`'cloud'`**: everything comes through `CARACMS_CLOUD_KEY`. The self-hosted variables
361
+ (the GitHub token or App and `GITHUB_REPO`, the image, video, and form and email ones
362
+ below) are **ignored**, never read, and each is named once in the build log and once
363
+ when the server starts:
364
+
365
+ > S3_BUCKET is set, but this site runs on CaraCMS Cloud (caracms.config.ts): it's ignored. Remove it once the old bucket and imgproxy are retired.
366
+
367
+ So a site can move with its old variables still set, and remove them afterwards. A
368
+ production build or server with no key fails with "This site runs on CaraCMS Cloud
369
+ (caracms.config.ts), but CARACMS_CLOUD_KEY isn't set. Make a key for it in
370
+ app.caracms.com." (the build fails; a running server's `/admin` answers 503 with it, and
371
+ the public site keeps serving). It never falls back to the self-hosted variables.
372
+ `astro dev` without a key is local mode, as always.
373
+ - **`'self-hosted'`**: the site's own variables, as [the variables](#the-variables) list
374
+ them. A `CARACMS_CLOUD_KEY` beside them is ignored the same way.
375
+ - **Not set** (a site from before caracms 0.8.0): the key decides, and setting it beside
376
+ the site's own variables is refused, as each section below says. The next `caracms init`
377
+ adds the field.
378
+
379
+ `caracms init --cloud` or `--self-hosted` writes the field (as the first line inside
380
+ `defineSite({`). With neither, `init` asks in a terminal and, run by an agent or a script,
381
+ leaves a set-up site's mode unset (and says so) or stops on a new one. Once the field is
382
+ there, `init` reads it and never asks again.
383
+
384
+ **Moving a self-hosted site onto the cloud.** `caracms init --cloud` on a self-hosted site
385
+ writes the field and prints the steps of the move, in order, each with the check that it
386
+ worked: make the site, its key and the App's install in app.caracms.com; push the field
387
+ (the host's build fails until the key is set: the old deployment keeps serving); copy the
388
+ images in with [`caracms media push`](#moving-a-sites-images-in); set the key on the host;
389
+ upload the video clips again; remove every variable the cloud replaces (it names them all,
390
+ marking the ones it found in `.env` and `.railway/railway.ts`; it can't see the host's own
391
+ page, so check that too); retire what the cloud replaced; and, last, cut
392
+ `.railway/railway.ts` down to the site and Redis. `init` never edits an existing
393
+ `.railway/railway.ts`: `railway config apply` would remove the buckets with it. With a key
394
+ in the environment or `.env`, `init --cloud` also checks it, showing the site, its team,
395
+ the linked repo and whether the App is installed on it, or the cloud's reason for refusing
396
+ it (it never prints the key). The `caracms-hosting` skill walks through all of this.
397
+
398
+ **Leaving.** `caracms init --self-hosted` on a cloud site writes the field back and prints
399
+ the way back: set up the site's own services ([Hosting](#hosting)), copy the site's folder
400
+ from the cloud's media bucket to a bucket of your own (the keys in content stay the same),
401
+ set the variables, then remove the key.
402
+
403
+ #### GitHub access on CaraCMS Cloud
404
+
320
405
  A site run by CaraCMS can take its GitHub access from CaraCMS Cloud instead of a token or
321
406
  an App of its own. The cloud gives the site's server a short-lived token for the site's
322
407
  repo, and only that repo, about once an hour.
@@ -329,8 +414,9 @@ repo, and only that repo, about once an hour.
329
414
  site's server.
330
415
 
331
416
  The key replaces `CARACMS_GITHUB_TOKEN` (or `GITHUB_APP_ID` and `GITHUB_APP_PRIVATE_KEY`)
332
- and `GITHUB_REPO`: the repo comes from the cloud when the server starts, so setting
333
- `GITHUB_REPO` beside the key is refused, and so is setting a token or an App as well. It
417
+ and `GITHUB_REPO`: the repo comes from the cloud when the server starts. With `services: 'cloud'`
418
+ they're ignored; on a site whose config doesn't say, setting `GITHUB_REPO` beside the key is
419
+ refused, and so is setting a token or an App as well. It
334
420
  also replaces the image variables ([Images](#images-on-caracms-cloud), below), the
335
421
  video ones ([Video](#video-on-caracms-cloud)), and the form and email ones
336
422
  ([Forms and email](#forms-and-email-on-caracms-cloud)).
@@ -350,7 +436,8 @@ start.
350
436
  With the key, the site's images live in its own folder of CaraCMS Cloud's media bucket and
351
437
  are served, resized and signed, from `img.caracms.cloud`. Set none of `S3_ENDPOINT`,
352
438
  `S3_REGION`, `S3_BUCKET`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `IMGPROXY_KEY`,
353
- `IMGPROXY_SALT` or `CARACMS_MEDIA_URL`: setting any of them beside the key is refused with
439
+ `IMGPROXY_SALT` or `CARACMS_MEDIA_URL`. With `services: 'cloud'` they're ignored; on a site
440
+ whose config doesn't say, setting any of them beside the key is refused with
354
441
 
355
442
  > Set CARACMS_CLOUD_KEY or the site's own media variables, not both: remove S3_BUCKET.
356
443
 
@@ -374,8 +461,9 @@ site's `.env`), with the same rules and output, and its uploads count like any o
374
461
 
375
462
  With the key, the site needs no submissions bucket and no Resend account. Set none of
376
463
  `SUBMISSIONS_S3_BUCKET`, `SUBMISSIONS_S3_ACCESS_KEY_ID`, `SUBMISSIONS_S3_SECRET_ACCESS_KEY`,
377
- `RESEND_API_KEY` or `FORMS_FROM`: setting any of them beside the key is refused, at build
378
- and when the server starts, with
464
+ `RESEND_API_KEY` or `FORMS_FROM`. With `services: 'cloud'` they're ignored; on a site whose
465
+ config doesn't say, setting any of them beside the key is refused, at build and when the
466
+ server starts, with
379
467
 
380
468
  > Set CARACMS_CLOUD_KEY or the site's own forms and email variables, not both: remove RESEND_API_KEY and FORMS_FROM.
381
469
 
@@ -406,8 +494,9 @@ them.
406
494
 
407
495
  `caracms media push` copies everything under `uploads/` in the site's own bucket into its
408
496
  CaraCMS Cloud folder, under the same keys, so the content doesn't change. It's the one
409
- command that takes both credentials, so give the site's own `S3_*` on its command line
410
- rather than in `.env` (the running site refuses both):
497
+ command that takes both credentials. The old bucket's `S3_*` can stay in `.env` once the
498
+ config says `services: 'cloud'` (the running site ignores them); on a site whose config
499
+ doesn't say, give them on its command line instead (it refuses both):
411
500
 
412
501
  ```bash
413
502
  S3_ENDPOINT=https://t3.storageapi.dev S3_REGION=auto S3_BUCKET=… S3_ACCESS_KEY_ID=… S3_SECRET_ACCESS_KEY=… npx caracms media push
@@ -423,8 +512,8 @@ CaraCMS to raise it, then run it again.
423
512
 
424
513
  With the key, the site's clips are uploaded to CaraCMS Cloud's Mux and listed from the
425
514
  site's own clips there; the editor works exactly as it does with a Mux account of your
426
- own. Set neither `MUX_TOKEN_ID` nor `MUX_TOKEN_SECRET`: setting either beside the key is
427
- refused with
515
+ own. Set neither `MUX_TOKEN_ID` nor `MUX_TOKEN_SECRET`. With `services: 'cloud'` they're
516
+ ignored; on a site whose config doesn't say, setting either beside the key is refused with
428
517
 
429
518
  > Set CARACMS_CLOUD_KEY or the site's own Mux variables, not both: remove MUX_TOKEN_ID.
430
519
 
package/bin/caracms.mjs CHANGED
@@ -31,7 +31,9 @@ import { createGate, describeClientIpSource, markProcessGated, parsePrelaunch, p
31
31
 
32
32
  const USAGE = `Usage: caracms <command>, from the site's root
33
33
 
34
- init [--write] [--railway] Add caracms to an existing Astro site. Shows the change; --write applies it.
34
+ init [--write] [--railway] [--cloud | --self-hosted]
35
+ Add caracms to an existing Astro site, and say where it runs (CaraCMS
36
+ Cloud or self-hosted). Shows the change; --write applies it.
35
37
  schemas Write schemas/ from caracms.config.ts, and add $schema to content files.
36
38
  validate [--fix] [--json] Check content against caracms.config.ts. --fix runs \`schemas\` first,
37
39
  then lays content files out as the editor saves them;
@@ -180,10 +182,16 @@ async function main(argv) {
180
182
  if (rest.length) return usage(`start takes no arguments.`);
181
183
  return start();
182
184
  case 'init': {
183
- const f = flags('init', rest, ['--write', '--railway']);
185
+ const f = flags('init', rest, ['--write', '--railway', '--cloud', '--self-hosted']);
184
186
  if (f.error) return usage(f.error);
185
187
  const { run } = await import('./init.mjs');
186
- process.exitCode = await run({ root: process.cwd(), write: f.set.has('--write'), railway: f.set.has('--railway') });
188
+ process.exitCode = await run({
189
+ root: process.cwd(),
190
+ write: f.set.has('--write'),
191
+ railway: f.set.has('--railway'),
192
+ cloud: f.set.has('--cloud'),
193
+ selfHosted: f.set.has('--self-hosted'),
194
+ });
187
195
  return;
188
196
  }
189
197
  case 'schemas':