riverbankcms 0.9.0 → 0.11.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.
@@ -11,6 +11,7 @@ All commands support these options:
11
11
  | `--json` | Output a single JSON envelope for machine parsing |
12
12
  | `--quiet` | Minimal output (suppress non-essential messages) |
13
13
  | `--env <target>` | Target project environment: `local` (default), `remote`, or `both` |
14
+ | `--timeout <seconds>` | How long to wait for each dashboard request: a whole number from 1 to 600 (default 30, or ten minutes for media transfers). Overrides `RIVERBANK_REQUEST_TIMEOUT` |
14
15
  | `--remote` | **[Deprecated]** Use `--env=remote` instead |
15
16
 
16
17
  **The `--json` envelope (single-document contract):** under `--json`, *every* invocation emits **exactly one** JSON document on stdout:
@@ -63,11 +64,60 @@ RIVERBANK_REMOTE_MGMT_API_KEY=bld_mgmt_sk_...
63
64
  RIVERBANK_REMOTE_API_KEY=bld_live_sk_... # Required for deploy command
64
65
  ```
65
66
 
67
+ ### Any environment
68
+
69
+ ```bash
70
+ # Optional: how long to wait for each dashboard request, in seconds (1-600).
71
+ # Unset: 30 for dashboard requests, ten minutes for media transfers.
72
+ RIVERBANK_REQUEST_TIMEOUT=60
73
+ ```
74
+
75
+ `--timeout` wins over `RIVERBANK_REQUEST_TIMEOUT`. When you set either one, it
76
+ applies to every request, media uploads and downloads included. When neither
77
+ is set, dashboard requests wait 30 seconds and media transfers get ten minutes.
78
+ A value that isn't a whole number in range stops the command before it sends
79
+ anything, so a typo never quietly falls back to the default.
80
+
66
81
  **Notes:**
67
82
 
68
83
  - Management API keys start with `bld_mgmt_sk_` (for write operations)
69
84
  - Content API keys start with `bld_live_sk_` (for read-only operations)
70
85
 
86
+ ## Network failures and recovery
87
+
88
+ When a request never gets an answer from the dashboard, the error code is
89
+ `sdk:network-error`, or `sdk:timeout` if the CLI's own time limit ran out. Both
90
+ are kept separate from errors the dashboard sent back, such as a 409 conflict.
91
+ The error's details carry a `transport` object saying what kind of failure it
92
+ was, in the `--json` envelope and in the human output alike:
93
+
94
+ | `transport.kind` | What it means | What to check |
95
+ | ---------------- | ------------- | ------------- |
96
+ | `dns` | The dashboard hostname couldn't be resolved | The dashboard URL in your environment |
97
+ | `connect` | A connection to the dashboard couldn't be opened | That the dashboard is running and reachable |
98
+ | `tls` | The TLS handshake failed | The certificate, or any proxy in between |
99
+ | `reset` | The connection closed before a response arrived | See below: a write may have landed |
100
+ | `timeout` | No response arrived in time | See below; `--timeout` allows longer |
101
+ | `unknown` | Something else went wrong in transport | See below |
102
+
103
+ `transport.code` is included when the platform reported a recognised code (for
104
+ example `ENOTFOUND` or `ECONNRESET`). Nothing else from the underlying error is
105
+ passed on: the message is fixed for each kind, so it can't echo hostnames,
106
+ headers or keys.
107
+
108
+ **A failed write may still have been saved.** With `dns`, `connect` and `tls`
109
+ failures the request usually never left your machine. With `reset`, `timeout`
110
+ and `unknown` it may well have reached the dashboard and been applied before the
111
+ connection dropped. So after one of those on a request that can change
112
+ something, the CLI tells you to read the record back before running the command
113
+ again, rather than to retry. Whether a request can change something is set by
114
+ the client for each operation, not guessed from the HTTP method, so read-only
115
+ lookups such as a media delete preview get the plainer hint. The `--json`
116
+ envelope shows it as `request.intent` (`read` or `write`). Some commands, such
117
+ as `event offline`, `event delete` and `page design push`, do that readback for
118
+ you (see [Event Commands](#event-commands) and
119
+ [page design pull / page design push](#page-design-pull--page-design-push)).
120
+
71
121
  ---
72
122
 
73
123
  ## Setup Commands
@@ -622,6 +672,19 @@ riverbankcms page upsert about \
622
672
  | `--seo-title <title>` | SEO title tag |
623
673
  | `--seo-description <desc>` | SEO meta description |
624
674
 
675
+ A new page is created together with its route in one step, so a failed create
676
+ doesn't leave a half-made page behind. If a new page's path is already taken,
677
+ upsert is refused with a `409` that says why, and nothing is written:
678
+
679
+ - `Path "<path>" is already used by page "<identifier>"` when another page
680
+ sits there. Use that page's identifier, or pick another path.
681
+ - `Another route on this site already uses this path` when the path belongs to
682
+ something else, such as an entry or an event page.
683
+
684
+ On a managed site, a new page has no stored design of its own and follows its
685
+ site style's defaults until a design is saved for it, for example with
686
+ `page design push`.
687
+
625
688
  ### page publish
626
689
 
627
690
  Publish a page.
@@ -660,6 +723,74 @@ riverbankcms page list --limit 10 --page 1
660
723
  riverbankcms page list --json
661
724
  ```
