riverbankcms 0.4.3 → 0.6.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/README.md CHANGED
@@ -30,3 +30,42 @@ a new identifier, or delete and upload the asset again in the dashboard.
30
30
  Every persisted media asset now has a canonical, site-unique identifier. Backup
31
31
  restore preserves an existing asset's identifier and rejects an archive that
32
32
  tries to replace it.
33
+
34
+ ## Media library commands
35
+
36
+ Linked SDK projects can manage library assets by stable identifier:
37
+
38
+ ```sh
39
+ riverbankcms media add ./hero.jpg --env local
40
+ riverbankcms media list --search hero --env both
41
+ riverbankcms media download hero --output ./downloads --env remote
42
+ riverbankcms media delete hero --dry-run --env both
43
+ riverbankcms media delete hero --yes --env both
44
+ ```
45
+
46
+ `media add` accepts explicit regular files only. For multiple files, identifiers
47
+ come from filenames; `--identifier` and `--title` are single-file options. Add
48
+ and delete preflight every selected environment before mutation, and delete
49
+ refuses assets still referenced by content. `media download` preserves the
50
+ stored safe filename, refuses overwrites unless `--overwrite` is present, and
51
+ uses `local/` and `remote/` subdirectories for `--env both`.
52
+
53
+ These commands manage library records and bytes. They do not rewrite media
54
+ references inside pages, entries, blocks, or other content.
55
+
56
+ ## Account site backups
57
+
58
+ Signed-in account owners can export a safe-profile backup and import it as a
59
+ new, independent site without linking a project:
60
+
61
+ ```sh
62
+ riverbankcms sites export my-site --account-env staging
63
+ riverbankcms sites import my-site-backup.zip --account-env local
64
+ ```
65
+
66
+ Exports include media and exclude memberships, access, and credentials. The
67
+ default filename is `<site-slug>-backup.zip`; existing files require
68
+ `--overwrite`. Imports are limited to 100 MB, show the server preview before a
69
+ single confirmation, always create a fresh site identity, and never run
70
+ `riverbankcms link` automatically. Use `--yes` for JSON or non-interactive
71
+ imports.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import "../src-DKm0bLEA.mjs";
3
- import "../sdk-event-upsert-Dy08IXYL.mjs";
4
- import { t as eventUpsertInputSchema } from "../eventUpsertInputSchema-7QFvBuxF.mjs";
2
+ import "../sdk-event-upsert-CvFCbzNc.mjs";
3
+ import "../src-X9oZOIoK.mjs";
4
+ import { t as eventUpsertInputSchema } from "../eventUpsertInputSchema-CtkDnYlQ.mjs";
5
5
 
6
6
  //#region src/acceptance/matrix-builders.ts
