@riverbankcms/sdk 0.70.2 → 0.70.3

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
@@ -3257,6 +3257,35 @@ export default Page;
3257
3257
  export { generateMetadata };
3258
3258
  ```
3259
3259
 
3260
+ ### Lazy Block Overrides
3261
+
3262
+ Eager `blockOverrides` remain supported and are a good fit for small server-safe components. For client-heavy or rarely used custom blocks, register a lazy override so `createCatchAllPage()` resolves only the override modules needed by the page or entry template being rendered:
3263
+
3264
+ ```tsx
3265
+ // src/app/[[...slug]]/page.tsx
3266
+ import { createCatchAllPage } from '@riverbankcms/sdk/next';
3267
+ import { defineLazyBlockOverride } from '@riverbankcms/sdk/rendering';
3268
+ import { getRiverbankClient } from '@/lib/builder-client';
3269
+ import config from '@/riverbank.config';
3270
+
3271
+ const { Page, generateMetadata } = createCatchAllPage({
3272
+ getClient: getRiverbankClient,
3273
+ config,
3274
+ blockOverrides: {
3275
+ 'custom.quiz': defineLazyBlockOverride(async () =>
3276
+ import('@/components/blocks/QuizBlock').then((module) => module.QuizBlock)
3277
+ ),
3278
+ },
3279
+ });
3280
+
3281
+ export default Page;
3282
+ export { generateMetadata };
3283
+ ```
3284
+
3285
+ Keep the `import()` inside the lazy factory. Wrapping a component that was imported at the top of the route file is still eager from the bundler's point of view and will not slim the catch-all route graph. Lazy factories must be wrapped with `defineLazyBlockOverride()`; raw promise-returning functions are not treated as lazy overrides.
3286
+
3287
+ `createCatchAllPage()` filters and resolves lazy overrides per rendered page, so unused factories are not invoked. Next.js still builds the client-reference manifest from the static catch-all route graph, though, so a static `import('./HeavyClientBlock')` inside the route graph can still appear in internal build manifests even when that block is not on the current page. For client-heavy blocks, keep the server override wrapper small and put ALTCHA/forms/booking widgets behind block-local client boundaries, then verify the production build artifacts for the specific site.
3288
+
3260
3289
  This single call replaces ~160 lines of typical boilerplate that handles:
3261
3290
  - Path resolution from URL slugs
3262
3291
  - Content loading (pages and entries)
@@ -6,6 +6,9 @@
6
6
  * Usage:
7
7
  * riverbankcms delete entry <type> <identifier>
8
8
  * riverbankcms delete block <page-identifier> <block-identifier>
9
+ *
10
+ * These destructive one-off commands intentionally remain direct for #931 so
11
+ * their confirmation and remote --yes safety semantics stay unchanged.
9
12
  */
10
13
  import { Command } from 'commander';
11
14
  export declare const deleteCommand: Command;
@@ -4,6 +4,7 @@
4
4
  * Apply bounded site-style selections via the command-backed theme API.
5
5
  */
6
6
  import { Command } from 'commander';
7
+ import { type CuratedSiteStyle } from '../../../../theme-core/src/site-styles/index';
7
8
  import type { ThemeStyleSelectionApplyInput } from '../../client/management';
8
9
  export type StyleApplyOptions = Readonly<{
9
10
  siteStyleId: string;
@@ -15,5 +16,22 @@ export type StyleApplyOptions = Readonly<{
15
16
  headerLookId?: string;
16
17
  footerLookId?: string;
17
18
  }>;
19
+ export type StyleCatalogListOptions = Readonly<{
20
+ siteStyleId?: string;
21
+ }>;
22
+ export type StyleCatalogChoice = Readonly<{
23
+ id: string;
24
+ label: string;
25
+ }>;
26
+ export type StyleCatalogListRow = Readonly<{
27
+ siteStyleId: string;
28
+ name: string;
29
+ palettes: readonly StyleCatalogChoice[];
30
+ buttonPersonalities: readonly StyleCatalogChoice[];
31
+ headerLooks: readonly StyleCatalogChoice[];
32
+ footerLooks: readonly StyleCatalogChoice[];
33
+ }>;
18
34
  export declare function buildStyleSelectionApplyInputFromOptions(options: StyleApplyOptions): ThemeStyleSelectionApplyInput;
35
+ export declare function buildStyleCatalogListRows(styles?: readonly CuratedSiteStyle[], options?: StyleCatalogListOptions): readonly StyleCatalogListRow[];
36
+ export declare function formatStyleCatalogListRow(row: StyleCatalogListRow): string[];
19
37
  export declare const styleCommand: Command;
@@ -5,6 +5,7 @@
5
5
  * Reduces duplication and ensures consistent behavior.
6
6
  */
7
7
  import { Command } from 'commander';
8
+ import type { SiteId } from '../../../core/src/index';
8
9
  import type { ManagementClient } from '../client/management/types';
9
10
  import { type OutputContext } from './output';
10
11
  import { type CliErrorContext } from './errors';
@@ -52,6 +53,8 @@ export interface CommandContext {
52
53
  client: ManagementClient;
53
54
  isRemote: boolean;
54
55
  isJsonOutput: boolean;
56
+ targetEnv: EnvTarget;
57
+ siteId: SiteId;
55
58
  /** Environment targets for this operation */
56
59
  envTargets: EnvTarget[];
57
60
  }
@@ -271,6 +274,11 @@ export declare function handleCommandError(error: unknown, output: OutputContext
271
274
  * }))
272
275
  */
273
276
  export declare function withErrorHandling<TArgs extends unknown[]>(action: (...args: [...TArgs, Command]) => Promise<void>): (...args: [...TArgs, Command]) => Promise<void>;
277
+ /**
278
+ * Wrap a command action so handlers receive a resolved command context instead
279
+ * of the raw Commander instance.
280
+ */
281
+ export declare function withCommandContext<TArgs extends unknown[], TOptions = unknown>(action: (ctx: CommandContext, ...args: [...TArgs, TOptions]) => Promise<void>): (...args: [...TArgs, TOptions, Command]) => Promise<void>;
274
282
  /**
275
283
  * Wrap a destructive command action with confirmation prompt and error handling.
276
284
  *
@@ -289,39 +297,6 @@ export declare function withErrorHandling<TArgs extends unknown[]>(action: (...a
289
297
  * ))
290
298
  */
291
299
  export declare function withConfirmation<TArgs extends unknown[]>(getMessage: (...args: TArgs) => string, action: (ctx: CommandContext, ...args: TArgs) => Promise<void>): (...args: [...TArgs, ConfirmOptions, Command]) => Promise<void>;
292
- /**
293
- * Configuration for publish/unpublish command factories.
294
- */
295
- export interface PublishCommandConfig {
296
- /** Resource name for display (e.g., 'entry', 'page') */
297
- resourceName: string;
298
- /** Argument names (e.g., ['type', 'identifier'] or ['identifier']) */
299
- args: readonly string[];
300
- /** Function to call the appropriate client method */
301
- action: (client: ManagementClient, ...args: string[]) => Promise<void>;
302
- }
303
- /**
304
- * Create a publish command for a resource.
305
- *
306
- * @example
307
- * const publishCommand = createPublishCommand({
308
- * resourceName: 'entry',
309
- * args: ['type', 'identifier'],
310
- * action: (client, type, id) => client.entries.publish(type, id),
311
- * });
312
- */
313
- export declare function createPublishCommand(config: PublishCommandConfig): Command;
314
- /**
315
- * Create an unpublish command for a resource.
316
- *
317
- * @example
318
- * const unpublishCommand = createUnpublishCommand({
319
- * resourceName: 'entry',
320
- * args: ['type', 'identifier'],
321
- * action: (client, type, id) => client.entries.unpublish(type, id),
322
- * });
323
- */
324
- export declare function createUnpublishCommand(config: PublishCommandConfig): Command;
325
300
  /**
326
301
  * Configuration for get command factory.
327
302
  */
@@ -0,0 +1,81 @@
1
+ import { type Result, type SiteId } from '../../../../core/src/index';
2
+ import type { ManagementBlock, ManagementClient, ManagementEntry, ManagementNavigationMenu, ManagementPage, NavigationItemInput } from '../../client/management/types';
3
+ import type { EnvTarget } from '../helpers';
4
+ import { type CliCommandCompileBatchSuccessWithReports, type CliCommandValidationCompileError } from './commandRuntime';
5
+ import { type EntryCommandReportSubject, type EntrySiteCommand, type EntrySiteCommandType } from './entryCommands';
6
+ import { type NavigationCommandReportSubject, type NavigationSiteCommand, type NavigationSiteCommandType } from './navigationCommands';
7
+ import { type PageCommandReportSubject, type PageSiteCommand, type PageSiteCommandType } from './pageCommands';
8
+ export type OneOffCommandBatchSource = 'one_off_entry' | 'one_off_page' | 'one_off_block' | 'one_off_navigation';
9
+ type OneOffCompileInput<TMutation> = Readonly<{
10
+ source: OneOffCommandBatchSource;
11
+ siteId: SiteId;
12
+ targetEnv: EnvTarget;
13
+ mutation: TMutation;
14
+ }>;
15
+ export type OneOffEntryMutation = Readonly<{
16
+ kind: 'upsert';
17
+ contentType: string;
18
+ identifier: string;
19
+ data: Record<string, unknown>;
20
+ slug?: string;
21
+ }> | Readonly<{
22
+ kind: 'publish' | 'unpublish';
23
+ contentType: string;
24
+ identifier: string;
25
+ }>;
26
+ export type OneOffPageMutation = Readonly<{
27
+ kind: 'upsert';
28
+ identifier: string;
29
+ title: string;
30
+ path: string;
31
+ seoTitle?: string;
32
+ seoDescription?: string;
33
+ }> | Readonly<{
34
+ kind: 'publish' | 'unpublish';
35
+ identifier: string;
36
+ }>;
37
+ export type OneOffBlockMutation = Readonly<{
38
+ kind: 'upsert';
39
+ pageIdentifier: string;
40
+ blockIdentifier: string;
41
+ blockKind: string;
42
+ data: Record<string, unknown>;
43
+ }> | Readonly<{
44
+ kind: 'reorder';
45
+ pageIdentifier: string;
46
+ blockIdentifiers: readonly string[];
47
+ }>;
48
+ export type OneOffNavigationMutation = Readonly<{
49
+ kind: 'upsert';
50
+ menuName: string;
51
+ items: readonly NavigationItemInput[];
52
+ }>;
53
+ export type OneOffEntryCommandCompileSuccess = CliCommandCompileBatchSuccessWithReports<EntrySiteCommandType, EntryCommandReportSubject>;
54
+ export type OneOffPageCommandCompileSuccess = CliCommandCompileBatchSuccessWithReports<PageSiteCommandType, PageCommandReportSubject>;
55
+ export type OneOffNavigationCommandCompileSuccess = CliCommandCompileBatchSuccessWithReports<NavigationSiteCommandType, NavigationCommandReportSubject>;
56
+ export declare function compileOneOffEntryCommand(input: OneOffCompileInput<OneOffEntryMutation>): Result<OneOffEntryCommandCompileSuccess, CliCommandValidationCompileError>;
57
+ export declare function compileOneOffPageCommand(input: OneOffCompileInput<OneOffPageMutation>): Result<OneOffPageCommandCompileSuccess, CliCommandValidationCompileError>;
58
+ export declare function compileOneOffBlockCommand(input: OneOffCompileInput<OneOffBlockMutation>): Result<OneOffPageCommandCompileSuccess, CliCommandValidationCompileError>;
59
+ export declare function compileOneOffNavigationCommand(input: OneOffCompileInput<OneOffNavigationMutation>): Result<OneOffNavigationCommandCompileSuccess, CliCommandValidationCompileError>;
60
+ export declare function executeOneOffEntryCommand<TResult>(input: Readonly<{
61
+ compiledPlan: Pick<OneOffEntryCommandCompileSuccess, 'plan'>;
62
+ client: Pick<ManagementClient, 'entries'>;
63
+ apply: (command: EntrySiteCommand, client: Pick<ManagementClient, 'entries'>) => Promise<TResult>;
64
+ }>): Promise<TResult>;
65
+ export declare function executeOneOffPageCommand<TResult>(input: Readonly<{
66
+ compiledPlan: Pick<OneOffPageCommandCompileSuccess, 'plan'>;
67
+ client: Pick<ManagementClient, 'pages' | 'blocks'>;
68
+ apply: (command: PageSiteCommand, client: Pick<ManagementClient, 'pages' | 'blocks'>) => Promise<TResult>;
69
+ }>): Promise<TResult>;
70
+ export declare function executeOneOffNavigationCommand<TResult>(input: Readonly<{
71
+ compiledPlan: Pick<OneOffNavigationCommandCompileSuccess, 'plan'>;
72
+ client: Pick<ManagementClient, 'navigation'>;
73
+ apply: (command: NavigationSiteCommand, client: Pick<ManagementClient, 'navigation'>) => Promise<TResult>;
74
+ }>): Promise<TResult>;
75
+ export declare function applyOneOffEntryCommand(command: EntrySiteCommand, client: Pick<ManagementClient, 'entries'>): Promise<ManagementEntry | void>;
76
+ export declare function applyOneOffEntryCommandReturningEntry(command: EntrySiteCommand, client: Pick<ManagementClient, 'entries'>): Promise<ManagementEntry>;
77
+ export declare function applyOneOffPageCommand(command: PageSiteCommand, client: Pick<ManagementClient, 'pages' | 'blocks'>): Promise<ManagementPage | ManagementBlock | void>;
78
+ export declare function applyOneOffPageCommandReturningPage(command: PageSiteCommand, client: Pick<ManagementClient, 'pages' | 'blocks'>): Promise<ManagementPage>;
79
+ export declare function applyOneOffPageCommandReturningBlock(command: PageSiteCommand, client: Pick<ManagementClient, 'pages' | 'blocks'>): Promise<ManagementBlock>;
80
+ export declare function applyOneOffNavigationCommand(command: NavigationSiteCommand, client: Pick<ManagementClient, 'navigation'>): Promise<ManagementNavigationMenu>;
81
+ export {};
@@ -27,4 +27,5 @@ export { Layout } from './rendering/components/Layout';
27
27
  export type { LayoutProps, HeaderData, FooterData } from './rendering/components/Layout';
28
28
  export { PageRenderer, ThemeScope, buildThemeRuntime, RichText, resolveImageUrl, resolveImageUrlWithContext, resolveImageSourceSetWithContext, ImagePresets, } from './rendering';
29
29
  export type { AppointmentBookingContent, BlogListingContent, BlogPostContent, BodyTextAlignment, BodyTextContent, CollectionContent, ColumnsContent, CourseDetailsContent, CourseRegistrationContent, CtaFullContent, EmbedContent, EventCalendarContent, EventDetailsContent, EventListingContent, EventRegistrationContent, EventSpotlightContent, FaqContent, FormBlockContent, GiftingContent, HeroContent, HeroMedia, ImageGalleryContent, MediaTextContent, NewsletterSignupContent, ShopContent, SingleButtonContent, SiteFooterContent, SiteHeaderContent, SystemBlockContentByKind, SystemBlockContentFor, TeamMembersContent, TestimonialEntryContent, TestimonialsBlockContent, VideoGridContent, ImageOptions, ResolveImageUrlContext, ResolveImageSourceSetOptions, ResolvedImageSourceSet, PageOutline, RouteMap, Theme, ThemeTokens, Media, TipTapNode, LinkValue, RichTextPrimitiveProps, } from './rendering';
30
- export type { BlockOverrideProps, BlockOverrides, BlockOverrideComponent, KnownSystemBlockOverrides, } from './rendering';
30
+ export type { BlockOverrideProps, BlockOverrideRegistration, BlockOverrideRegistrations, BlockOverrides, BlockOverrideComponent, KnownSystemBlockOverrides, LazyBlockOverrideFactory, LazyBlockOverrideRegistration, } from './rendering';
31
+ export { defineLazyBlockOverride } from './rendering';
@@ -8,7 +8,7 @@ import type { ReactNode } from 'react';
8
8
  import type { RiverbankClient } from '../client/types';
9
9
  import type { RiverbankSiteConfig } from '../config/types';
10
10
  import type { Theme } from '../contracts';
11
- import type { BlockOverrides } from '../rendering/overrides';
11
+ import type { BlockOverrideRegistrations } from '../rendering/overrides';
12
12
  import type { Metadata } from '../metadata/generatePageMetadata';
13
13
  import type { LoadContentResult } from '../rendering/helpers/loadContent';
14
14
  /**
@@ -182,8 +182,10 @@ export type CreateCatchAllPageOptions = {
182
182
  /**
183
183
  * Custom block component overrides for rendering.
184
184
  * Keys can be full block kind ("block.hero") or short form ("hero").
185
+ * Wrap client-heavy dynamic imports with defineLazyBlockOverride(); raw
186
+ * promise-returning functions are not lazy override registrations.
185
187
  */
186
- blockOverrides?: BlockOverrides;
188
+ blockOverrides?: BlockOverrideRegistrations;
187
189
  /**
188
190
  * Override site URL for metadata generation.
189
191
  * Defaults to `config.liveUrl` or `config.previewUrl` based on preview mode.
@@ -23,7 +23,8 @@ export { ThemeScope, } from './blocks-theme-scope';
23
23
  export { buildThemeRuntime, } from './blocks-theme-runtime';
24
24
  export type { ComponentRegistry, PageRendererDataContext, PageRendererProps, SdkConfigForRenderer, } from './blocks-page-renderer';
25
25
  export type { ThemeScopeProps, } from './blocks-theme-scope';
26
- export type { BlockOverrideProps, BlockOverrideComponent, BlockOverrides, KnownSystemBlockOverrides, } from './overrides';
26
+ export type { BlockOverrideProps, BlockOverrideComponent, BlockOverrideRegistration, BlockOverrideRegistrations, BlockOverrides, KnownSystemBlockOverrides, LazyBlockOverrideFactory, LazyBlockOverrideRegistration, } from './overrides';
27
+ export { defineLazyBlockOverride } from './overrides';
27
28
  /**
28
29
  * @example Server-side rendering (Next.js App Router)
29
30
  * ```tsx
