@riverbankcms/sdk 0.95.0 → 0.96.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 (149) hide show
  1. package/README.md +27 -4
  2. package/dist/{PreviewEditorSidebar-6FQYKOSB.mjs → PreviewEditorSidebar-46VT4QVU.mjs} +2 -2
  3. package/dist/{PreviewEditorUI-NPO2XMFB.mjs → PreviewEditorUI-JD75GE5F.mjs} +2 -2
  4. package/dist/SdkPreviewModeRuntime-MJUSYUFB.mjs +8 -0
  5. package/dist/_dts/ai/src/contracts/proposals.d.ts +10 -10
  6. package/dist/_dts/block-form/src/client/index.d.ts +1 -1
  7. package/dist/_dts/block-form/src/fields/types.d.ts +9 -1
  8. package/dist/_dts/block-form/src/registry/schemas.d.ts +4 -0
  9. package/dist/_dts/block-form/src/registry/widgets.d.ts +1 -0
  10. package/dist/_dts/block-form/src/widgets/InlineTextField/InlineTextField.d.ts +4 -0
  11. package/dist/_dts/block-form/src/widgets/InlineTextField/index.d.ts +3 -0
  12. package/dist/_dts/block-form/src/widgets/InlineTextField/inlineTextFieldDefaults.d.ts +2 -0
  13. package/dist/_dts/block-form/src/widgets/InlineTextField/inlineTextFieldSchema.d.ts +3 -0
  14. package/dist/_dts/blocks/src/bindings/index.d.ts +1 -0
  15. package/dist/_dts/blocks/src/bindings/types.d.ts +1 -0
  16. package/dist/_dts/blocks/src/index.d.ts +2 -0
  17. package/dist/_dts/blocks/src/system/blocks/appointment-booking.d.ts +2 -29631
  18. package/dist/_dts/blocks/src/system/blocks/blog-listing.d.ts +2 -29614
  19. package/dist/_dts/blocks/src/system/blocks/cart.d.ts +2 -29634
  20. package/dist/_dts/blocks/src/system/blocks/checkout.d.ts +2 -29627
  21. package/dist/_dts/blocks/src/system/blocks/collection.d.ts +2 -29585
  22. package/dist/_dts/blocks/src/system/blocks/faq.d.ts +2 -29677
  23. package/dist/_dts/blocks/src/system/blocks/gifting.d.ts +2 -29655
  24. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +220 -0
  25. package/dist/_dts/blocks/src/system/blocks/product-detail.d.ts +2 -29655
  26. package/dist/_dts/blocks/src/system/blocks/product-list.d.ts +2 -29685
  27. package/dist/_dts/blocks/src/system/blocks/shop.d.ts +2 -29761
  28. package/dist/_dts/blocks/src/system/fragments/library/heroCopy.d.ts +9 -0
  29. package/dist/_dts/blocks/src/system/inlineText.d.ts +78 -0
  30. package/dist/_dts/blocks/src/system/manifest/derivedContent.d.ts +3 -1
  31. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +15 -3
  32. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +188 -7
  33. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +2 -1
  34. package/dist/_dts/blocks/src/system/runtime/nodes/renderInlineText.d.ts +3 -0
  35. package/dist/_dts/content-editor/src/index.d.ts +1 -0
  36. package/dist/_dts/content-editor/src/primitives/PreviewInlineTextEditor.d.ts +9 -0
  37. package/dist/_dts/content-model/src/templates/bindingTypeCompatibility.d.ts +1 -1
  38. package/dist/_dts/content-model/src/templates/defaults.d.ts +30 -0
  39. package/dist/_dts/content-model/src/templates/types.d.ts +1 -0
  40. package/dist/_dts/editor/src/inline/FloatingToolbar.d.ts +18 -0
  41. package/dist/_dts/editor/src/inline/InlineTextEditor.d.ts +18 -1
  42. package/dist/_dts/editor/src/inline/InlineTextEditorStyles.d.ts +1 -0
  43. package/dist/_dts/editor/src/inline/domParse.d.ts +3 -0
  44. package/dist/_dts/editor/src/inline/presentation.d.ts +48 -0
  45. package/dist/_dts/editor/src/inline/selection.d.ts +12 -0
  46. package/dist/_dts/editor-blocks/src/widgets/index.d.ts +1 -0
  47. package/dist/_dts/editor-blocks/src/widgets/inline-text/InlineTextField.d.ts +3 -0
  48. package/dist/_dts/editor-blocks/src/widgets/inline-text/index.d.ts +1 -0
  49. package/dist/_dts/path-utils/src/index.d.ts +1 -0
  50. package/dist/_dts/preview-next/src/client/widgets/widgetOverrides.d.ts +1 -0
  51. package/dist/_dts/sdk/src/cli/commands/content.d.ts +56 -0
  52. package/dist/_dts/sdk/src/cli/commands/push/options.d.ts +4 -0
  53. package/dist/_dts/sdk/src/cli/commands/push/scopes/commandBackedDatedOfferingScope.d.ts +2 -6
  54. package/dist/_dts/sdk/src/cli/commands/push/scopes/types.d.ts +2 -0
  55. package/dist/_dts/sdk/src/cli/commands/push/verification.d.ts +56 -0
  56. package/dist/_dts/sdk/src/cli/commands/push-execute.d.ts +2 -1
  57. package/dist/_dts/sdk/src/cli/content/edit/format.d.ts +2 -0
  58. package/dist/_dts/sdk/src/cli/content/edit/path.d.ts +14 -0
  59. package/dist/_dts/sdk/src/cli/content/edit/planner.d.ts +46 -0
  60. package/dist/_dts/sdk/src/cli/content/fs-utils.d.ts +4 -0
  61. package/dist/_dts/sdk/src/cli/content/writer.d.ts +2 -0
  62. package/dist/_dts/sdk/src/cli/merge-remote/mergeFieldChanges.d.ts +22 -0
  63. package/dist/_dts/sdk/src/cli/merge-remote/pageLocal.d.ts +3 -0
  64. package/dist/_dts/sdk/src/cli/merge-remote/pageMerge.d.ts +78 -0
  65. package/dist/_dts/sdk/src/cli/merge-remote/pageMergePlan.d.ts +36 -0
  66. package/dist/_dts/sdk/src/cli/merge-remote/pageMergePush.d.ts +21 -0
  67. package/dist/_dts/sdk/src/cli/merge-remote/pageMergeReport.d.ts +29 -0
  68. package/dist/_dts/sdk/src/cli/merge-remote/pageSnapshots.d.ts +48 -0
  69. package/dist/_dts/sdk/src/cli/sync/diff.d.ts +3 -6
  70. package/dist/_dts/sdk/src/cli/sync/field-diff.d.ts +39 -0
  71. package/dist/_dts/sdk/src/cli/sync/index.d.ts +1 -0
  72. package/dist/_dts/sdk/src/config/field-definitions.d.ts +8 -3
  73. package/dist/_dts/sdk/src/config/typed-entries.d.ts +5 -3
  74. package/dist/_dts/sdk/src/contracts/bindings.d.ts +1 -0
  75. package/dist/_dts/sdk/src/contracts/content.d.ts +18 -0
  76. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  77. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +3 -2
  78. package/dist/_dts/sdk/src/rendering/helpers/bindings.d.ts +1 -0
  79. package/dist/_dts/sdk/src/version.d.ts +1 -1
  80. package/dist/_dts/theme-core/src/accordions/types.d.ts +1 -1
  81. package/dist/_dts/theme-core/src/buttons/types.d.ts +1 -1
  82. package/dist/_dts/theme-core/src/cards/types.d.ts +15 -15
  83. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  84. package/dist/_dts/theme-core/src/inputs/types.d.ts +1 -1
  85. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +42 -42
  86. package/dist/_dts/theme-core/src/palette/variants/types.d.ts +2 -2
  87. package/dist/_dts/theme-core/src/schema.d.ts +44 -44
  88. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  89. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +16 -16
  90. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +4 -4
  91. package/dist/_dts/theme-core/src/site-styles/tokenRecipes.d.ts +1 -1
  92. package/dist/_dts/theme-core/src/tokens/themeColorTokens.d.ts +1 -1
  93. package/dist/{chunk-K2SJTZMX.mjs → chunk-4TLTV7OB.mjs} +1 -1
  94. package/dist/{chunk-4A6NWO3X.mjs → chunk-EL55VV6Q.mjs} +47 -7
  95. package/dist/{chunk-PJPFTVZG.mjs → chunk-VHIMXQ7Z.mjs} +105 -3
  96. package/dist/{chunk-VZBSD4AJ.mjs → chunk-XMATDBXO.mjs} +1633 -225
  97. package/dist/cli/index.mjs +2691 -772
  98. package/dist/cli/init-docs/content/cli-reference.md +43 -4
  99. package/dist/cli/init-docs/content/content-management.md +22 -0
  100. package/dist/client/{accordion-LGO4IO5U.mjs → accordion-GUNXIIZR.mjs} +1 -1
  101. package/dist/client/{bookingForm-MWLS4LGU.mjs → bookingForm-AOU4VHP5.mjs} +3 -3
  102. package/dist/client/{carousel-6AHNP5HA.mjs → carousel-4QSX2JPC.mjs} +1 -1
  103. package/dist/client/{cart-CIXGYAZB.mjs → cart-JKN6C4ZB.mjs} +4 -4
  104. package/dist/client/{checkout-OTX6O3DN.mjs → checkout-YY6YMITW.mjs} +4 -4
  105. package/dist/client/{chunk-Y2PMKCXO.mjs → chunk-6H27K6AA.mjs} +1 -1
  106. package/dist/client/{chunk-R3NQTBWE.mjs → chunk-A3YN7M3G.mjs} +1 -1
  107. package/dist/client/{chunk-TREIML53.mjs → chunk-BFCJZODD.mjs} +3 -3
  108. package/dist/client/{chunk-INRIT6DP.mjs → chunk-CPKMJVBD.mjs} +5 -5
  109. package/dist/client/{chunk-V67AT7ZW.mjs → chunk-GWCRCJLB.mjs} +6 -0
  110. package/dist/client/{chunk-AVBQSUIS.mjs → chunk-H7A6PUGM.mjs} +1 -1
  111. package/dist/client/{chunk-HOP7MKAZ.mjs → chunk-SY543WZ3.mjs} +1 -1
  112. package/dist/client/{chunk-MYCXAKWP.mjs → chunk-VJROUYOQ.mjs} +2 -2
  113. package/dist/client/client.mjs +4007 -3687
  114. package/dist/client/{courseRegistration-2LU6C3JL.mjs → courseRegistration-UU2KMPSQ.mjs} +3 -3
  115. package/dist/client/{eventCalendar-OKUSISBZ.mjs → eventCalendar-WIVNS2TM.mjs} +6 -6
  116. package/dist/client/{eventCombined-CMF6CD4S.mjs → eventCombined-P3G5MW4J.mjs} +6 -6
  117. package/dist/client/{eventDetails-XYWH7WXI.mjs → eventDetails-2VHJ2RDG.mjs} +6 -6
  118. package/dist/client/{eventListing-EK5DJTAH.mjs → eventListing-TGO6JI3R.mjs} +6 -6
  119. package/dist/client/{eventRegistration-XDOSICV6.mjs → eventRegistration-B46HEJES.mjs} +6 -6
  120. package/dist/client/{form-J6GOHKYG.mjs → form-VKRTFUNQ.mjs} +1 -1
  121. package/dist/client/{headerSection-C5SYJTRW.mjs → headerSection-XUX4ZJLO.mjs} +2 -2
  122. package/dist/client/hooks.mjs +20 -3
  123. package/dist/client/{locationMap-QSQIFVVT.mjs → locationMap-NQFI5MJO.mjs} +2 -2
  124. package/dist/client/{newsletterForm-47VXJRWK.mjs → newsletterForm-P2GETCQK.mjs} +1 -1
  125. package/dist/client/{productDetail-5U6ISCC2.mjs → productDetail-7NQ7O5IF.mjs} +4 -4
  126. package/dist/client/{productList-XDZUMR3P.mjs → productList-RX6ZJ7BL.mjs} +4 -4
  127. package/dist/client/rendering/client.mjs +3822 -3600
  128. package/dist/client/rendering/islands.mjs +18 -18
  129. package/dist/client/rendering.mjs +1211 -883
  130. package/dist/client/{shop-ZSZKIY3R.mjs → shop-BHN5AKY6.mjs} +4 -4
  131. package/dist/preview-next/client/runtime.mjs +3 -3
  132. package/dist/{sdk-runtime-PHNM7MRD.mjs → sdk-runtime-X2LXQ5IC.mjs} +4 -4
  133. package/dist/server/components.mjs +3144 -2827
  134. package/dist/server/config-template-validation.mjs +29 -20
  135. package/dist/server/config-validation.mjs +22 -4
  136. package/dist/server/config.mjs +22 -4
  137. package/dist/server/data.mjs +20 -3
  138. package/dist/server/index.mjs +1 -1
  139. package/dist/server/next.mjs +3181 -2858
  140. package/dist/server/page-converter.mjs +20 -3
  141. package/dist/server/prebuild.mjs +1 -1
  142. package/dist/server/rendering/server.mjs +3153 -2837
  143. package/dist/server/rendering.mjs +3168 -2844
  144. package/dist/server/routing.mjs +21 -4
  145. package/dist/server/server.mjs +2299 -2188
  146. package/dist/server/theme-bridge.mjs +43 -0
  147. package/dist/styles/index.css +44 -0
  148. package/package.json +1 -1
  149. package/dist/SdkPreviewModeRuntime-PBEFH5AL.mjs +0 -8