7
7
  const workflow = (scenario, stepId) => ({
@@ -56,6 +56,18 @@ const writeFileStep = (id, path, content) => ({
56
56
  path,
57
57
  content: `${JSON.stringify(content, null, 2)}\n`
58
58
  });
59
+ const writeBase64FileStep = (id, path, base64) => ({
60
+ kind: "write_base64_file",
61
+ id,
62
+ path,
63
+ base64
64
+ });
65
+ const assertFilesEqualStep = (id, leftPath, rightPath) => ({
66
+ kind: "assert_files_equal",
67
+ id,
68
+ leftPath,
69
+ rightPath
70
+ });
59
71
  const assertJsonFileStep = (id, path, jsonPath, expected) => ({
60
72
  kind: "assert_json_file",
61
73
  id,
@@ -301,8 +313,300 @@ const bookingSettingsScenario = {
301
313
  ]
302
314
  };
303
315
 
316
+ //#endregion
317
+ //#region src/acceptance/event-scenario.ts
318
+ const eventAcceptanceInput = eventUpsertInputSchema.parse({
319
+ title: "CLI acceptance event",
320
+ slug: "acceptance-event",
321
+ preset: "event",
322
+ runRefs: ["acceptance-event"],
323
+ runs: [{
324
+ identifier: "acceptance-event",
325
+ eventRef: "acceptance-event",
326
+ bookingScope: "whole_series",
327
+ occurrences: [{
328
+ startsAt: "2026-12-01T10:00:00.000Z",
329
+ endsAt: "2026-12-01T11:00:00.000Z",
330
+ timeZone: "Europe/London"
331
+ }],
332
+ bookingOffer: {
333
+ bookingRequirement: "required",
334
+ pricing: {
335
+ kind: "free",
336
+ currency: "GBP"
337
+ },
338
+ paymentTerms: { allowedOptions: [{ kind: "upfront" }] }
339
+ }
340
+ }]
341
+ });
342
+ const eventScenario = {
343
+ id: "event-occurrence-lifecycle",
344
+ steps: [
345
+ commandStep("upsert", "event upsert", [
346
+ "event",
347
+ "upsert",
348
+ "acceptance-event",
349
+ "--data",
350
+ JSON.stringify(eventAcceptanceInput),
351
+ "--env=local",
352
+ "--json"
353
+ ]),
354
+ {
355
+ kind: "known_identifier",
356
+ id: "event-identifier",
357
+ value: "acceptance-event"
358
+ },
359
+ commandStep("get", "event get", [
360
+ "event",
361
+ "get",
362
+ capture("event-identifier"),
363
+ "--env=local",
364
+ "--json"
365
+ ]),
366
+ commandStep("list-occurrences", "event occurrence list", [
367
+ "event",
368
+ "occurrence",
369
+ "list",
370
+ capture("event-identifier"),
371
+ capture("event-identifier"),
372
+ "--env=local",
373
+ "--json"
374
+ ]),
375
+ commandStep("add-occurrence", "event occurrence add", [
376
+ "event",
377
+ "occurrence",
378
+ "add",
379
+ capture("event-identifier"),
380
+ capture("event-identifier"),
381
+ "--starts-at",
382
+ "2026-12-10T10:00:00Z",
383
+ "--duration-minutes",
384
+ "60",
385
+ "--env=local",
386
+ "--json"
387
+ ]),
388
+ commandStep("update-occurrence", "event occurrence update", [
389
+ "event",
390
+ "occurrence",
391
+ "update",
392
+ capture("event-identifier"),
393
+ capture("event-identifier"),
394
+ capture("add-occurrence", "result", "id"),
395
+ "--capacity",
396
+ "20",
397
+ "--env=local",
398
+ "--json"
399
+ ]),
400
+ commandStep("cancel-occurrence", "event occurrence cancel", [
401
+ "event",
402
+ "occurrence",
403
+ "cancel",
404
+ capture("event-identifier"),
405
+ capture("event-identifier"),
406
+ capture("add-occurrence", "result", "id"),
407
+ "--env=local",
408
+ "--json"
409
+ ]),
410
+ commandStep("delete-occurrence", "event occurrence delete", [
411
+ "event",
412
+ "occurrence",
413
+ "delete",
414
+ capture("event-identifier"),
415
+ capture("event-identifier"),
416
+ capture("add-occurrence", "result", "id"),
417
+ "--yes",
418
+ "--env=local",
419
+ "--json"
420
+ ]),
421
+ commandStep("cancel-event", "event cancel", [
422
+ "event",
423
+ "cancel",
424
+ capture("event-identifier"),
425
+ "--env=local",
426
+ "--json"
427
+ ]),
428
+ commandStep("delete-event", "event delete", [
429
+ "event",
430
+ "delete",
431
+ capture("event-identifier"),
432
+ "--yes",
433
+ "--env=local",
434
+ "--json"
435
+ ])
436
+ ]
437
+ };
438
+
439
+ //#endregion
440
+ //#region src/acceptance/media-scenario.ts
441
+ const ONE_PIXEL_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAOaQqR0AAAAASUVORK5CYII=";
442
+ const mediaScenario = {
443
+ id: "media-library-lifecycle",
444
+ steps: [
445
+ writeBase64FileStep("fixture", ".acceptance/media/acceptance-media.png", ONE_PIXEL_PNG),
446
+ commandStep("add", "media add", [
447
+ "media",
448
+ "add",
449
+ ".acceptance/media/acceptance-media.png",
450
+ "--identifier",
451
+ "acceptance-media",
452
+ "--yes",
453
+ "--env=local",
454
+ "--json"
455
+ ], { jsonAssertions: [{
456
+ jsonPath: [
457
+ "result",
458
+ "environments",
459
+ "0",
460
+ "result",
461
+ "items",
462
+ "0",
463
+ "status"
464
+ ],
465
+ expected: "created"
466
+ }] }),
467
+ commandStep("repeat", "media add", [
468
+ "media",
469
+ "add",
470
+ ".acceptance/media/acceptance-media.png",
471
+ "--identifier",
472
+ "acceptance-media",
473
+ "--yes",
474
+ "--env=local",
475
+ "--json"
476
+ ], { jsonAssertions: [{
477
+ jsonPath: [
478
+ "result",
479
+ "environments",
480
+ "0",
481
+ "result",
482
+ "items",
483
+ "0",
484
+ "status"
485
+ ],
486
+ expected: "unchanged"
487
+ }] }),
488
+ commandStep("alias-conflict", "media add", [
489
+ "media",
490
+ "add",
491
+ ".acceptance/media/acceptance-media.png",
492
+ "--identifier",
493
+ "acceptance-media-alias",
494
+ "--yes",
495
+ "--env=local",
496
+ "--json"
497
+ ], { expectedExitCode: 1 }),
498
+ commandStep("list", "media list", [
499
+ "media",
500
+ "list",
501
+ "--search",
502
+ "acceptance-media",
503
+ "--env=local",
504
+ "--json"
505
+ ], { jsonAssertions: [{
506
+ jsonPath: [
507
+ "result",
508
+ "environments",
509
+ "0",
510
+ "result",
511
+ "items",
512
+ "0",
513
+ "identifier"
514
+ ],
515
+ expected: "acceptance-media"
516
+ }] }),
517
+ commandStep("download", "media download", [
518
+ "media",
519
+ "download",
520
+ "acceptance-media",
521
+ "--output",
522
+ ".acceptance/media-download",
523
+ "--env=local",
524
+ "--json"
525
+ ]),
526
+ assertFilesEqualStep("download-local-equal", ".acceptance/media/acceptance-media.png", ".acceptance/media-download/acceptance-media.png"),
527
+ commandStep("reference-page", "page upsert", [
528
+ "page",
529
+ "upsert",
530
+ "acceptance-media-page",
531
+ "--title",
532
+ "CLI acceptance media page",
533
+ "--path",
534
+ "/cli-acceptance-media",
535
+ "--env=local",
536
+ "--json"
537
+ ]),
538
+ commandStep("reference-block", "block upsert", [
539
+ "block",
540
+ "upsert",
541
+ capture("reference-page", "result", "identifier"),
542
+ "acceptance-media-block",
543
+ "--kind",
544
+ "block.media-text",
545
+ "--data",
546
+ JSON.stringify({
547
+ text: richText("CLI acceptance media reference."),
548
+ images: [{ image: { identifier: "acceptance-media" } }]
549
+ }),
550
+ "--env=local",
551
+ "--json"
552
+ ]),
553
+ commandStep("guarded-delete", "media delete", [
554
+ "media",
555
+ "delete",
556
+ "acceptance-media",
557
+ "--yes",
558
+ "--env=local",
559
+ "--json"
560
+ ], { expectedExitCode: 1 }),
561
+ commandStep("remove-reference", "delete block", [
562
+ "delete",
563
+ "block",
564
+ capture("reference-page", "result", "identifier"),
565
+ capture("reference-block", "result", "identifier"),
566
+ "--yes",
567
+ "--env=local",
568
+ "--json"
569
+ ]),
570
+ commandStep("delete-preview", "media delete", [
571
+ "media",
572
+ "delete",
573
+ "acceptance-media",
574
+ "--dry-run",
575
+ "--env=local",
576
+ "--json"
577
+ ], { jsonAssertions: [{
578
+ jsonPath: [
579
+ "result",
580
+ "environments",
581
+ "0",
582
+ "result",
583
+ "kind"
584
+ ],
585
+ expected: "dry_run"
586
+ }] }),
587
+ commandStep("delete", "media delete", [
588
+ "media",
589
+ "delete",
590
+ "acceptance-media",
591
+ "--yes",
592
+ "--env=local",
593
+ "--json"
594
+ ], { jsonAssertions: [{
595
+ jsonPath: [
596
+ "result",
597
+ "environments",
598
+ "0",
599
+ "result",
600
+ "kind"
601
+ ],
602
+ expected: "deleted"
603
+ }] })
604
+ ]
605
+ };
606
+
304
607
  //#endregion
305
608
  //#region src/acceptance/matrix.ts
609
+ const expectedCommandFailure = { expectedExitCode: 1 };
306
610
  const entryScenario = {
307
611
  id: "entry-lifecycle",
308
612
  steps: [
@@ -457,119 +761,6 @@ const blockScenario = {
457
761
  ])
458
762
  ]
459
763
  };
460
- const eventAcceptanceInput = eventUpsertInputSchema.parse({
461
- title: "CLI acceptance event",
462
- slug: "acceptance-event",
463
- preset: "event",
464
- runRefs: ["acceptance-event"],
465
- runs: [{
466
- identifier: "acceptance-event",
467
- eventRef: "acceptance-event",
468
- bookingScope: "whole_series",
469
- occurrences: [{
470
- startsAt: "2026-12-01T10:00:00.000Z",
471
- endsAt: "2026-12-01T11:00:00.000Z",
472
- timeZone: "Europe/London"
473
- }],
474
- pricing: { requiresPayment: false }
475
- }]
476
- });
477
- const eventScenario = {
478
- id: "event-occurrence-lifecycle",
479
- steps: [
480
- commandStep("upsert", "event upsert", [
481
- "event",
482
- "upsert",
483
- "acceptance-event",
484
- "--data",
485
- JSON.stringify(eventAcceptanceInput),
486
- "--env=local",
487
- "--json"
488
- ]),
489
- {
490
- kind: "known_identifier",
491
- id: "event-identifier",
492
- value: "acceptance-event"
493
- },
494
- commandStep("get", "event get", [
495
- "event",
496
- "get",
497
- capture("event-identifier"),
498
- "--env=local",
499
- "--json"
500
- ]),
501
- commandStep("list-occurrences", "event occurrence list", [
502
- "event",
503
- "occurrence",
504
- "list",
505
- capture("event-identifier"),
506
- capture("event-identifier"),
507
- "--env=local",
508
- "--json"
509
- ]),
510
- commandStep("add-occurrence", "event occurrence add", [
511
- "event",
512
- "occurrence",
513
- "add",
514
- capture("event-identifier"),
515
- capture("event-identifier"),
516
- "--starts-at",
517
- "2026-12-10T10:00:00Z",
518
- "--duration-minutes",
519
- "60",
520
- "--env=local",
521
- "--json"
522
- ]),
523
- commandStep("update-occurrence", "event occurrence update", [
524
- "event",
525
- "occurrence",
526
- "update",
527
- capture("event-identifier"),
528
- capture("event-identifier"),
529
- capture("add-occurrence", "result", "id"),
530
- "--capacity",
531
- "20",
532
- "--env=local",
533
- "--json"
534
- ]),
535
- commandStep("cancel-occurrence", "event occurrence cancel", [
536
- "event",
537
- "occurrence",
538
- "cancel",
539
- capture("event-identifier"),
540
- capture("event-identifier"),
541
- capture("add-occurrence", "result", "id"),
542
- "--env=local",
543
- "--json"
544
- ]),
545
- commandStep("delete-occurrence", "event occurrence delete", [
546
- "event",
547
- "occurrence",
548
- "delete",
549
- capture("event-identifier"),
550
- capture("event-identifier"),
551
- capture("add-occurrence", "result", "id"),
552
- "--yes",
553
- "--env=local",
554
- "--json"
555
- ]),
556
- commandStep("cancel-event", "event cancel", [
557
- "event",
558
- "cancel",
559
- capture("event-identifier"),
560
- "--env=local",
561
- "--json"
562
- ]),
563
- commandStep("delete-event", "event delete", [
564
- "event",
565
- "delete",
566
- capture("event-identifier"),
567
- "--yes",
568
- "--env=local",
569
- "--json"
570
- ])
571
- ]
572
- };
573
764
  /**
574
765
  * Every entry is keyed by the complete Commander leaf path. The automated
575
766
  * runner executes representatives and grouped scratch workflows; provider and
@@ -666,7 +857,7 @@ const acceptanceTuples = [
666
857
  cwd: "nested",
667
858
  output: "non-empty"
668
859
  })],
669
- ["init", scratchExecute(["init", "--json"], { expectedExitCode: 1 })],
860
+ ["init", scratchExecute(["init", "--json"], expectedCommandFailure)],
670
861
  ["identifiers backfill", scratchExecute([
671
862
  "identifiers",
672
863
  "backfill",
@@ -684,7 +875,7 @@ const acceptanceTuples = [
684
875
  "verify",
685
876
  "--env=local",
686
877
  "--json"
687
- ], { expectedExitCode: 1 })],
878
+ ], expectedCommandFailure)],
688
879
  ["publish-all", scratchDryRun([
689
880
  "publish-all",
690
881
  "post",
@@ -703,7 +894,7 @@ const acceptanceTuples = [
703
894
  "post",
704
895
  "--env=local",
705
896
  "--json"
706
- ], { expectedExitCode: 1 })],
897
+ ], expectedCommandFailure)],
707
898
  ["event list", scratchExecute([
708
899
  "event",
709
900
  "list",
@@ -749,34 +940,38 @@ const acceptanceTuples = [
749
940
  "--json"
750
941
  ])],
751
942
  ["settings booking configure", workflow(bookingSettingsScenario, "apply")],
943
+ ["media add", workflow(mediaScenario, "add")],
944
+ ["media list", workflow(mediaScenario, "list")],
945
+ ["media download", workflow(mediaScenario, "download")],
946
+ ["media delete", workflow(mediaScenario, "delete")],
752
947
  ["login", scratchExecute([
753
948
  "login",
754
949
  "--account-env=local",
755
950
  "--json"
756
- ], { expectedExitCode: 1 })],
951
+ ], expectedCommandFailure)],
757
952
  ["logout", scratchExecute([
758
953
  "logout",
759
954
  "--account-env=local",
760
955
  "--json"
761
- ], { expectedExitCode: 1 })],
956
+ ], expectedCommandFailure)],
762
957
  ["whoami", scratchExecute([
763
958
  "whoami",
764
959
  "--account-env=local",
765
960
  "--json"
766
- ], { expectedExitCode: 1 })],
961
+ ], expectedCommandFailure)],
767
962
  ["auth revoke", scratchExecute([
768
963
  "auth",
769
964
  "revoke",
770
965
  "--account-env=local",
771
966
  "--yes",
772
967
  "--json"
773
- ], { expectedExitCode: 1 })],
968
+ ], expectedCommandFailure)],
774
969
  ["sites list", scratchExecute([
775
970
  "sites",
776
971
  "list",
777
972
  "--account-env=local",
778
973
  "--json"
779
- ], { expectedExitCode: 1 })],
974
+ ], expectedCommandFailure)],
780
975
  ["sites create", scratchExecute([
781
976
  "sites",
782
977
  "create",
@@ -785,7 +980,7 @@ const acceptanceTuples = [
785
980
  "Acceptance site",
786
981
  "--yes",
787
982
  "--json"
788
- ], { expectedExitCode: 1 })],
983
+ ], expectedCommandFailure)],
789
984
  ["link", scratchExecute([
790
985
  "link",
791
986
  "--project",
@@ -795,13 +990,28 @@ const acceptanceTuples = [
795
990
  "--no-preview",
796
991
  "--account-env=local",
797
992
  "--json"
798
- ], { expectedExitCode: 1 })],
993
+ ], expectedCommandFailure)],
799
994
  ["recover", scratchExecute([
800
995
  "recover",
801
996
  "--project",
802
997
  ".",
803
998
  "--account-env=local",
804
999
  "--json"
1000
+ ], expectedCommandFailure)],
1001
+ ["sites export", scratchExecute([
1002
+ "sites",
1003
+ "export",
1004
+ "acceptance-site",
1005
+ "--account-env=local",
1006
+ "--json"
1007
+ ], { expectedExitCode: 1 })],
1008
+ ["sites import", scratchExecute([
1009
+ "sites",
1010
+ "import",
1011
+ "acceptance-backup.zip",
1012
+ "--account-env=local",
1013
+ "--yes",
1014
+ "--json"
805
1015
  ], { expectedExitCode: 1 })],
806
1016
  ["migrate entries", scratchDryRun([
807
1017
  "migrate",