@@ -0,0 +1,10 @@
1
+ import type { PageOutline } from '../contracts';
2
+ import { type BlockOverrideRegistrations, type BlockOverrides } from './overrides';
3
+ /**
4
+ * Resolve only override registrations used by the current page or template.
5
+ *
6
+ * Eager components are passed through unchanged. Lazy registrations created
7
+ * with `defineLazyBlockOverride()` are loaded only after their block kind is
8
+ * found in the rendered block tree.
9
+ */
10
+ export declare function resolveBlockOverridesForPage(registrations: BlockOverrideRegistrations | undefined, page: PageOutline | null | undefined): Promise<BlockOverrides | undefined>;
@@ -25,8 +25,20 @@ type BivariantOverrideComponent<TContent = Record<string, unknown>> = {
25
25
  bivarianceHack(props: BlockOverrideProps<TContent, Record<string, unknown>>): React.ReactElement | null;
26
26
  }['bivarianceHack'];
27
27
  type CompatibleBlockOverrideComponent<TContent = Record<string, unknown>> = BlockOverrideComponent<TContent, Record<string, unknown>> | BivariantOverrideComponent<TContent>;
28
+ export declare const lazyBlockOverrideSymbol: unique symbol;
29
+ export type LazyBlockOverrideFactory<TContent = Record<string, unknown>> = () => Promise<BivariantOverrideComponent<TContent>>;
30
+ export type LazyBlockOverrideRegistration<TContent = Record<string, unknown>> = {
31
+ readonly [lazyBlockOverrideSymbol]: true;
32
+ readonly load: LazyBlockOverrideFactory<TContent>;
33
+ };
34
+ export type BlockOverrideRegistration<TContent = Record<string, unknown>> = CompatibleBlockOverrideComponent<TContent> | LazyBlockOverrideRegistration<TContent>;
35
+ export declare function defineLazyBlockOverride<TContent = Record<string, unknown>>(load: LazyBlockOverrideFactory<TContent>): LazyBlockOverrideRegistration<TContent>;
28
36
  export type KnownSystemBlockOverrides = {
29
37
  [K in keyof SystemBlockContentByKind]?: CompatibleBlockOverrideComponent<SystemBlockContentByKind[K]>;
30
38
  };
31
39
  export type BlockOverrides = KnownSystemBlockOverrides & Record<string, CompatibleBlockOverrideComponent<Record<string, unknown>>>;
40
+ export type KnownSystemBlockOverrideRegistrations = {
41
+ [K in keyof SystemBlockContentByKind]?: BlockOverrideRegistration<SystemBlockContentByKind[K]>;
42
+ };
43
+ export type BlockOverrideRegistrations = KnownSystemBlockOverrideRegistrations & Record<string, BlockOverrideRegistration<Record<string, unknown>>>;
32
44
  export {};
@@ -8,4 +8,4 @@
8
8
  * 1. This constant
9
9
  * 2. The "version" field in package.json
10
10
  */
11
- export declare const SDK_VERSION = "0.70.2";
11
+ export declare const SDK_VERSION = "0.70.3";