@riverbankcms/sdk 0.6.0 → 0.7.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.
Files changed (84) hide show
  1. package/README.md +239 -0
  2. package/dist/cli/index.js +22 -5
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/client/client.js +1 -1
  5. package/dist/client/client.js.map +1 -1
  6. package/dist/client/client.mjs +1 -1
  7. package/dist/client/client.mjs.map +1 -1
  8. package/dist/server/{Layout-BClXUTsd.d.mts → Layout-B7cvis7r.d.mts} +3 -3
  9. package/dist/server/{Layout-UXGjXv8M.d.ts → Layout-wBtJLTVX.d.ts} +3 -3
  10. package/dist/server/{chunk-Z5ZA6Q4D.mjs → chunk-74XUVNOO.mjs} +5 -3
  11. package/dist/server/chunk-74XUVNOO.mjs.map +1 -0
  12. package/dist/server/{chunk-PHS2KWJX.js → chunk-7BVRA5MY.js} +7 -52
  13. package/dist/server/chunk-7BVRA5MY.js.map +1 -0
  14. package/dist/server/{chunk-SQMGHEJM.mjs → chunk-7FIJSGHU.mjs} +2 -2
  15. package/dist/server/{chunk-SQMGHEJM.mjs.map → chunk-7FIJSGHU.mjs.map} +1 -1
  16. package/dist/server/chunk-ARNCLSQT.mjs +52 -0
  17. package/dist/server/chunk-ARNCLSQT.mjs.map +1 -0
  18. package/dist/server/chunk-BNQV3PXP.js +69 -0
  19. package/dist/server/chunk-BNQV3PXP.js.map +1 -0
  20. package/dist/server/{chunk-SSS7CCRR.js → chunk-EIVISR62.js} +15 -57
  21. package/dist/server/chunk-EIVISR62.js.map +1 -0
  22. package/dist/server/{chunk-I2D7KOEA.js → chunk-JWRNMNWI.js} +5 -3
  23. package/dist/server/chunk-JWRNMNWI.js.map +1 -0
  24. package/dist/server/{chunk-EOWGKCUZ.js → chunk-P7UVAMK6.js} +2 -2
  25. package/dist/server/{chunk-EOWGKCUZ.js.map → chunk-P7UVAMK6.js.map} +1 -1
  26. package/dist/server/{chunk-2HXHFSMI.mjs → chunk-RBJFXNDM.mjs} +6 -51
  27. package/dist/server/chunk-RBJFXNDM.mjs.map +1 -0
  28. package/dist/server/chunk-SWYWZT3L.mjs +69 -0
  29. package/dist/server/chunk-SWYWZT3L.mjs.map +1 -0
  30. package/dist/server/chunk-T26N3P26.js +52 -0
  31. package/dist/server/chunk-T26N3P26.js.map +1 -0
  32. package/dist/server/{chunk-PMHLZ3FW.mjs → chunk-YXA4GAAQ.mjs} +15 -57
  33. package/dist/server/chunk-YXA4GAAQ.mjs.map +1 -0
  34. package/dist/server/{components-DppHY5oD.d.ts → components-CICSJyp_.d.ts} +1 -1
  35. package/dist/server/{components-BmaJxgCV.d.mts → components-CMMwDXTW.d.mts} +1 -1
  36. package/dist/server/components.d.mts +2 -2
  37. package/dist/server/components.d.ts +2 -2
  38. package/dist/server/components.js +5 -3
  39. package/dist/server/components.js.map +1 -1
  40. package/dist/server/components.mjs +5 -3
  41. package/dist/server/index-BTwWvSBu.d.ts +130 -0
  42. package/dist/server/index-DI_qlYx3.d.mts +130 -0
  43. package/dist/server/index.js +2 -2
  44. package/dist/server/index.mjs +1 -1
  45. package/dist/server/{loadContent-BS-3wesN.d.mts → loadContent-C-YYUKQa.d.mts} +10 -2
  46. package/dist/server/{loadContent-Buvmudee.d.ts → loadContent-DmgpFcFC.d.ts} +10 -2
  47. package/dist/server/metadata.d.mts +8 -135
  48. package/dist/server/metadata.d.ts +8 -135
  49. package/dist/server/metadata.js +5 -65
  50. package/dist/server/metadata.js.map +1 -1
  51. package/dist/server/metadata.mjs +4 -64
  52. package/dist/server/metadata.mjs.map +1 -1
  53. package/dist/server/navigation.d.mts +80 -145
  54. package/dist/server/navigation.d.ts +80 -145
  55. package/dist/server/navigation.js +2 -10
  56. package/dist/server/navigation.js.map +1 -1
  57. package/dist/server/navigation.mjs +7 -15
  58. package/dist/server/next.d.mts +274 -0
  59. package/dist/server/next.d.ts +274 -0
  60. package/dist/server/next.js +150 -0
  61. package/dist/server/next.js.map +1 -0
  62. package/dist/server/next.mjs +150 -0
  63. package/dist/server/next.mjs.map +1 -0
  64. package/dist/server/rendering/server.d.mts +1 -1
  65. package/dist/server/rendering/server.d.ts +1 -1
  66. package/dist/server/rendering/server.js +5 -3
  67. package/dist/server/rendering/server.js.map +1 -1
  68. package/dist/server/rendering/server.mjs +5 -3
  69. package/dist/server/rendering.d.mts +4 -4
  70. package/dist/server/rendering.d.ts +4 -4
  71. package/dist/server/rendering.js +6 -4
  72. package/dist/server/rendering.js.map +1 -1
  73. package/dist/server/rendering.mjs +6 -4
  74. package/dist/server/server.d.mts +1 -1
  75. package/dist/server/server.d.ts +1 -1
  76. package/dist/server/server.js +3 -3
  77. package/dist/server/server.mjs +2 -2
  78. package/package.json +13 -1
  79. package/dist/server/chunk-2HXHFSMI.mjs.map +0 -1
  80. package/dist/server/chunk-I2D7KOEA.js.map +0 -1
  81. package/dist/server/chunk-PHS2KWJX.js.map +0 -1
  82. package/dist/server/chunk-PMHLZ3FW.mjs.map +0 -1
  83. package/dist/server/chunk-SSS7CCRR.js.map +0 -1
  84. package/dist/server/chunk-Z5ZA6Q4D.mjs.map +0 -1