@@ -0,0 +1,48 @@
1
+ import type { CSSProperties } from 'react';
2
+ import { type InlineTextAccentTone, type InlineTextHighlightTone } from '../../../blocks/src/index';
3
+ import type { InlineTextEditorTone } from './FloatingToolbar';
4
+ type RgbaColor = Readonly<{
5
+ r: number;
6
+ g: number;
7
+ b: number;
8
+ a: number;
9
+ }>;
10
+ export type InlineTextTypographySnapshot = Pick<CSSProperties, 'fontFamily' | 'fontSize' | 'fontStyle' | 'fontWeight' | 'letterSpacing' | 'lineHeight' | 'textTransform'>;
11
+ export type InlineTextSurface = Readonly<{
12
+ kind: 'block-solid';
13
+ color: string;
14
+ rgba: RgbaColor;
15
+ }> | Readonly<{
16
+ kind: 'image-overlay';
17
+ color: string;
18
+ rgba: RgbaColor;
19
+ }> | Readonly<{
20
+ kind: 'theme-fallback';
21
+ color: string;
22
+ rgba: RgbaColor;
23
+ }> | Readonly<{
24
+ kind: 'toolbar-fallback';
25
+ color: string;
26
+ rgba: RgbaColor;
27
+ }>;
28
+ export type InlineTextPresentationContext = Readonly<{
29
+ surface: InlineTextSurface;
30
+ selectedTextColor: string;
31
+ selectedTextRgba: RgbaColor;
32
+ accentTextRgbaByTone: Readonly<Record<InlineTextAccentTone, RgbaColor | null>>;
33
+ typography: InlineTextTypographySnapshot;
34
+ }>;
35
+ export type InlineTextTonePreview = Readonly<{
36
+ surfaceStyle: CSSProperties;
37
+ textStyle: CSSProperties;
38
+ surfaceRgba: RgbaColor;
39
+ textRgba: RgbaColor;
40
+ lowContrast: boolean;
41
+ }>;
42
+ export declare function defaultInlineTextPresentationContext(tone: InlineTextEditorTone): InlineTextPresentationContext;
43
+ export declare function readInlineTextPresentationContext(editable: HTMLElement, toolbarTone: InlineTextEditorTone): InlineTextPresentationContext;
44
+ export declare function buildAccentTonePreview(tone: InlineTextAccentTone, context: InlineTextPresentationContext): InlineTextTonePreview;
45
+ export declare function buildHighlightTonePreview(tone: InlineTextHighlightTone, context: InlineTextPresentationContext): InlineTextTonePreview;
46
+ export declare function buildPlainTonePreview(context: InlineTextPresentationContext): InlineTextTonePreview;
47
+ export declare function inlineTextTonePreviewLooksSame(left: InlineTextTonePreview, right: InlineTextTonePreview): boolean;
48
+ export {};
@@ -0,0 +1,12 @@
1
+ import { type InlineText, type InlineTextMark } from '../../../blocks/src/index';
2
+ export type InlineTextSelection = Readonly<{
3
+ start: number;
4
+ end: number;
5
+ rect: DOMRect;
6
+ }>;
7
+ export declare function readSelection(root: HTMLElement | null): InlineTextSelection | null;
8
+ export declare function readSelectionRange(root: HTMLElement | null): InlineTextSelection | null;
9
+ export declare function rangeHasMark(value: InlineText, range: Readonly<{
10
+ start: number;
11
+ end: number;
12
+ }>, kind: InlineTextMark['kind']): boolean;
@@ -2,6 +2,7 @@ export * from './button/index';
2
2
  export * from './content-type-select/index';