662
725
 
726
+ ### page design pull / page design push
727
+
728
+ Move a page's draft design (its pinned block Looks and section breaks) onto
729
+ another page built from the same blocks.
730
+
731
+ ```bash
732
+ # Write the stored design to a file (stdout when --out is left off)
733
+ riverbankcms page design pull <identifier> [--out <file>]
734
+ riverbankcms page design pull radiant-women --out radiant-design.json
735
+
736
+ # Write that design onto another page's draft
737
+ riverbankcms page design push <identifier> <file>
738
+ riverbankcms page design push radiant-women-celebration radiant-design.json
739
+ ```
740
+
741
+ The file is JSON keyed by **block identifiers** (`hero-0`, `body-text-1`), not
742
+ by block ids, so it can land on a page whose blocks were created separately.
743
+ Every block the design mentions has to exist on the destination under the same
744
+ identifier and kind, and every section break it sets has to sit between two
745
+ blocks that are next to each other there.
746
+
747
+ A few rules worth knowing before you rely on it:
748
+
749
+ - **Same site style only.** A design only transfers between pages on the same
750
+ site style, and push refuses anything else before it looks at the blocks:
751
+ `This design comes from site style "<id>" and page <id> uses "<id>". Designs only transfer between pages on the same site style.`
752
+ - **Managed sites only.** SDK sites style their pages in code and don't use
753
+ stored page designs, so push refuses them:
754
+ `Page <id> is on an SDK site, which does not use stored page designs.`
755
+ - **Stored design only.** Pull exports what was saved for the page: its draft
756
+ design, or its published design when there's no draft (`--json` says which
757
+ as `source`). A page that has never had a design saved uses its site style's
758
+ defaults, and pull says so and writes nothing rather than turning those
759
+ defaults into pinned choices.
760
+ - **Pins on deleted blocks are left out, and named.** A stored Look or break
761
+ that points at a block no longer on the page has no effect, so pull leaves it
762
+ out of the file and prints one line per pin it left out (for example
763
+ `Left out: a Look pinned on block <block-id>, which is no longer on page <id>.`).
764
+ They're listed under `omitted` in `--json`, and the pull still succeeds. A
765
+ break between two blocks that are both still on the page is kept even if
766
+ they're no longer next to each other; push then decides whether it fits the
767
+ destination.
768
+ - **Draft only.** Push writes the page's draft design. It never publishes, and
769
+ it works on a page that's already published without changing that; publish
770
+ the page when you want the design live.
771
+ - **Nothing is written on a refusal.** If the page flow, or any block, Look or
772
+ break, doesn't fit the destination, push lists each problem (for example
773
+ `The design refers to block "hero-0", which page <id> does not have.`) and
774
+ saves nothing.
775
+
776
+ **What the revision check protects.** The design file carries no revision.
777
+ Push reads the destination's current design revision immediately before it
778
+ writes, so you never handle revision tokens. That only protects the short
779
+ interval between that read and the write: if someone changes the destination's
780
+ design in between, push fails with a conflict and writes nothing. It does
781
+ **not** notice that the destination changed between an earlier `pull` and this
782
+ `push`, so push overwrites whatever draft design the destination has at the
783
+ time.
784
+
785
+ **If the connection drops** during push, the CLI reads the destination's design
786
+ back. It reports `Page <id>: draft design saved, confirmed by readback.` only
787
+ when the page's *draft* design is exactly the design you sent. A matching
788
+ published design doesn't count, because push only ever writes the draft. The
789
+ dashboard may also adjust a design as it saves it (a site style that locks a
790
+ block's Look adds that pin), and an adjusted draft no longer matches, so that
791
+ case is reported as unknown too. Anything unconfirmed exits non-zero (see
792
+ [Network failures and recovery](#network-failures-and-recovery)).
793
+
663
794
  ---
664
795
 
665
796
  ## Block Commands
@@ -787,6 +918,115 @@ riverbankcms navigation list --json
787
918
 
788
919
  ---
789
920
 
921
+ ## Event Commands
922
+
923
+ Manage events and their dates.
924
+
925
+ ### event
926
+
927
+ Manage event series directly through the CLI.
928
+
929
+ ```bash
930
+ riverbankcms event list
931
+ riverbankcms event get summer-fest
932
+ riverbankcms event upsert summer-fest --file ./content/events/summer-fest.json
933
+ riverbankcms event cancel summer-fest
934
+ riverbankcms event offline summer-fest
935
+ riverbankcms event delete summer-fest --yes
936
+ riverbankcms event occurrence list summer-fest summer-run
937
+ riverbankcms event occurrence add summer-fest summer-run \
938
+ --starts-at 2026-08-01T10:00 \
939
+ --duration-minutes 60
940
+ riverbankcms event occurrence update summer-fest summer-run <occurrence-uuid> \
941
+ --capacity 20
942
+ riverbankcms event occurrence cancel summer-fest summer-run <occurrence-uuid>
943
+ riverbankcms event occurrence delete summer-fest summer-run <occurrence-uuid> --yes
944
+ ```
945
+
946
+ `event upsert` and `push events` accept canonical event files with `path` and `blocks`. Events use native page authority: an `eventOffering` or `eventSeries` subject owns a page surface, and that surface owns its route, field content, publication state, and slots. `path` updates the page-surface route. `blocks` maps slot keys to surface-slot blocks, for example `blocks.main` for the built-in event template. Use stable block identifiers so repeat pushes update existing slot blocks safely. Unknown slot keys or block kinds fail validation; default slots accept enabled SDK `custom.*` blocks, while explicit template `allowedBlocks` lists remain authoritative. Ordinary editorial entries continue to use the separate `entry` commands and content-entry model described above.
947
+
948
+ For an online Event page, updating `content` publishes its field content immediately, matching the dashboard Content tab. The same save updates the image shown in run listings. Route, block and layout changes retain their separate draft and publish flow.
949
+
950
+ #### Taking an event offline and deleting it
951
+
952
+ `event delete` is refused while the event's page is still published, and the
953
+ page cannot go offline while any run is live. `event offline` handles both in
954
+ one call: it takes every live run and the Event page offline together, in one
955
+ transaction on the dashboard. That makes deleting a published event a sequence
956
+ of two explicit commands:
957
+
958
+ ```bash
959
+ riverbankcms event offline summer-fest
960
+ riverbankcms event delete summer-fest --yes
961
+ ```
962
+
963
+ Running `event offline` again is safe — it reports `Event summer-fest was
964
+ already offline.` and writes nothing. There is no `--offline-first` flag on
965
+ `event delete`; the two steps stay separate so the deletion guards for
966
+ registrations and payments are never bundled into a lifecycle change. That also
967
+ means registrations don't stop `event offline` (existing bookings are left
968
+ alone), but they do stop `event delete`: an event with registrations or
969
+ payments can go offline and still be refused deletion.
970
+
971
+ Under `--json` the outcome travels as typed data: either
972
+ `{ "kind": "applied", "changedRunIds": [...], "pageTakenOffline": true }` or
973
+ `{ "kind": "already-offline" }`.
974
+
975
+ **If the connection drops.** A network error or timeout on either command tells
976
+ you nothing about whether the dashboard applied the change, so the CLI does not
977
+ guess and never tells you to retry. Instead it reads the event back:
978
+
979
+ - `event offline` reports success only when every run reads back non-live and
980
+ the event's `pageState` reads back `offline`. If the dashboard is old enough
981
+ not to report `pageState` at all, the page cannot be proven and the outcome
982
+ stays unknown.
983
+ - `event delete` reads the event immediately before sending the delete. If that
984
+ read found the event and the readback afterwards finds nothing, the deletion
985
+ is reported as a success "confirmed by readback".
986
+ - Anything else is reported as unknown, with a non-zero exit code:
987
+ `The dashboard did not confirm this change and it could not be read back.
988
+ Check the event in the dashboard before running this again.`
989
+ The error keeps the original `sdk:network-error` or `sdk:timeout` code and
990
+ its `transport` kind (see [Network failures and recovery](#network-failures-and-recovery)).
991
+
992
+ #### Individual event dates
993
+
994
+ `event occurrence` manages bookable dates without replacing the parent event
995
+ series. Mutation commands always use the occurrence UUID shown by `list`; a
996
+ calendar date is never accepted as an identity.
997
+
998
+ - `list <event-identifier> <run-identifier>` shows all future dates, including
999
+ cancelled dates. The parent Event and run are both explicit, so multi-run
1000
+ Events never rely on an ambiguous identifier.
1001
+ Add `--all` to include history or `--status scheduled|cancelled|completed` to
1002
+ filter. The CLI follows the API's bounded pages, while `--json` keeps the
1003
+ canonical UTC instants in its single result envelope.
1004
+ - `add` requires `--starts-at` plus exactly one of `--ends-at` or
1005
+ `--duration-minutes`. `update` accepts the same complete schedule replacement,
1006
+ `--capacity <positive-integer>`, or `--inherit-capacity`.
1007
+ - Local timestamps such as `2026-08-01T10:00` use the event's effective
1008
+ timezone: occurrence venue override, then series venue, then site default.
1009
+ Offset timestamps such as `2026-08-01T10:00+01:00` are absolute instants.
1010
+ Do not mix the two modes in one start/end pair.
1011
+ - Local times that do not exist during a spring clock change are rejected.
1012
+ Repeated autumn times are also rejected unless both start and end include
1013
+ explicit offsets. A duration is elapsed real time from the resolved start,
1014
+ even when clocks change during the date.
1015
+ - A manual date or recurring exception can be rescheduled. A generated
1016
+ recurring date cannot be moved or deleted because schedule reconciliation
1017
+ owns it; cancel the generated date and add a replacement exception instead.
1018
+ Cancellation preserves registrations and is idempotent. Deletion requires
1019
+ zero registrations and confirmation (`--yes` for remote or JSON use).
1020
+
1021
+ `pull events` writes the complete non-cancelled occurrence schedule into each
1022
+ portable event file. `push events` creates those rows for a new series and
1023
+ accepts an exact replay for an existing series. It deliberately rejects
1024
+ existing-series additions, removals, or retiming; use the imperative occurrence
1025
+ commands for those operational changes. Cancelled occurrence tombstones remain
1026
+ in the CMS and are omitted from portable authored content.
1027
+
1028
+ ---
1029
+
790
1030
  ## Delete Commands
791
1031
 
792
1032
  Delete content from the CMS.
@@ -987,64 +1227,6 @@ riverbankcms compare events
987
1227
  - `0`: Content is in sync
988
1228
  - `1`: Differences found
989
1229
 
990
- ### event
991
-
992
- Manage event series directly through the CLI.
993
-
994
- ```bash
995
- riverbankcms event list
996
- riverbankcms event get summer-fest
997
- riverbankcms event upsert summer-fest --file ./content/events/summer-fest.json
998
- riverbankcms event cancel summer-fest
999
- riverbankcms event delete summer-fest --yes
1000
- riverbankcms event occurrence list summer-fest summer-run
1001
- riverbankcms event occurrence add summer-fest summer-run \
1002
- --starts-at 2026-08-01T10:00 \
1003
- --duration-minutes 60
1004
- riverbankcms event occurrence update summer-fest summer-run <occurrence-uuid> \
1005
- --capacity 20
1006
- riverbankcms event occurrence cancel summer-fest summer-run <occurrence-uuid>
1007
- riverbankcms event occurrence delete summer-fest summer-run <occurrence-uuid> --yes
1008
- ```
1009
-
1010
- `event upsert` and `push events` accept canonical event files with `path` and `blocks`. Events use native page authority: an `eventOffering` or `eventSeries` subject owns a page surface, and that surface owns its route, field content, publication state, and slots. `path` updates the page-surface route. `blocks` maps slot keys to surface-slot blocks, for example `blocks.main` for the built-in event template. Use stable block identifiers so repeat pushes update existing slot blocks safely. Unknown slot keys or block kinds fail validation; default slots accept enabled SDK `custom.*` blocks, while explicit template `allowedBlocks` lists remain authoritative. Ordinary editorial entries continue to use the separate `entry` commands and content-entry model described above.
1011
-
1012
- #### Individual event dates
1013
-
1014
- `event occurrence` manages bookable dates without replacing the parent event
1015
- series. Mutation commands always use the occurrence UUID shown by `list`; a
1016
- calendar date is never accepted as an identity.
1017
-
1018
- - `list <event-identifier> <run-identifier>` shows all future dates, including
1019
- cancelled dates. The parent Event and run are both explicit, so multi-run
1020
- Events never rely on an ambiguous identifier.
1021
- Add `--all` to include history or `--status scheduled|cancelled|completed` to
1022
- filter. The CLI follows the API's bounded pages, while `--json` keeps the
1023
- canonical UTC instants in its single result envelope.
1024
- - `add` requires `--starts-at` plus exactly one of `--ends-at` or
1025
- `--duration-minutes`. `update` accepts the same complete schedule replacement,
1026
- `--capacity <positive-integer>`, or `--inherit-capacity`.
1027
- - Local timestamps such as `2026-08-01T10:00` use the event's effective
1028
- timezone: occurrence venue override, then series venue, then site default.
1029
- Offset timestamps such as `2026-08-01T10:00+01:00` are absolute instants.
1030
- Do not mix the two modes in one start/end pair.
1031
- - Local times that do not exist during a spring clock change are rejected.
1032
- Repeated autumn times are also rejected unless both start and end include
1033
- explicit offsets. A duration is elapsed real time from the resolved start,
1034
- even when clocks change during the date.
1035
- - A manual date or recurring exception can be rescheduled. A generated
1036
- recurring date cannot be moved or deleted because schedule reconciliation
1037
- owns it; cancel the generated date and add a replacement exception instead.
1038
- Cancellation preserves registrations and is idempotent. Deletion requires
1039
- zero registrations and confirmation (`--yes` for remote or JSON use).
1040
-
1041
- `pull events` writes the complete non-cancelled occurrence schedule into each
1042
- portable event file. `push events` creates those rows for a new series and
1043
- accepts an exact replay for an existing series. It deliberately rejects
1044
- existing-series additions, removals, or retiming; use the imperative occurrence
1045
- commands for those operational changes. Cancelled occurrence tombstones remain
1046
- in the CMS and are omitted from portable authored content.
1047
-
1048
1230
  ### verify
1049
1231
 
1050
1232
  Compare local SDK config content types against target site.
@@ -743,14 +743,18 @@ Footer links are managed via the site's footer navigation menu (see `content/nav
743
743
  ISO datetimes and every item carries the event's IANA timezone.
744
744
  - Run `venueRef` and `categoryRef` values reference identifiers from venues and event-categories.
745
745
  - `status`: "draft", "active", "completed", "cancelled", or "archived"
746
- - `visibility`: "private", "public", or "unlisted"
746
+ - `visibility`: "public" (listed on the site and findable in search) or
747
+ "unlisted" (the link works and people can book, but it is left out of
748
+ listings and search). A run that has not gone live yet is a "draft"
749
+ `status`, not a listing choice.
747
750
  - Event page content is owned by the native `eventOffering` or `eventSeries` page-surface subject.
751
+ - Updating an online Event's `content` through `event upsert` or `push events` publishes its field content immediately, as the dashboard Content tab does. Its route, blocks and layout keep their separate draft and publish flow.
748
752
  - `blocks` writes to the event page surface's template-defined surface slots. The default event template declares `blocks.main`.
749
753
  - Use stable block `identifier` values. Re-pushing the same event updates matching slot blocks instead of creating duplicates.
750
754
  - Default surface slots accept built-in content blocks plus enabled SDK custom blocks. If a template sets `allowedBlocks`, that explicit list is authoritative.
751
755
  - `riverbankcms push events` aborts when two `content/events/*.json` files use the same `identifier`; merge or delete stale duplicates before pushing.
752
756
 
753
- See `cli-reference.md` for event-related CLI commands: `riverbankcms push/pull venues|event-categories|events`.
757
+ See `cli-reference.md` for event-related CLI commands: `riverbankcms push/pull venues|event-categories|events`, plus `riverbankcms event offline` and `riverbankcms event delete` for taking a published event offline and then removing it.
754
758
 
755
759
  `riverbankcms pull events` writes every non-cancelled occurrence to the event
756
760
  file. Pushing a new event creates that explicit schedule, and pushing an
@@ -190,6 +190,10 @@ riverbankcms page publish about
190
190
 
191
191
  If the page identifier already exists, `upsert` will update it. This is intentional - use unique identifiers for new pages.
192
192
 
193
+ ### Path already in use
194
+
195
+ Creating a page on a path that another page or route already uses fails with a `409` and writes nothing. The message names the page that holds the path when there is one (`Path "/contact" is already used by page "contact-us"`); otherwise it says another route on the site uses it, such as an entry or event page. Pick a different path, or upsert the existing page by its identifier instead.
196
+
193
197
  ### Block not appearing
194
198
 
195
199
  - Verify the block was created: `riverbankcms block get <page> <block-id>`
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as classifySdkEventBookingOffer, i as classifySdkEventSchedule, r as sdkEventUpsertOptionalFieldSchemas } from "./sdk-event-upsert-BNxbxaA0.mjs";
3
- import { l as EVENT_PRESET_VALUES } from "./src-DbmhanBI.mjs";
2
+ import { a as classifySdkEventSchedule, i as sdkEventUpsertOptionalFieldSchemas, n as sdkCourseBookingSettingsSchema, o as classifySdkEventBookingOffer } from "./sdk-event-upsert-CRQseim3.mjs";
3
+ import { l as EVENT_PRESET_VALUES } from "./src-CiZrHLCj.mjs";
4
4
  import { z } from "zod";
5
5
 
6
6
  //#region ../blocks/src/lib/typeGuards.ts
@@ -4804,6 +4804,7 @@ const eventRunInputSchema = z.object({
4804
4804
  }).strict();
4805
4805
  const eventUpsertInputSchema = z.object({
4806
4806
  identifier: z.string().min(1).optional(),
4807
+ courseBooking: sdkCourseBookingSettingsSchema.optional(),
4807
4808
  title: z.string().min(1, "title is required"),
4808
4809
  slug: z.string().min(1).optional(),
4809
4810
  content: z.record(z.string(), z.unknown()).optional(),
@@ -7,7 +7,7 @@ import { existsSync as existsSync$1 } from "fs";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
9
  //#region package.json
10
- var version = "0.9.0";
10
+ var version = "0.11.0";
11
11
  var peerDependencies = { "@riverbankcms/sdk": ">=0.139.0 <1.0.0" };
12
12
  var package_default = {
13
13
  name: "riverbankcms",
@@ -83,7 +83,7 @@ var package_default = {
83
83
  //#endregion
84
84
  //#region src/version.ts
85
85
  /** The package manifest is the source-run fallback; builds inject this value. */
86
- const CLI_VERSION = typeof "0.9.0" === "string" ? "0.9.0" : version;
86
+ const CLI_VERSION = typeof "0.11.0" === "string" ? "0.11.0" : version;
87
87
  /**
88
88
  * The CLI's SDK peer range is bundled from the CLI manifest alongside its
89
89
  * version. Runtime config loading uses this value as the single compatibility
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { t as loadConfigFile } from "./load-config-B7AiZalH.mjs";
3
+
4
+ export { loadConfigFile };