package/README.md CHANGED
@@ -458,6 +458,245 @@ import { usePage, Page } from '@riverbankcms/sdk/client';
458
458
 
459
459
  The `/client` subpath exports React hooks that require client-side React features (useState, useEffect). The main entry point only exports server-safe code.
460
460
 
461
+ ## Navigation
462
+
463
+ The SDK provides navigation utilities for transforming CMS navigation data into render-ready structures. All navigation functions return nested structures that support both direct links and dropdown menus.
464
+
465
+ ### Navigation Types
466
+
467
+ Navigation items are discriminated unions using a `kind` field:
468
+
469
+ ```typescript
470
+ import type {
471
+ NavItem, // NavLink | NavDropdown
472
+ NavLink, // Direct link with href
473
+ NavDropdown, // Dropdown container with children
474
+ } from '@riverbankcms/sdk/navigation';
475
+
476
+ // NavLink - a direct navigation link
477
+ type NavLink = {
478
+ kind: 'link';
479
+ id: string;
480
+ label: string;
481
+ href: string;
482
+ isExternal: boolean;
483
+ };
484
+
485
+ // NavDropdown - a dropdown menu container
486
+ type NavDropdown = {
487
+ kind: 'dropdown';
488
+ id: string;
489
+ label: string;
490
+ children: NavLink[]; // Max 1 level of nesting
491
+ };
492
+
493
+ // NavItem - either a link or dropdown
494
+ type NavItem = NavLink | NavDropdown;
495
+ ```
496
+
497
+ ### Type Guards
498
+
499
+ Use the provided type guards for safe type narrowing:
500
+
501
+ ```typescript
502
+ import { isNavLink, isNavDropdown } from '@riverbankcms/sdk/navigation';
503
+
504
+ const items = getPrimaryNavItems(siteData.navigation);
505
+
506
+ items.forEach(item => {
507
+ if (isNavLink(item)) {
508
+ // TypeScript knows this is NavLink
509
+ console.log(item.href);
510
+ } else if (isNavDropdown(item)) {
511
+ // TypeScript knows this is NavDropdown
512
+ console.log(`${item.label} has ${item.children.length} children`);
513
+ }
514
+ });
515
+ ```
516
+
517
+ Or use the `kind` discriminator directly:
518
+
519
+ ```typescript
520
+ items.forEach(item => {
521
+ if (item.kind === 'link') {
522
+ console.log(item.href);
523
+ } else {
524
+ // item.kind === 'dropdown'
525
+ console.log(item.children);
526
+ }
527
+ });
528
+ ```
529
+
530
+ ### Navigation Helper Functions
531
+
532
+ ```typescript
533
+ import {
534
+ getPrimaryNavItems,
535
+ getNavItemsBySlug,
536
+ getPrimaryNavigation,
537
+ getNavigationBySlug,
538
+ transformToNavItems,
539
+ } from '@riverbankcms/sdk/navigation';
540
+
541
+ // Get nav items from the primary menu (marked isPrimary, or first menu)
542
+ const headerNav = getPrimaryNavItems(siteData.navigation);
543
+
544
+ // Get nav items from a specific menu by name/slug
545
+ const footerNav = getNavItemsBySlug(siteData.navigation, 'footer');
546
+
547
+ // Get the raw menu object (for accessing menu metadata)
548
+ const primaryMenu = getPrimaryNavigation(siteData.navigation);
549
+ const footerMenu = getNavigationBySlug(siteData.navigation, 'footer');
550
+
551
+ // Transform a menu object to nav items
552
+ const items = transformToNavItems(primaryMenu);
553
+ ```
554
+
555
+ ### Building Menu and Logo
556
+
557
+ For rendering, use `buildMenu` and `buildLogo` which resolve all hrefs from the route map:
558
+
559
+ ```typescript
560
+ import type { Menu, Logo } from '@riverbankcms/sdk/navigation';
561
+ import { buildMenu, buildLogo } from '@riverbankcms/sdk/navigation';
562
+
563
+ // Build menu with pre-resolved hrefs from route map
564
+ const menu = buildMenu(siteData.navigation, siteData.routes);
565
+
566
+ // menu.items is NavItem[] (links and dropdowns)
567
+ // menu.ctaItem is NavLink | null (CTA is always a link, never dropdown)
568
+
569
+ const logo = buildLogo(siteData.layout.logo, siteData.site.title);
570
+ ```
571
+
572
+ ### Rendering Navigation with Dropdowns
573
+
574
+ ```tsx
575
+ import { getPrimaryNavItems, isNavLink, isNavDropdown } from '@riverbankcms/sdk/navigation';
576
+
577
+ function Header({ navigation }) {
578
+ const items = getPrimaryNavItems(navigation);
579
+
580
+ return (
581
+ <nav>
582
+ {items.map(item => {
583
+ if (isNavLink(item)) {
584
+ return (
585
+ <a
586
+ key={item.id}
587
+ href={item.href}
588
+ target={item.isExternal ? '_blank' : undefined}
589
+ rel={item.isExternal ? 'noopener noreferrer' : undefined}
590
+ >
591
+ {item.label}
592
+ </a>
593
+ );
594
+ }
595
+
596
+ // Dropdown
597
+ return (
598
+ <div key={item.id} className="dropdown">
599
+ <button>{item.label}</button>
600
+ <div className="dropdown-menu">
601
+ {item.children.map(child => (
602
+ <a
603
+ key={child.id}
604
+ href={child.href}
605
+ target={child.isExternal ? '_blank' : undefined}
606
+ >
607
+ {child.label}
608
+ </a>
609
+ ))}
610
+ </div>
611
+ </div>
612
+ );
613
+ })}
614
+ </nav>
615
+ );
616
+ }
617
+ ```
618
+
619
+ ### Rendering Menu in Components
620
+
621
+ ```tsx
622
+ import { buildMenu, buildLogo } from '@riverbankcms/sdk/navigation';
623
+
624
+ function SiteHeader({ siteData }) {
625
+ const menu = buildMenu(siteData.navigation, siteData.routes);
626
+ const logo = buildLogo(siteData.layout.logo, siteData.site.title);
627
+
628
+ return (
629
+ <header>
630
+ {logo && <img src={logo.src} alt={logo.alt} />}
631
+
632
+ <nav>
633
+ {menu.items.map(item => {
634
+ if (item.kind === 'link') {
635
+ return <a key={item.id} href={item.href}>{item.label}</a>;
636
+ }
637
+
638
+ return (
639
+ <div key={item.id} className="dropdown">
640
+ <span>{item.label}</span>
641
+ <ul>
642
+ {item.children.map(child => (
643
+ <li key={child.id}>
644
+ <a href={child.href}>{child.label}</a>
645
+ </li>
646
+ ))}
647
+ </ul>
648
+ </div>
649
+ );
650
+ })}
651
+ </nav>
652
+
653
+ {menu.ctaItem && (
654
+ <a href={menu.ctaItem.href} className="cta-button">
655
+ {menu.ctaItem.label}
656
+ </a>
657
+ )}
658
+ </header>
659
+ );
660
+ }
661
+ ```
662
+
663
+ ### Navigation Exports
664
+
665
+ ```typescript
666
+ import {
667
+ // Helper functions
668
+ getPrimaryNavItems,
669
+ getNavItemsBySlug,
670
+ getPrimaryNavigation,
671
+ getNavigationBySlug,
672
+ transformToNavItems,
673
+ buildMenu,
674
+ buildLogo,
675
+ buildMenuViewModel,
676
+ buildLogoViewModel,
677
+
678
+ // Type guards
679
+ isNavLink,
680
+ isNavDropdown,
681
+
682
+ // Types
683
+ type NavItem,
684
+ type NavLink,
685
+ type NavDropdown,
686
+ type Menu,
687
+ type Logo,
688
+ type MenuViewModel,
689
+ type MenuLinkViewModel,
690
+ type MenuCtaViewModel,
691
+ type LogoViewModel,
692
+ type LogoSource,
693
+ type LinkValue,
694
+ type InternalLinkValue,
695
+ type ExternalLinkValue,
696
+ type CustomLinkValue,
697
+ } from '@riverbankcms/sdk/navigation';
698
+ ```
699
+
461
700
  ## Layout Component