3
3
  export * from './entry-picker/index';
4
4
  export * from './icon-picker/index';
5
+ export * from './inline-text/index';
5
6
  export * from './link/index';
6
7
  export * from './offerings-preview/index';
7
8
  export * from './reference/index';
@@ -0,0 +1,3 @@
1
+ import type { FieldWidgetProps, InlineTextFieldConfig } from '../../../../block-form/src/client/index';
2
+ import { type InlineText } from '../../../../blocks/src/index';
3
+ export declare function InlineTextField({ path, backend, config, }: FieldWidgetProps<InlineText, InlineTextFieldConfig>): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export { InlineTextField } from './InlineTextField';
@@ -1,6 +1,7 @@
1
1
  export type PathSegment = string | number;
2
2
  export declare function parsePath(path: string | Array<PathSegment>): Array<PathSegment>;
3
3
  export declare function segmentsToPath(segments: Array<PathSegment>): string | undefined;
4
+ export declare function siblingPath(path: string, siblingFieldPath: string): string;
4
5
  export declare function getAtPath<T = unknown>(source: unknown, path: string | Array<PathSegment>): T | undefined;
5
6
  export declare function setAtPath(target: Record<string, unknown> | Array<unknown>, path: string | Array<PathSegment>, value: unknown): void;
6
7
  export declare function toAbsolutePath(base: string | undefined | null, path: string): string;