462
701
 
463
702
  The `Layout` component renders the site header, footer, and wraps your page content. Use this when you want consistent site chrome across all pages.
package/dist/cli/index.js CHANGED
@@ -7227,6 +7227,23 @@ ${contentType} (updated):`);
7227
7227
  }
7228
7228
 
7229
7229
  // src/cli/sync/executor.ts
7230
+ function hasFieldErrors(details) {
7231
+ if (typeof details !== "object" || details === null || !("fieldErrors" in details)) {
7232
+ return false;
7233
+ }
7234
+ const fieldErrors = details.fieldErrors;
7235
+ return Array.isArray(fieldErrors) && fieldErrors.length > 0;
7236
+ }
7237
+ function formatApiError(error) {
7238
+ if (!(error instanceof ManagementApiError)) {
7239
+ return error instanceof Error ? error.message : String(error);
7240
+ }
7241
+ let message = error.message;
7242
+ if (hasFieldErrors(error.details)) {
7243
+ message += "\n" + error.details.fieldErrors.map((fe) => ` ${fe.field} (${fe.code}): ${fe.message}`).join("\n");
7244
+ }
7245
+ return message;
7246
+ }
7230
7247
  function createEmptyResult() {
7231
7248
  return {
7232
7249
  entries: { created: 0, updated: 0, failed: 0 },
@@ -7266,7 +7283,7 @@ async function syncEntries(client, diff, local, options, result) {
7266
7283
  result.errors.push({
7267
7284
  resource: "entry",
7268
7285
  identifier: `${contentType}/${entryDiff.identifier}`,
7269
- error: error instanceof Error ? error.message : String(error)
7286
+ error: formatApiError(error)
7270
7287
  });
7271
7288
  }
7272
7289
  }
@@ -7310,7 +7327,7 @@ async function syncPages(client, diff, local, options, result) {
7310
7327
  result.errors.push({
7311
7328
  resource: "page",
7312
7329
  identifier: pageDiff.identifier,
7313
- error: error instanceof Error ? error.message : String(error)
7330
+ error: formatApiError(error)
7314
7331
  });
7315
7332
  }
7316
7333
  }
@@ -7342,7 +7359,7 @@ async function syncBlocks(client, pageIdentifier, blockDiffs, localBlocks, optio
7342
7359
  result.errors.push({
7343
7360
  resource: "block",
7344
7361
  identifier: `${pageIdentifier}/${blockDiff.identifier}`,
7345
- error: error instanceof Error ? error.message : String(error)
7362
+ error: formatApiError(error)
7346
7363
  });
7347
7364
  }
7348
7365
  }
@@ -7380,7 +7397,7 @@ async function syncNavigation(client, diff, local, options, result) {
7380
7397
  result.errors.push({
7381
7398
  resource: "navigation",
7382
7399
  identifier: navDiff.name,
7383
- error: error instanceof Error ? error.message : String(error)
7400
+ error: formatApiError(error)
7384
7401
  });
7385
7402
  }
7386
7403
  }
@@ -7399,7 +7416,7 @@ async function syncSettings(client, diff, local, options, result) {
7399
7416
  result.errors.push({
7400
7417
  resource: "settings",
7401
7418
  identifier: "site",
7402
- error: error instanceof Error ? error.message : String(error)
7419
+ error: formatApiError(error)
7403
7420
  });
7404
7421
  }
7405
7422
  }