@@ -20,6 +20,7 @@ export declare const previewWidgetOverrides: {
20
20
  readonly file: FieldWidgetComponent;
21
21
  readonly media: FieldWidgetComponent;
22
22
  readonly richText: FieldWidgetComponent;
23
+ readonly inlineText: FieldWidgetComponent;
23
24
  readonly reference: FieldWidgetComponent;
24
25
  readonly backgroundColor: FieldWidgetComponent;
25
26
  readonly backgroundGradient: FieldWidgetComponent;
@@ -0,0 +1,56 @@
1
+ import { Command } from 'commander';
2
+ import { type ContentEditOperation, type ContentEditPlan, type PageBlockMoveTarget } from '../content/edit/planner';
3
+ import { type OutputContext } from '../output';
4
+ type CommonEditOptions = Readonly<{
5
+ contentDir?: string;
6
+ write?: boolean;
7
+ }>;
8
+ type FieldSetOptions = CommonEditOptions & Readonly<{
9
+ value?: string;
10
+ jsonValue?: string;
11
+ }>;
12
+ type MoveBlockOptions = CommonEditOptions & Readonly<{
13
+ before?: string;
14
+ after?: string;
15
+ toStart?: boolean;
16
+ toEnd?: boolean;
17
+ }>;
18
+ export type RunContentEditErrorCode = 'missing_file' | 'invalid_json' | 'read_failed';
19
+ export type RunContentEditError = Readonly<{
20
+ code: RunContentEditErrorCode;
21
+ message: string;
22
+ }>;
23
+ export type RunContentEditResult = Readonly<{
24
+ success: true;
25
+ plan: ContentEditPlan;
26
+ filePath: string;
27
+ written: boolean;
28
+ }> | Readonly<{
29
+ success: false;
30
+ plan?: ContentEditPlan;
31
+ filePath: string;
32
+ written: false;
33
+ error?: RunContentEditError;
34
+ }>;
35
+ export declare function runPageContentEdit(input: Readonly<{
36
+ operation: ContentEditOperation;
37
+ contentDir?: string;
38
+ write: boolean;
39
+ output: OutputContext;
40
+ }>): Promise<RunContentEditResult>;
41
+ export declare function parseContentEditValue(options: FieldSetOptions): Readonly<{
42
+ ok: true;
43
+ value: unknown;
44
+ } | {
45
+ ok: false;
46
+ error: string;
47
+ }>;
48
+ export declare function parseMoveTarget(options: MoveBlockOptions): Readonly<{
49
+ ok: true;
50
+ target: PageBlockMoveTarget;
51
+ } | {
52
+ ok: false;
53
+ error: string;
54
+ }>;
55
+ export declare const contentCommand: Command;
56
+ export {};
@@ -22,4 +22,8 @@ export interface PushOptions {
22
22
  meta?: boolean;
23
23
  /** Show human-readable field-level diff (requires --dry-run) */
24
24
  diff?: boolean;
25
+ /** Re-fetch after an applied push and fail if changes remain */
26
+ verifyNoop?: boolean;
27
+ /** Continue event scoped pushes when local media identifiers are missing */
28
+ allowMissingMedia?: boolean;
25
29
  }
@@ -1,5 +1,5 @@
1
1
  import type { SiteId } from '../../../../../../core/src/index';
2
- import { type JsonDiffMode } from '../../../sync';
2
+ import { type FieldChange, type JsonDiffMode } from '../../../sync';
3
3
  import { type CliDatedOfferingCommandBatchSource, type CliDatedOfferingCommandCompileInput, type DatedOfferingReportSubject, type DatedOfferingUpsertItem } from '../../../site-commands';
4
4
  import type { WriterOptions } from '../../../content/writer';
5
5
  import type { PushScopeResult, ScopePushContext } from './types';
@@ -38,11 +38,7 @@ export type DatedOfferingDryRunItem = Readonly<{
38
38
  identifier: string;
39
39
  label: string;
40
40
  status: DatedOfferingDryRunItemStatus;
41
- changes: readonly Readonly<{
42
- path: string;
43
- before: unknown;
44
- after: unknown;
45
- }>[];
41
+ changes: readonly FieldChange[];
46
42
  }>;
47
43
  export type DatedOfferingDryRunSummary = Readonly<{
48
44
  created: number;
@@ -5,6 +5,7 @@ import type { SiteId } from '../../../../../../core/src/index';
5
5
  import type { EnvTarget } from '../../../helpers';
6
6
  import type { JsonDiffMode } from '../../../sync';
7
7
  import type { LocalMediaPushReport } from '../../../media/local-sync';
8
+ export type PushMediaPolicy = 'require' | 'allow-missing';
8
9
  export interface ScopePushContext {
9
10
  output: OutputContext;
10
11
  contentDir: string;
@@ -21,6 +22,7 @@ export interface ScopePushContext {
21
22
  autoPullStale: boolean;
22
23
  skipMeta: boolean;
23
24
  humanDiff: boolean;
25
+ mediaPolicy: PushMediaPolicy;
24
26
  }
25
27
  export type PushScopeResultStatus = 'noop' | 'dry-run' | 'aborted' | 'failed' | 'pushed';
26
28
  export interface PushScopeItemError {
@@ -0,0 +1,56 @@
1
+ import type { EnvTarget } from '../../helpers';
2
+ import type { OutputContext } from '../../output';
3
+ import type { PushOptions } from './options';
4
+ import { type PushScope } from './consts';
5
+ export type PushVerificationStatus = 'clean' | 'dirty' | 'fetch_failed' | 'unsupported_scope';
6
+ export type PushVerificationDirtyScope = Readonly<{
7
+ scope: string;
8
+ summary: Readonly<Record<string, number>>;
9
+ }>;
10
+ export type PushVerificationResult = Readonly<{
11
+ status: 'clean';
12
+ target: EnvTarget;
13
+ }> | Readonly<{
14
+ status: 'dirty';
15
+ target: EnvTarget;
16
+ dirtyScopes: readonly PushVerificationDirtyScope[];
17
+ }> | Readonly<{
18
+ status: 'fetch_failed';
19
+ target: EnvTarget;
20
+ error: string;
21
+ }> | Readonly<{
22
+ status: 'unsupported_scope';
23
+ target: EnvTarget;
24
+ scope: string;
25
+ }>;
26
+ export type PushVerificationInput = Readonly<{
27
+ target: EnvTarget;
28
+ scope: string | undefined;
29
+ type: string | undefined;
30
+ identifier: string | undefined;
31
+ options: PushOptions;
32
+ }>;
33
+ export type PushVerificationDeps = Readonly<{
34
+ verifyGeneric: (input: PushVerificationInput & {
35
+ pushScope: PushScope;
36
+ }) => Promise<readonly PushVerificationDirtyScope[]>;
37
+ verifyDatedOffering: (input: PushVerificationInput & {
38
+ pushScope: 'venues' | 'event-categories' | 'events' | 'courses';
39
+ }) => Promise<readonly PushVerificationDirtyScope[]>;
40
+ }>;
41
+ export declare function collectDatedOfferingVerificationOrphans<TLocal, TRemote>(input: Readonly<{
42
+ deleteOrphaned: boolean;
43
+ hasScopedSelector: boolean;
44
+ allLocalItems: readonly TLocal[];
45
+ remoteItems: readonly TRemote[];
46
+ getLocalIdentifier: (item: TLocal) => string;
47
+ getRemoteIdentifier: (item: TRemote) => string;
48
+ }>): readonly string[];
49
+ export declare function verifyGenericPushNoop(input: PushVerificationInput & {
50
+ pushScope: PushScope;
51
+ }): Promise<readonly PushVerificationDirtyScope[]>;
52
+ export declare function createProductionPushVerificationDeps(): PushVerificationDeps;
53
+ export declare function verifyPushNoopForEnvWithDeps(deps: PushVerificationDeps, input: PushVerificationInput): Promise<PushVerificationResult>;
54
+ export declare function verifyPushNoopForEnv(input: PushVerificationInput): Promise<PushVerificationResult>;
55
+ export declare function isPushVerificationClean(result: PushVerificationResult): boolean;
56
+ export declare function emitPushVerificationResult(output: OutputContext, result: PushVerificationResult, jsonOutput: boolean): void;
@@ -28,7 +28,8 @@ type MergeRemoteValidationError = Readonly<{
28
28
  suggestion: string;
29
29
  }>;
30
30
  }>;
31
- export declare function validateMergeRemoteEntryPushOptions(input: MergeRemoteValidationInput): MergeRemoteValidationError | null;
31
+ export declare function validateMergeRemotePushOptions(input: MergeRemoteValidationInput): MergeRemoteValidationError | null;
32
+ export declare const validateMergeRemoteEntryPushOptions: typeof validateMergeRemotePushOptions;
32
33
  /**
33
34
  * Execute push for a single environment.
34
35
  * Extracted to support --env=both multi-environment operations.
@@ -0,0 +1,2 @@
1
+ import type { ContentEditPlan } from './planner';
2
+ export declare function formatContentEditPlan(plan: ContentEditPlan): readonly string[];
@@ -0,0 +1,14 @@
1
+ import { type FieldPathSegment } from '../../sync/field-diff';
2
+ export type ContentFieldPathSegment = Extract<FieldPathSegment, Readonly<{
3
+ type: 'property' | 'index';
4
+ }>>;
5
+ export type ContentFieldPath = readonly ContentFieldPathSegment[];
6
+ export type ParseContentFieldPathResult = Readonly<{
7
+ ok: true;
8
+ path: ContentFieldPath;
9
+ }> | Readonly<{
10
+ ok: false;
11
+ error: string;
12
+ }>;
13
+ export declare function parseContentFieldPath(value: string): ParseContentFieldPathResult;
14
+ export declare function formatContentFieldPath(path: ContentFieldPath): string;
@@ -0,0 +1,46 @@
1
+ import type { LocalPage } from '../reader';
2
+ import { type FieldChange } from '../../sync/field-diff';
3
+ import { type ContentFieldPath } from './path';
4
+ export type PageBlockMoveTarget = Readonly<{
5
+ tag: 'to_start';
6
+ }> | Readonly<{
7
+ tag: 'to_end';
8
+ }> | Readonly<{
9
+ tag: 'before';
10
+ blockIdentifier: string;
11
+ }> | Readonly<{
12
+ tag: 'after';
13
+ blockIdentifier: string;
14
+ }>;
15
+ export type ContentEditOperation = Readonly<{
16
+ tag: 'remove_page_block';
17
+ pageIdentifier: string;
18
+ blockIdentifier: string;
19
+ }> | Readonly<{
20
+ tag: 'move_page_block';
21
+ pageIdentifier: string;
22
+ blockIdentifier: string;
23
+ target: PageBlockMoveTarget;
24
+ }> | Readonly<{
25
+ tag: 'update_page_field';
26
+ pageIdentifier: string;
27
+ path: ContentFieldPath;
28
+ value: unknown;
29
+ }>;
30
+ export type ContentEditErrorCode = 'wrong_page' | 'missing_blocks' | 'missing_block' | 'duplicate_block_identifier' | 'missing_move_target' | 'move_target_is_source' | 'invalid_field_path' | 'noop';
31
+ export type ContentEditError = Readonly<{
32
+ code: ContentEditErrorCode;
33
+ message: string;
34
+ }>;
35
+ export type ContentEditPlan = Readonly<{
36
+ tag: 'will_change';
37
+ operation: ContentEditOperation;
38
+ before: LocalPage;
39
+ after: LocalPage;
40
+ changes: readonly FieldChange[];
41
+ }> | Readonly<{
42
+ tag: 'invalid';
43
+ operation: ContentEditOperation;
44
+ error: ContentEditError;
45
+ }>;
46
+ export declare function planPageContentEdit(page: LocalPage, operation: ContentEditOperation): ContentEditPlan;
@@ -29,6 +29,10 @@ export declare function readJsonFile<T>(filePath: string): Promise<T>;
29
29
  * Write JSON file with pretty formatting
30
30
  */
31
31
  export declare function writeJsonFile(filePath: string, data: unknown): Promise<void>;
32
+ /**
33
+ * Write JSON file via a same-directory temp file and atomic rename.
34
+ */
35
+ export declare function writeJsonFileAtomic(filePath: string, data: unknown): Promise<void>;
32
36
  /**
33
37
  * Ensure directory exists, creating it if necessary
34
38
  */
@@ -7,10 +7,12 @@ import type { PulledEntries, PulledPages, PulledNavigation, PulledSettings, Pull
7
7
  import type { Theme } from '../../contracts';
8
8
  import { type PulledEntryOutput } from '../helpers';
9
9
  import type { EntryBaseSnapshotTarget } from '../merge-remote/entrySnapshots';
10
+ import type { PageBaseSnapshotTarget } from '../merge-remote/pageSnapshots';
10
11
  export interface WriterOptions {
11
12
  skipMeta?: boolean;
12
13
  cleanupMissing?: boolean;
13
14
  entryBaseSnapshot?: EntryBaseSnapshotTarget;
15
+ pageBaseSnapshot?: PageBaseSnapshotTarget;
14
16
  }
15
17
  type WritablePulledEntries = Omit<PulledEntries, 'entries'> & {
16
18
  entries: PulledEntryOutput[];
@@ -0,0 +1,22 @@
1
+ export type ScalarMergeChangeValue = string | number | boolean | null | undefined;
2
+ export type MergeChange = Readonly<{
3
+ path: string;
4
+ }> & (Readonly<{
5
+ value: ScalarMergeChangeValue;
6
+ containerKind: 'scalar' | 'null';
7
+ }> | Readonly<{
8
+ value: Record<string, unknown>;
9
+ containerKind: 'object';
10
+ }> | Readonly<{
11
+ value: readonly unknown[];
12
+ containerKind: 'array';
13
+ }>);
14
+ export type ScalarMergeChange = Extract<MergeChange, {
15
+ containerKind: 'scalar' | 'null';
16
+ }>;
17
+ export declare function collectMergeChanges(currentPath: string, base: unknown, next: unknown, changes: Map<string, MergeChange>): void;
18
+ export declare function firstNestedContainerChange<TChange extends MergeChange>(localChanges: ReadonlyMap<string, TChange>, remoteChanges: ReadonlyMap<string, TChange>): TChange | null;
19
+ export declare function firstSameFieldConflictPath(localChanges: ReadonlyMap<string, MergeChange>, remoteChanges: ReadonlyMap<string, MergeChange>): string | null;
20
+ export declare function scalarMergeChanges<TChange extends MergeChange>(changes: ReadonlyMap<string, TChange>, label: string): readonly Extract<TChange, {
21
+ containerKind: 'scalar' | 'null';
22
+ }>[];
@@ -0,0 +1,3 @@
1
+ import type { PulledPages } from '../../client/management/types';
2
+ import type { LocalPage } from '../content/reader';
3
+ export declare function pulledPageToLocalPage(page: PulledPages['pages'][number]): LocalPage;
@@ -0,0 +1,78 @@
1
+ import type { LocalPage } from '../content/reader';
2
+ export type PageMergeSubject = Readonly<{
3
+ identifier: string;
4
+ }>;
5
+ export type PageMergeInputs = Readonly<{
6
+ subject: PageMergeSubject;
7
+ base: LocalPage | null;
8
+ local: LocalPage | null;
9
+ remote: LocalPage | null;
10
+ }>;
11
+ export type PageMergeConflict = Readonly<{
12
+ code: 'missing_base';
13
+ subject: PageMergeSubject;
14
+ message: string;
15
+ }> | Readonly<{
16
+ code: 'same_field_changed';
17
+ subject: PageMergeSubject;
18
+ fieldPath: string;
19
+ message: string;
20
+ }> | Readonly<{
21
+ code: 'local_delete_unsupported';
22
+ subject: PageMergeSubject;
23
+ message: string;
24
+ }> | Readonly<{
25
+ code: 'remote_delete_local_edit';
26
+ subject: PageMergeSubject;
27
+ message: string;
28
+ }> | Readonly<{
29
+ code: 'unsupported_nested_merge';
30
+ subject: PageMergeSubject;
31
+ fieldPath: string;
32
+ message: string;
33
+ }>;
34
+ export type PageMergeDecision = Readonly<{
35
+ kind: 'unchanged';
36
+ subject: PageMergeSubject;
37
+ desired: LocalPage | null;
38
+ }> | Readonly<{
39
+ kind: 'bootstrap_base';
40
+ subject: PageMergeSubject;
41
+ desired: LocalPage;
42
+ }> | Readonly<{
43
+ kind: 'preserve_remote';
44
+ subject: PageMergeSubject;
45
+ desired: LocalPage | null;
46
+ }> | Readonly<{
47
+ kind: 'apply_local_create' | 'apply_local_update';
48
+ subject: PageMergeSubject;
49
+ desired: LocalPage;
50
+ }> | Readonly<{
51
+ kind: 'auto_merge';
52
+ subject: PageMergeSubject;
53
+ desired: LocalPage;
54
+ localFieldPaths: readonly string[];
55
+ remoteFieldPaths: readonly string[];
56
+ }> | Readonly<{
57
+ kind: 'conflict';
58
+ subject: PageMergeSubject;
59
+ conflict: PageMergeConflict;
60
+ }>;
61
+ export type ComparablePage = Readonly<{
62
+ identifier: string;
63
+ title: string;
64
+ path: string;
65
+ status?: LocalPage['status'];
66
+ hasUnpublishedChanges?: boolean;
67
+ seoTitle?: string;
68
+ seoDescription?: string;
69
+ blocks?: ReadonlyArray<Readonly<{
70
+ identifier: string;
71
+ kind: string;
72
+ position?: number;
73
+ data: Readonly<Record<string, unknown>>;
74
+ }>>;
75
+ }>;
76
+ export declare function planPageMerge(input: PageMergeInputs): PageMergeDecision;
77
+ export declare function comparablePage(page: LocalPage | null): ComparablePage | null;
78
+ export declare function pagesEqual(left: LocalPage | null, right: LocalPage | null): boolean;
@@ -0,0 +1,36 @@
1
+ import type { PulledPages } from '../../client/management/types';
2
+ import type { LocalPage } from '../content/reader';
3
+ import { type PageMergeDecision } from './pageMerge';
4
+ import { type PageBaseSnapshotTarget } from './pageSnapshots';
5
+ export type PageMergePushScope = Readonly<{
6
+ kind: 'all_pages';
7
+ }> | Readonly<{
8
+ kind: 'single_page';
9
+ identifier: string;
10
+ }>;
11
+ export type RemotePageMergePlan = Readonly<{
12
+ scope: PageMergePushScope;
13
+ decisions: readonly PageMergeDecision[];
14
+ summary: RemotePageMergePlanSummary;
15
+ }>;
16
+ export type RemotePageMergePlanSummary = Readonly<{
17
+ unchanged: number;
18
+ bootstrapBase: number;
19
+ preserveRemote: number;
20
+ applyLocalCreate: number;
21
+ applyLocalUpdate: number;
22
+ autoMerge: number;
23
+ conflicts: number;
24
+ }>;
25
+ export type RemotePageMergePlanInput = Readonly<{
26
+ contentDir: string;
27
+ target: PageBaseSnapshotTarget;
28
+ scope: PageMergePushScope;
29
+ localPages: readonly LocalPage[];
30
+ remotePages: readonly PulledPages['pages'][number][];
31
+ }>;
32
+ export declare function buildRemotePageMergePlan(input: RemotePageMergePlanInput): Promise<RemotePageMergePlan>;
33
+ export declare function summarizePageMergeDecisions(decisions: readonly PageMergeDecision[]): RemotePageMergePlanSummary;
34
+ export declare function pageMergePushScope(input: Readonly<{
35
+ identifier?: string;
36
+ }>): PageMergePushScope;
@@ -0,0 +1,21 @@
1
+ import type { SiteId } from '../../../../core/src/index';
2
+ import type { ManagementClient } from '../../client/management/types';
3
+ import type { LocalPage } from '../content/reader';
4
+ import type { EnvTarget } from '../helpers';
5
+ import type { OutputContext } from '../output';
6
+ export type RemotePageMergePushResult = Readonly<{
7
+ success: boolean;
8
+ }>;
9
+ export declare function planAndReportRemotePageMerge(input: Readonly<{
10
+ output: OutputContext;
11
+ client: Pick<ManagementClient, 'pages' | 'blocks' | 'pull'>;
12
+ contentDir: string;
13
+ target: EnvTarget;
14
+ siteId: SiteId;
15
+ identifier?: string;
16
+ localPages: readonly LocalPage[];
17
+ dryRun: boolean;
18
+ jsonOutput: boolean;
19
+ jsonDiff: boolean;
20
+ skipMeta: boolean;
21
+ }>): Promise<RemotePageMergePushResult>;
@@ -0,0 +1,29 @@
1
+ import type { OutputContext } from '../output';
2
+ import type { PageMergeConflict, PageMergeDecision } from './pageMerge';
3
+ import type { RemotePageMergePlan } from './pageMergePlan';
4
+ export type RemotePageMergeJsonReport = Readonly<{
5
+ kind: 'push';
6
+ scope: 'pages';
7
+ mergeRemote: true;
8
+ dryRun: boolean;
9
+ summary: RemotePageMergePlan['summary'];
10
+ decisions: readonly RemotePageMergeDecisionReport[];
11
+ }>;
12
+ export type RemotePageMergeDecisionReport = Readonly<{
13
+ kind: PageMergeDecision['kind'];
14
+ identifier: string;
15
+ localFieldPaths?: readonly string[];
16
+ remoteFieldPaths?: readonly string[];
17
+ conflict?: Readonly<{
18
+ code: PageMergeConflict['code'];
19
+ fieldPath?: string;
20
+ message: string;
21
+ }>;
22
+ }>;
23
+ export declare function remotePageMergeJsonReport(input: Readonly<{
24
+ plan: RemotePageMergePlan;
25
+ dryRun: boolean;
26
+ }>): RemotePageMergeJsonReport;
27
+ export declare function writeRemotePageMergeHumanReport(output: Pick<OutputContext, 'info'>, plan: RemotePageMergePlan, options: Readonly<{
28
+ dryRun: boolean;
29
+ }>): void;
@@ -0,0 +1,48 @@
1
+ import type { PulledPages } from '../../client/management/types';
2
+ import type { EnvTarget } from '../helpers';
3
+ import type { LocalPage } from '../content/reader';
4
+ export type PageBaseSnapshotTarget = Readonly<{
5
+ env: EnvTarget;
6
+ siteId: string;
7
+ }>;
8
+ export type PageBaseSnapshotSubject = Readonly<{
9
+ identifier: string;
10
+ }>;
11
+ export type PageBaseSnapshot = Readonly<{
12
+ version: 'page-base.v1';
13
+ target: Readonly<{
14
+ env: EnvTarget;
15
+ siteIdHash: string;
16
+ }>;
17
+ subject: PageBaseSnapshotSubject;
18
+ pulledAt: string;
19
+ remoteVersion: Readonly<{
20
+ createdAt?: string;
21
+ updatedAt?: string;
22
+ publishedAt?: string | null;
23
+ }>;
24
+ page: LocalPage;
25
+ }>;
26
+ export type WritePageBaseSnapshotsInput = Readonly<{
27
+ contentDir: string;
28
+ target: PageBaseSnapshotTarget;
29
+ pages: PulledPages['pages'];
30
+ meta: PulledPages['meta'];
31
+ cleanupMissing?: boolean;
32
+ }>;
33
+ export type WritePageBaseSnapshotsResult = Readonly<{
34
+ rootDir: string;
35
+ count: number;
36
+ }>;
37
+ export declare function pageBaseSnapshotRoot(contentDir: string): string;
38
+ export declare function pageBaseSnapshotPath(input: Readonly<{
39
+ contentDir: string;
40
+ target: PageBaseSnapshotTarget;
41
+ subject: PageBaseSnapshotSubject;
42
+ }>): string;
43
+ export declare function writePageBaseSnapshots(input: WritePageBaseSnapshotsInput): Promise<WritePageBaseSnapshotsResult>;
44
+ export declare function readPageBaseSnapshot(input: Readonly<{
45
+ contentDir: string;
46
+ target: PageBaseSnapshotTarget;
47
+ subject: PageBaseSnapshotSubject;
48
+ }>): Promise<PageBaseSnapshot | null>;
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import type { LocalContent, LocalFooterContent } from '../content/reader';
8
8
  import type { PulledContent, ManagementFooterContent } from '../../client/management/types';
9
+ import { type BuildFieldDiffOptions, type FieldChange } from './field-diff';
10
+ export type { FieldChange } from './field-diff';
9
11
  export type DiffType = 'create' | 'update' | 'unchanged';
10
12
  export interface EntryDiff {
11
13
  type: DiffType | 'delete';
@@ -205,15 +207,10 @@ export declare function formatDiffDetail(diff: ContentDiff): string;
205
207
  * @returns Content diff showing what would be created/updated locally
206
208
  */
207
209
  export declare function calculatePullDiff(remote: PulledContent, local: LocalContent | null): ContentDiff;
208
- export interface FieldChange {
209
- path: string;
210
- before: unknown;
211
- after: unknown;
212
- }
213
210
  /**
214
211
  * Find changed fields between two objects with their before/after values.
215
212
  */
216
- export declare function findChangedFieldsWithValues(local: Record<string, unknown>, remote: Record<string, unknown>, prefix?: string): FieldChange[];
213
+ export declare function findChangedFieldsWithValues(local: Record<string, unknown>, remote: Record<string, unknown>, prefix?: string, options?: BuildFieldDiffOptions): FieldChange[];
217
214
  /**
218
215
  * Format a value for human-readable display.
219
216
  */