@riverbankcms/sdk 0.8.0 → 0.8.1
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/dist/cli/index.js +583 -147
- package/dist/cli/index.js.map +1 -1
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +126 -30
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +126 -30
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/hooks.d.mts +2 -2
- package/dist/client/hooks.d.ts +2 -2
- package/dist/client/hooks.js +34 -20
- package/dist/client/hooks.js.map +1 -1
- package/dist/client/hooks.mjs +34 -20
- package/dist/client/hooks.mjs.map +1 -1
- package/dist/client/rendering/client.js +182 -123
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +174 -109
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/usePage-Dsi39Exp.d.ts +6915 -0
- package/dist/client/usePage-Im82JRRe.d.mts +6915 -0
- package/dist/server/{Layout-l2v4Qa6E.d.ts → Layout-CZ-kxKfl.d.ts} +1 -1
- package/dist/server/{Layout-D4J009eS.d.mts → Layout-ESG8zvrk.d.mts} +1 -1
- package/dist/server/{chunk-4YQJUL5W.mjs → chunk-5GCSRTIU.mjs} +8 -4
- package/dist/server/chunk-5GCSRTIU.mjs.map +1 -0
- package/dist/server/{chunk-YYO3RIFO.js → chunk-6ERSDFTY.js} +35 -21
- package/dist/server/chunk-6ERSDFTY.js.map +1 -0
- package/dist/server/{chunk-YXA4GAAQ.mjs → chunk-6VTKALLN.mjs} +2 -6
- package/dist/server/{chunk-YXA4GAAQ.mjs.map → chunk-6VTKALLN.mjs.map} +1 -1
- package/dist/server/{chunk-BYBJA6SP.mjs → chunk-A3UZ2LDH.mjs} +35 -21
- package/dist/server/chunk-A3UZ2LDH.mjs.map +1 -0
- package/dist/server/{chunk-OSF34JTQ.mjs → chunk-ADD3O2QO.mjs} +4 -4
- package/dist/server/{chunk-C6FIJC7T.mjs → chunk-BNHK7YOC.mjs} +2 -2
- package/dist/server/{chunk-TT5JWA4X.js → chunk-DAXWU3S3.js} +9 -9
- package/dist/server/{chunk-TT5JWA4X.js.map → chunk-DAXWU3S3.js.map} +1 -1
- package/dist/server/{chunk-7UPVCT3K.js → chunk-F2NDLDDA.js} +239 -154
- package/dist/server/chunk-F2NDLDDA.js.map +1 -0
- package/dist/server/{chunk-LNOUXALA.mjs → chunk-FUFPKTSI.mjs} +96 -11
- package/dist/server/chunk-FUFPKTSI.mjs.map +1 -0
- package/dist/server/{chunk-65A5HAUZ.mjs → chunk-GRFFJUCO.mjs} +3 -3
- package/dist/server/{chunk-65A5HAUZ.mjs.map → chunk-GRFFJUCO.mjs.map} +1 -1
- package/dist/server/{chunk-AEFWG657.mjs → chunk-HDHY4236.mjs} +2 -2
- package/dist/server/{chunk-2KCF2DNK.js → chunk-HE3RTUDX.js} +8 -8
- package/dist/server/{chunk-2KCF2DNK.js.map → chunk-HE3RTUDX.js.map} +1 -1
- package/dist/server/{chunk-RVDS7VSP.js → chunk-IJTJH4J3.js} +4 -4
- package/dist/server/{chunk-RVDS7VSP.js.map → chunk-IJTJH4J3.js.map} +1 -1
- package/dist/server/{chunk-P3NNN73G.js → chunk-K44OPKLA.js} +3 -3
- package/dist/server/{chunk-P3NNN73G.js.map → chunk-K44OPKLA.js.map} +1 -1
- package/dist/server/{chunk-EIJ27EZQ.js → chunk-KDCVCDW6.js} +10 -6
- package/dist/server/chunk-KDCVCDW6.js.map +1 -0
- package/dist/server/{chunk-7BVRA5MY.js → chunk-KGORQCHF.js} +9 -9
- package/dist/server/{chunk-7BVRA5MY.js.map → chunk-KGORQCHF.js.map} +1 -1
- package/dist/server/{chunk-WM646WI3.js → chunk-MFNWLB5G.js} +7 -7
- package/dist/server/{chunk-WM646WI3.js.map → chunk-MFNWLB5G.js.map} +1 -1
- package/dist/server/{chunk-EIVISR62.js → chunk-P4O3WSAR.js} +2 -6
- package/dist/server/chunk-P4O3WSAR.js.map +1 -0
- package/dist/server/{chunk-RBJFXNDM.mjs → chunk-PGZJUNCY.mjs} +4 -4
- package/dist/server/{chunk-ARNCLSQT.mjs → chunk-T5PAA22U.mjs} +2 -2
- package/dist/server/{chunk-T26N3P26.js → chunk-TLZHVGTL.js} +4 -4
- package/dist/server/{chunk-T26N3P26.js.map → chunk-TLZHVGTL.js.map} +1 -1
- package/dist/server/{chunk-P4K63SBZ.mjs → chunk-TR7MSLWL.mjs} +3 -3
- package/dist/server/{chunk-NFEGQTCC.mjs → chunk-WMJKH4XE.mjs} +8 -1
- package/dist/server/{chunk-4CV4JOE5.js → chunk-Z6ZWNWWR.js} +9 -2
- package/dist/server/chunk-Z6ZWNWWR.js.map +1 -0
- package/dist/server/{components-D2uCKCj7.d.ts → components-CE48wJM1.d.ts} +4 -4
- package/dist/server/{components-vtYEmmPF.d.mts → components-iEDvl2Yw.d.mts} +4 -4
- package/dist/server/components.d.mts +6 -6
- package/dist/server/components.d.ts +6 -6
- package/dist/server/components.js +7 -7
- package/dist/server/components.mjs +6 -6
- package/dist/server/config-validation.d.mts +3 -3
- package/dist/server/config-validation.d.ts +3 -3
- package/dist/server/config-validation.js +6 -6
- package/dist/server/config-validation.mjs +5 -5
- package/dist/server/config.d.mts +5 -5
- package/dist/server/config.d.ts +5 -5
- package/dist/server/config.js +6 -6
- package/dist/server/config.mjs +5 -5
- package/dist/server/data.d.mts +3 -3
- package/dist/server/data.d.ts +3 -3
- package/dist/server/data.js +4 -4
- package/dist/server/data.mjs +3 -3
- package/dist/server/env.js +1 -1
- package/dist/server/env.mjs +1 -1
- package/dist/server/{index-BxrAuL9K.d.ts → index-BHLK2mgQ.d.ts} +2 -2
- package/dist/server/{index-DfWg1Qle.d.mts → index-BrH_NIRO.d.mts} +2 -2
- package/dist/server/{index-2qnY7VH_.d.mts → index-Cgvb5fVQ.d.mts} +2 -2
- package/dist/server/{index-CH_dvF6n.d.ts → index-DTBg8eXj.d.ts} +2 -2
- package/dist/server/index.d.mts +6 -6
- package/dist/server/index.d.ts +6 -6
- package/dist/server/index.js +11 -11
- package/dist/server/index.mjs +2 -2
- package/dist/server/{loadContent-DECnsp4k.d.ts → loadContent-BUK6IVJf.d.ts} +26 -4
- package/dist/server/{loadContent-Du5kS8UM.d.mts → loadContent-au9Weoy0.d.mts} +26 -4
- package/dist/server/loadPage-AWYZ2QA2.mjs +11 -0
- package/dist/server/loadPage-CMHYAW2J.js +11 -0
- package/dist/server/{loadPage-AXNAERDS.js.map → loadPage-CMHYAW2J.js.map} +1 -1
- package/dist/server/{loadPage-VBorKlWv.d.mts → loadPage-DiHEl8BA.d.mts} +3 -3
- package/dist/server/{loadPage-BZohBxxf.d.ts → loadPage-JOIbF7ih.d.ts} +3 -3
- package/dist/server/metadata.d.mts +5 -5
- package/dist/server/metadata.d.ts +5 -5
- package/dist/server/metadata.js +1 -1
- package/dist/server/metadata.mjs +1 -1
- package/dist/server/navigation.d.mts +4 -8
- package/dist/server/navigation.d.ts +4 -8
- package/dist/server/navigation.js +3 -7
- package/dist/server/navigation.js.map +1 -1
- package/dist/server/navigation.mjs +2 -6
- package/dist/server/next/revalidate.js +1 -1
- package/dist/server/next/revalidate.mjs +1 -1
- package/dist/server/next/tags.js +1 -1
- package/dist/server/next/tags.mjs +1 -1
- package/dist/server/next.d.mts +7 -7
- package/dist/server/next.d.ts +7 -7
- package/dist/server/next.js +20 -16
- package/dist/server/next.js.map +1 -1
- package/dist/server/next.mjs +12 -8
- package/dist/server/next.mjs.map +1 -1
- package/dist/server/rendering/server.d.mts +5 -5
- package/dist/server/rendering/server.d.ts +5 -5
- package/dist/server/rendering/server.js +9 -9
- package/dist/server/rendering/server.mjs +8 -8
- package/dist/server/rendering.d.mts +8 -8
- package/dist/server/rendering.d.ts +8 -8
- package/dist/server/rendering.js +11 -11
- package/dist/server/rendering.mjs +10 -10
- package/dist/server/routing.d.mts +5 -5
- package/dist/server/routing.d.ts +5 -5
- package/dist/server/routing.js +2 -2
- package/dist/server/routing.mjs +2 -2
- package/dist/server/{schema-Z6-afHJG.d.mts → schema-DYtW0zEu.d.mts} +40 -0
- package/dist/server/{schema-Z6-afHJG.d.ts → schema-DYtW0zEu.d.ts} +40 -0
- package/dist/server/server.d.mts +6 -6
- package/dist/server/server.d.ts +6 -6
- package/dist/server/server.js +7 -7
- package/dist/server/server.mjs +6 -6
- package/dist/server/theme-bridge.js +9 -9
- package/dist/server/theme-bridge.mjs +3 -3
- package/dist/server/theme.js +1 -1
- package/dist/server/theme.mjs +1 -1
- package/dist/server/{types-DT30Qy7x.d.mts → types-BAM1kcGA.d.mts} +1 -1
- package/dist/server/{types-D0rPF8l5.d.ts → types-CmBB0Osp.d.ts} +2 -2
- package/dist/server/{types-BRQ_6yOc.d.mts → types-DDNKxQXw.d.mts} +2 -2
- package/dist/server/{types-D8XqwoVd.d.ts → types-DVesWaB7.d.ts} +1 -1
- package/dist/server/{types-CJfJwcuL.d.mts → types-M0CviVW2.d.mts} +1 -1
- package/dist/server/{types-CgSO0yxg.d.ts → types-_SNCu2ZZ.d.ts} +1 -1
- package/dist/server/{validation-Pv3Zs6dP.d.mts → validation-BA1TKthZ.d.mts} +2 -2
- package/dist/server/{validation-D1LaY1kQ.d.ts → validation-js7BCPN8.d.ts} +2 -2
- package/dist/server/webhooks.js +1 -1
- package/dist/server/webhooks.mjs +1 -1
- package/package.json +1 -1
- package/dist/server/chunk-4CV4JOE5.js.map +0 -1
- package/dist/server/chunk-4YQJUL5W.mjs.map +0 -1
- package/dist/server/chunk-7UPVCT3K.js.map +0 -1
- package/dist/server/chunk-BYBJA6SP.mjs.map +0 -1
- package/dist/server/chunk-EIJ27EZQ.js.map +0 -1
- package/dist/server/chunk-EIVISR62.js.map +0 -1
- package/dist/server/chunk-LNOUXALA.mjs.map +0 -1
- package/dist/server/chunk-YYO3RIFO.js.map +0 -1
- package/dist/server/loadPage-AXNAERDS.js +0 -11
- package/dist/server/loadPage-XR7ORQ2E.mjs +0 -11
- /package/dist/server/{chunk-OSF34JTQ.mjs.map → chunk-ADD3O2QO.mjs.map} +0 -0
- /package/dist/server/{chunk-C6FIJC7T.mjs.map → chunk-BNHK7YOC.mjs.map} +0 -0
- /package/dist/server/{chunk-AEFWG657.mjs.map → chunk-HDHY4236.mjs.map} +0 -0
- /package/dist/server/{chunk-RBJFXNDM.mjs.map → chunk-PGZJUNCY.mjs.map} +0 -0
- /package/dist/server/{chunk-ARNCLSQT.mjs.map → chunk-T5PAA22U.mjs.map} +0 -0
- /package/dist/server/{chunk-P4K63SBZ.mjs.map → chunk-TR7MSLWL.mjs.map} +0 -0
- /package/dist/server/{chunk-NFEGQTCC.mjs.map → chunk-WMJKH4XE.mjs.map} +0 -0
- /package/dist/server/{loadPage-XR7ORQ2E.mjs.map → loadPage-AWYZ2QA2.mjs.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -7,9 +7,9 @@ var fs6 = require('fs');
|
|
|
7
7
|
var dotenv = require('dotenv');
|
|
8
8
|
var commander = require('commander');
|
|
9
9
|
var zod = require('zod');
|
|
10
|
-
var prompts = require('prompts');
|
|
11
10
|
var fs3 = require('fs/promises');
|
|
12
11
|
var readline = require('readline');
|
|
12
|
+
var prompts = require('prompts');
|
|
13
13
|
var equal = require('fast-deep-equal');
|
|
14
14
|
var os = require('os');
|
|
15
15
|
var child_process = require('child_process');
|
|
@@ -41,9 +41,9 @@ function _interopNamespace(e) {
|
|
|
41
41
|
|
|
42
42
|
var path9__namespace = /*#__PURE__*/_interopNamespace(path9);
|
|
43
43
|
var fs6__namespace = /*#__PURE__*/_interopNamespace(fs6);
|
|
44
|
-
var prompts__default = /*#__PURE__*/_interopDefault(prompts);
|
|
45
44
|
var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
|
|
46
45
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
46
|
+
var prompts__default = /*#__PURE__*/_interopDefault(prompts);
|
|
47
47
|
var equal__default = /*#__PURE__*/_interopDefault(equal);
|
|
48
48
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
49
49
|
var simpleGit__default = /*#__PURE__*/_interopDefault(simpleGit);
|
|
@@ -506,7 +506,11 @@ var uiSchema = zod.z.object({
|
|
|
506
506
|
layout: zod.z.enum(["stack", "grid"]).optional(),
|
|
507
507
|
columns: zod.z.number().int().min(2).max(4).optional(),
|
|
508
508
|
// Entry picker configuration
|
|
509
|
-
contentTypeField: zod.z.string().optional()
|
|
509
|
+
contentTypeField: zod.z.string().optional(),
|
|
510
|
+
// Extras pattern: fields marked as extras are hidden behind a modal toggle
|
|
511
|
+
extras: zod.z.boolean().optional(),
|
|
512
|
+
// Render in block header instead of form body (used for section styles)
|
|
513
|
+
renderInHeader: zod.z.boolean().optional()
|
|
510
514
|
}).partial();
|
|
511
515
|
var baseFieldSchema = zod.z.object({
|
|
512
516
|
id: zod.z.string().min(1, "Field id is required"),
|
|
@@ -1676,25 +1680,30 @@ function createButtonGroup(options = {}) {
|
|
|
1676
1680
|
ui: { colSpan: 2 }
|
|
1677
1681
|
}
|
|
1678
1682
|
];
|
|
1679
|
-
const
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1683
|
+
const iconFields = [
|
|
1684
|
+
{
|
|
1685
|
+
id: "iconLeft",
|
|
1686
|
+
type: "media",
|
|
1687
|
+
label: "Left icon",
|
|
1688
|
+
required: false,
|
|
1689
|
+
mediaKinds: ["image"],
|
|
1690
|
+
ui: { extras: true }
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
id: "iconRight",
|
|
1694
|
+
type: "media",
|
|
1695
|
+
label: "Right icon",
|
|
1696
|
+
required: false,
|
|
1697
|
+
mediaKinds: ["image"],
|
|
1698
|
+
ui: { extras: true }
|
|
1690
1699
|
}
|
|
1691
|
-
|
|
1700
|
+
];
|
|
1692
1701
|
return {
|
|
1693
1702
|
id: groupId,
|
|
1694
1703
|
type: "group",
|
|
1695
1704
|
label: groupLabel,
|
|
1696
1705
|
ui: { layout: "grid", columns: 2, flattenInRepeater, hideLabel: !showGroupLabel },
|
|
1697
|
-
schema: { fields: [...mainFields,
|
|
1706
|
+
schema: { fields: [...mainFields, ...iconFields] },
|
|
1698
1707
|
required: false
|
|
1699
1708
|
};
|
|
1700
1709
|
}
|
|
@@ -2990,6 +2999,10 @@ var BACKGROUND_POSITION_PRESETS = [
|
|
|
2990
2999
|
BACKGROUND_POSITION_PRESETS.map((p) => p.value);
|
|
2991
3000
|
|
|
2992
3001
|
// ../blocks/src/system/fields/background.ts
|
|
3002
|
+
var BACKGROUND_WIDGETS = {
|
|
3003
|
+
COLOR: "backgroundColor",
|
|
3004
|
+
GRADIENT: "backgroundGradient"
|
|
3005
|
+
};
|
|
2993
3006
|
function createBackgroundField(options = {}) {
|
|
2994
3007
|
const {
|
|
2995
3008
|
id = "background",
|
|
@@ -3015,8 +3028,7 @@ function createBackgroundField(options = {}) {
|
|
|
3015
3028
|
required: false,
|
|
3016
3029
|
multiline: false,
|
|
3017
3030
|
ui: {
|
|
3018
|
-
|
|
3019
|
-
widget: "backgroundColor"
|
|
3031
|
+
widget: BACKGROUND_WIDGETS.COLOR
|
|
3020
3032
|
}
|
|
3021
3033
|
}
|
|
3022
3034
|
]
|
|
@@ -3033,11 +3045,11 @@ function createBackgroundField(options = {}) {
|
|
|
3033
3045
|
id: "gradient",
|
|
3034
3046
|
type: "text",
|
|
3035
3047
|
label: "Gradient",
|
|
3036
|
-
description: "
|
|
3048
|
+
description: "Select a gradient from theme presets.",
|
|
3037
3049
|
required: false,
|
|
3038
|
-
multiline:
|
|
3050
|
+
multiline: false,
|
|
3039
3051
|
ui: {
|
|
3040
|
-
|
|
3052
|
+
widget: BACKGROUND_WIDGETS.GRADIENT
|
|
3041
3053
|
}
|
|
3042
3054
|
}
|
|
3043
3055
|
]
|
|
@@ -3096,7 +3108,7 @@ function createBackgroundField(options = {}) {
|
|
|
3096
3108
|
id: "position",
|
|
3097
3109
|
type: "presetOrCustom",
|
|
3098
3110
|
label: "Position",
|
|
3099
|
-
description: 'Anchor point for
|
|
3111
|
+
description: 'Anchor point for scaled images. For "Fill" mode, the image focus point (if set) takes precedence.',
|
|
3100
3112
|
required: false,
|
|
3101
3113
|
presets: [...BACKGROUND_POSITION_PRESETS],
|
|
3102
3114
|
customInput: {
|
|
@@ -3187,6 +3199,8 @@ function sectionStylesField(options = {}) {
|
|
|
3187
3199
|
required: false,
|
|
3188
3200
|
schema: { fields: fields4 },
|
|
3189
3201
|
ui: {
|
|
3202
|
+
// Render in block header instead of form body
|
|
3203
|
+
renderInHeader: true,
|
|
3190
3204
|
modalConfig: {
|
|
3191
3205
|
buttonLabel: label,
|
|
3192
3206
|
description: "Configure background and spacing for this section.",
|
|
@@ -5643,111 +5657,6 @@ var riverbankSiteConfigSchema = zod.z.object({
|
|
|
5643
5657
|
|
|
5644
5658
|
// src/cli/push-config.ts
|
|
5645
5659
|
init_load_config();
|
|
5646
|
-
async function pushToDashboard(dashboardUrl, siteId, apiKey, config3) {
|
|
5647
|
-
const pushUrl = `${dashboardUrl}/api/sites/${siteId}/sdk-config`;
|
|
5648
|
-
console.log(`Pushing config to ${pushUrl}...`);
|
|
5649
|
-
let response;
|
|
5650
|
-
try {
|
|
5651
|
-
response = await fetch(pushUrl, {
|
|
5652
|
-
method: "POST",
|
|
5653
|
-
headers: {
|
|
5654
|
-
"Content-Type": "application/json",
|
|
5655
|
-
"Authorization": `Bearer ${apiKey}`
|
|
5656
|
-
},
|
|
5657
|
-
body: JSON.stringify({ config: config3 }),
|
|
5658
|
-
signal: AbortSignal.timeout(3e4)
|
|
5659
|
-
});
|
|
5660
|
-
} catch (error) {
|
|
5661
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5662
|
-
throw new Error(`Failed to connect to dashboard: ${message}`);
|
|
5663
|
-
}
|
|
5664
|
-
if (!response.ok) {
|
|
5665
|
-
let errorMessage = `Dashboard returned ${response.status}`;
|
|
5666
|
-
try {
|
|
5667
|
-
const errorBody = await response.json();
|
|
5668
|
-
if (errorBody.error) {
|
|
5669
|
-
errorMessage = errorBody.error;
|
|
5670
|
-
if (errorBody.details) {
|
|
5671
|
-
errorMessage += ":\n" + errorBody.details.map((d) => ` - ${d.path}: ${d.message}`).join("\n");
|
|
5672
|
-
}
|
|
5673
|
-
}
|
|
5674
|
-
} catch {
|
|
5675
|
-
}
|
|
5676
|
-
throw new Error(errorMessage);
|
|
5677
|
-
}
|
|
5678
|
-
}
|
|
5679
|
-
async function pushConfigAction(options) {
|
|
5680
|
-
try {
|
|
5681
|
-
const rawConfig = await loadConfigFile(options.config);
|
|
5682
|
-
console.log("Validating config...");
|
|
5683
|
-
const parseResult = riverbankSiteConfigSchema.safeParse(rawConfig);
|
|
5684
|
-
if (!parseResult.success) {
|
|
5685
|
-
console.error("Invalid config:");
|
|
5686
|
-
for (const issue of parseResult.error.issues) {
|
|
5687
|
-
console.error(` - ${issue.path.join(".")}: ${issue.message}`);
|
|
5688
|
-
}
|
|
5689
|
-
process.exit(1);
|
|
5690
|
-
}
|
|
5691
|
-
const conflicts = validateFieldIdConflicts(parseResult.data.blockFieldExtensions);
|
|
5692
|
-
if (conflicts.length > 0) {
|
|
5693
|
-
console.error("Field ID conflicts detected in blockFieldExtensions:");
|
|
5694
|
-
for (const conflict of conflicts) {
|
|
5695
|
-
console.error(` - ${conflict.message}`);
|
|
5696
|
-
}
|
|
5697
|
-
process.exit(1);
|
|
5698
|
-
}
|
|
5699
|
-
const { siteId } = parseResult.data;
|
|
5700
|
-
const apiKey = resolveManagementApiKey(options.apiKey, options.isRemote);
|
|
5701
|
-
await pushToDashboard(options.dashboard, siteId, apiKey, parseResult.data);
|
|
5702
|
-
console.log("Config pushed successfully!");
|
|
5703
|
-
} catch (error) {
|
|
5704
|
-
console.error("Error:", error instanceof Error ? error.message : error);
|
|
5705
|
-
process.exit(1);
|
|
5706
|
-
}
|
|
5707
|
-
}
|
|
5708
|
-
function resolveDashboardUrl(cliOption, isRemote) {
|
|
5709
|
-
const envVar = isRemote ? "RIVERBANK_REMOTE_DASHBOARD_URL" : "RIVERBANK_LOCAL_DASHBOARD_URL";
|
|
5710
|
-
const url = cliOption || process.env[envVar];
|
|
5711
|
-
if (!url) {
|
|
5712
|
-
console.error("Error: Dashboard URL is required.");
|
|
5713
|
-
console.error(`Provide --dashboard <url> or set ${envVar} environment variable.`);
|
|
5714
|
-
process.exit(1);
|
|
5715
|
-
}
|
|
5716
|
-
return url;
|
|
5717
|
-
}
|
|
5718
|
-
function resolveManagementApiKey(cliOption, isRemote) {
|
|
5719
|
-
const envVar = isRemote ? "RIVERBANK_REMOTE_MGMT_API_KEY" : "RIVERBANK_LOCAL_MGMT_API_KEY";
|
|
5720
|
-
const apiKey = cliOption || process.env[envVar];
|
|
5721
|
-
if (!apiKey) {
|
|
5722
|
-
console.error("Error: Management API key is required.");
|
|
5723
|
-
console.error(`Provide --api-key <key> or set ${envVar} environment variable.`);
|
|
5724
|
-
process.exit(1);
|
|
5725
|
-
}
|
|
5726
|
-
if (!apiKey.startsWith("bld_mgmt_sk_")) {
|
|
5727
|
-
console.error(`Error: Invalid management API key format for ${envVar}.`);
|
|
5728
|
-
console.error("Expected key starting with bld_mgmt_sk_.");
|
|
5729
|
-
process.exit(1);
|
|
5730
|
-
}
|
|
5731
|
-
return apiKey;
|
|
5732
|
-
}
|
|
5733
|
-
var pushConfigCommand = new commander.Command("push-config").description("Push SDK config to dashboard").option("--api-key <key>", "Management API key (or set RIVERBANK_*_MGMT_API_KEY)").option("--dashboard <url>", "Dashboard URL (or set RIVERBANK_*_DASHBOARD_URL env var)").option("--config <path>", "Path to config file (default: ./riverbank.config.ts)").addHelpText("after", `
|
|
5734
|
-
Description:
|
|
5735
|
-
Syncs your local riverbank.config.ts to the CMS dashboard, including:
|
|
5736
|
-
- Custom blocks
|
|
5737
|
-
- Block field extensions
|
|
5738
|
-
- Block field options
|
|
5739
|
-
- Content types, pages, entries, and navigation
|
|
5740
|
-
|
|
5741
|
-
Examples:
|
|
5742
|
-
$ npx riverbankcms push-config
|
|
5743
|
-
$ npx riverbankcms push-config --api-key bld_mgmt_sk_... --dashboard https://www.riverbankcms.com
|
|
5744
|
-
$ npx riverbankcms push-config --config ./src/riverbank.config.ts
|
|
5745
|
-
`).action((options, command) => {
|
|
5746
|
-
const globalOpts = command.optsWithGlobals();
|
|
5747
|
-
const isRemote = globalOpts.remote ?? false;
|
|
5748
|
-
const dashboard = resolveDashboardUrl(options.dashboard, isRemote);
|
|
5749
|
-
return pushConfigAction({ ...options, dashboard, isRemote });
|
|
5750
|
-
});
|
|
5751
5660
|
|
|
5752
5661
|
// src/client/management/http.ts
|
|
5753
5662
|
var ManagementApiError = class extends Error {
|
|
@@ -5759,6 +5668,9 @@ var ManagementApiError = class extends Error {
|
|
|
5759
5668
|
this.statusCode = statusCode;
|
|
5760
5669
|
}
|
|
5761
5670
|
};
|
|
5671
|
+
function is404Error(error) {
|
|
5672
|
+
return error instanceof ManagementApiError && error.statusCode === 404;
|
|
5673
|
+
}
|
|
5762
5674
|
function createHttpClient(config3) {
|
|
5763
5675
|
const baseUrl = `${config3.dashboardUrl}/api/sdk/${config3.siteId}`;
|
|
5764
5676
|
const timeout = config3.timeout ?? 3e4;
|
|
@@ -5864,7 +5776,7 @@ function createEntryOperations(http) {
|
|
|
5864
5776
|
`/entries/${encodeURIComponent(contentType)}/${encodeURIComponent(identifier)}`
|
|
5865
5777
|
);
|
|
5866
5778
|
} catch (error) {
|
|
5867
|
-
if (error
|
|
5779
|
+
if (is404Error(error)) {
|
|
5868
5780
|
return null;
|
|
5869
5781
|
}
|
|
5870
5782
|
throw error;
|
|
@@ -5906,7 +5818,7 @@ function createPageOperations(http) {
|
|
|
5906
5818
|
`/pages/${encodeURIComponent(identifier)}`
|
|
5907
5819
|
);
|
|
5908
5820
|
} catch (error) {
|
|
5909
|
-
if (error
|
|
5821
|
+
if (is404Error(error)) {
|
|
5910
5822
|
return null;
|
|
5911
5823
|
}
|
|
5912
5824
|
throw error;
|
|
@@ -5939,7 +5851,7 @@ function createBlockOperations(http) {
|
|
|
5939
5851
|
`/pages/${encodeURIComponent(pageIdentifier)}/blocks/${encodeURIComponent(blockIdentifier)}`
|
|
5940
5852
|
);
|
|
5941
5853
|
} catch (error) {
|
|
5942
|
-
if (error
|
|
5854
|
+
if (is404Error(error)) {
|
|
5943
5855
|
return null;
|
|
5944
5856
|
}
|
|
5945
5857
|
throw error;
|
|
@@ -5978,7 +5890,7 @@ function createNavigationOperations(http) {
|
|
|
5978
5890
|
`/navigation/${encodeURIComponent(name)}`
|
|
5979
5891
|
);
|
|
5980
5892
|
} catch (error) {
|
|
5981
|
-
if (error
|
|
5893
|
+
if (is404Error(error)) {
|
|
5982
5894
|
return null;
|
|
5983
5895
|
}
|
|
5984
5896
|
throw error;
|
|
@@ -6047,6 +5959,9 @@ function createPullOperations(http) {
|
|
|
6047
5959
|
truncationMessage: entriesResult?.meta?.truncationMessage
|
|
6048
5960
|
}
|
|
6049
5961
|
};
|
|
5962
|
+
},
|
|
5963
|
+
async siteInfo() {
|
|
5964
|
+
return http.get("/pull/site-info");
|
|
6050
5965
|
}
|
|
6051
5966
|
};
|
|
6052
5967
|
}
|
|
@@ -6069,6 +5984,122 @@ function createIdentifiersOperations(http) {
|
|
|
6069
5984
|
};
|
|
6070
5985
|
}
|
|
6071
5986
|
|
|
5987
|
+
// src/client/management/media.ts
|
|
5988
|
+
function createMediaOperations(config3) {
|
|
5989
|
+
const baseUrl = `${config3.dashboardUrl}/api/sdk/${config3.siteId}`;
|
|
5990
|
+
const timeout = config3.timeout ?? 6e4;
|
|
5991
|
+
async function formDataRequest(path13, formData) {
|
|
5992
|
+
const url = `${baseUrl}${path13}`;
|
|
5993
|
+
const headers = {
|
|
5994
|
+
"Authorization": `Bearer ${config3.managementApiKey}`
|
|
5995
|
+
// Note: Don't set Content-Type - let fetch set it with boundary
|
|
5996
|
+
};
|
|
5997
|
+
let response;
|
|
5998
|
+
try {
|
|
5999
|
+
response = await fetch(url, {
|
|
6000
|
+
method: "POST",
|
|
6001
|
+
headers,
|
|
6002
|
+
body: formData,
|
|
6003
|
+
signal: AbortSignal.timeout(timeout)
|
|
6004
|
+
});
|
|
6005
|
+
} catch (error) {
|
|
6006
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
6007
|
+
throw new ManagementApiError(
|
|
6008
|
+
`Request timed out after ${timeout}ms`,
|
|
6009
|
+
"sdk:timeout",
|
|
6010
|
+
void 0,
|
|
6011
|
+
void 0
|
|
6012
|
+
);
|
|
6013
|
+
}
|
|
6014
|
+
throw new ManagementApiError(
|
|
6015
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
6016
|
+
"sdk:network-error",
|
|
6017
|
+
void 0,
|
|
6018
|
+
void 0
|
|
6019
|
+
);
|
|
6020
|
+
}
|
|
6021
|
+
const json = await response.json();
|
|
6022
|
+
if (!response.ok || !json.success) {
|
|
6023
|
+
if (json.error) {
|
|
6024
|
+
throw new ManagementApiError(
|
|
6025
|
+
json.error.message,
|
|
6026
|
+
json.error.code,
|
|
6027
|
+
json.error.details,
|
|
6028
|
+
response.status
|
|
6029
|
+
);
|
|
6030
|
+
}
|
|
6031
|
+
throw new ManagementApiError(
|
|
6032
|
+
`Request failed with status ${response.status}`,
|
|
6033
|
+
"sdk:http-error",
|
|
6034
|
+
void 0,
|
|
6035
|
+
response.status
|
|
6036
|
+
);
|
|
6037
|
+
}
|
|
6038
|
+
return json.data;
|
|
6039
|
+
}
|
|
6040
|
+
async function jsonRequest(path13, body) {
|
|
6041
|
+
const url = `${baseUrl}${path13}`;
|
|
6042
|
+
let response;
|
|
6043
|
+
try {
|
|
6044
|
+
response = await fetch(url, {
|
|
6045
|
+
method: "POST",
|
|
6046
|
+
headers: {
|
|
6047
|
+
"Authorization": `Bearer ${config3.managementApiKey}`,
|
|
6048
|
+
"Content-Type": "application/json"
|
|
6049
|
+
},
|
|
6050
|
+
body: JSON.stringify(body),
|
|
6051
|
+
signal: AbortSignal.timeout(3e4)
|
|
6052
|
+
});
|
|
6053
|
+
} catch (error) {
|
|
6054
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
6055
|
+
throw new ManagementApiError(
|
|
6056
|
+
"Request timed out after 30000ms",
|
|
6057
|
+
"sdk:timeout",
|
|
6058
|
+
void 0,
|
|
6059
|
+
void 0
|
|
6060
|
+
);
|
|
6061
|
+
}
|
|
6062
|
+
throw new ManagementApiError(
|
|
6063
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
6064
|
+
"sdk:network-error",
|
|
6065
|
+
void 0,
|
|
6066
|
+
void 0
|
|
6067
|
+
);
|
|
6068
|
+
}
|
|
6069
|
+
const json = await response.json();
|
|
6070
|
+
if (!response.ok || !json.success) {
|
|
6071
|
+
if (json.error) {
|
|
6072
|
+
throw new ManagementApiError(
|
|
6073
|
+
json.error.message,
|
|
6074
|
+
json.error.code,
|
|
6075
|
+
json.error.details,
|
|
6076
|
+
response.status
|
|
6077
|
+
);
|
|
6078
|
+
}
|
|
6079
|
+
throw new ManagementApiError(
|
|
6080
|
+
`Request failed with status ${response.status}`,
|
|
6081
|
+
"sdk:http-error",
|
|
6082
|
+
void 0,
|
|
6083
|
+
response.status
|
|
6084
|
+
);
|
|
6085
|
+
}
|
|
6086
|
+
return json.data;
|
|
6087
|
+
}
|
|
6088
|
+
return {
|
|
6089
|
+
async upload(file) {
|
|
6090
|
+
const formData = new FormData();
|
|
6091
|
+
const blob = new Blob([new Uint8Array(file.data)], { type: file.contentType });
|
|
6092
|
+
formData.append("file", blob, file.filename);
|
|
6093
|
+
formData.append("storagePath", file.storagePath);
|
|
6094
|
+
return formDataRequest("/media/upload", formData);
|
|
6095
|
+
},
|
|
6096
|
+
async exists(storagePath) {
|
|
6097
|
+
const result = await jsonRequest("/media/exists", { storagePath });
|
|
6098
|
+
return result.exists;
|
|
6099
|
+
}
|
|
6100
|
+
};
|
|
6101
|
+
}
|
|
6102
|
+
|
|
6072
6103
|
// src/client/management/index.ts
|
|
6073
6104
|
function createManagementClient(config3) {
|
|
6074
6105
|
if (!config3.dashboardUrl) {
|
|
@@ -6100,11 +6131,16 @@ function createManagementClient(config3) {
|
|
|
6100
6131
|
settings: createSettingsOperations(http),
|
|
6101
6132
|
pull: createPullOperations(http),
|
|
6102
6133
|
preview: createPreviewOperations(http),
|
|
6103
|
-
identifiers: createIdentifiersOperations(http)
|
|
6134
|
+
identifiers: createIdentifiersOperations(http),
|
|
6135
|
+
media: createMediaOperations(config3)
|
|
6104
6136
|
};
|
|
6105
6137
|
}
|
|
6106
6138
|
|
|
6107
6139
|
// src/cli/env.ts
|
|
6140
|
+
function getEnvVarName(key, remote) {
|
|
6141
|
+
const target = remote ? "REMOTE" : "LOCAL";
|
|
6142
|
+
return `RIVERBANK_${target}_${key}`;
|
|
6143
|
+
}
|
|
6108
6144
|
function getEnvPrefix(target) {
|
|
6109
6145
|
return target === "remote" ? "RIVERBANK_REMOTE" : "RIVERBANK_LOCAL";
|
|
6110
6146
|
}
|
|
@@ -6115,12 +6151,16 @@ function requireEnv(name) {
|
|
|
6115
6151
|
}
|
|
6116
6152
|
return value;
|
|
6117
6153
|
}
|
|
6154
|
+
function getEnv(name, fallback) {
|
|
6155
|
+
return process.env[name] ?? fallback;
|
|
6156
|
+
}
|
|
6118
6157
|
function loadEnvironment(remote) {
|
|
6119
6158
|
const target = remote ? "remote" : "local";
|
|
6120
6159
|
const prefix = getEnvPrefix(target);
|
|
6121
6160
|
const siteId = requireEnv(`${prefix}_SITE_ID`);
|
|
6122
6161
|
const dashboardUrl = requireEnv(`${prefix}_DASHBOARD_URL`);
|
|
6123
6162
|
const managementApiKey = requireEnv(`${prefix}_MGMT_API_KEY`);
|
|
6163
|
+
const supabaseUrl = getEnv(`${prefix}_SUPABASE_URL`);
|
|
6124
6164
|
if (!managementApiKey.startsWith("bld_mgmt_sk_")) {
|
|
6125
6165
|
throw new Error(
|
|
6126
6166
|
`Invalid management API key format for ${prefix}_MGMT_API_KEY. Expected key starting with bld_mgmt_sk_`
|
|
@@ -6133,10 +6173,20 @@ function loadEnvironment(remote) {
|
|
|
6133
6173
|
`Invalid dashboard URL in ${prefix}_DASHBOARD_URL: ${dashboardUrl}. Expected format: http://localhost:4000 or https://dashboard.example.com`
|
|
6134
6174
|
);
|
|
6135
6175
|
}
|
|
6176
|
+
if (supabaseUrl) {
|
|
6177
|
+
try {
|
|
6178
|
+
new URL(supabaseUrl);
|
|
6179
|
+
} catch {
|
|
6180
|
+
throw new Error(
|
|
6181
|
+
`Invalid Supabase URL in ${prefix}_SUPABASE_URL: ${supabaseUrl}. Expected format: http://127.0.0.1:54321 or https://xxx.supabase.co`
|
|
6182
|
+
);
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
6136
6185
|
return {
|
|
6137
6186
|
siteId,
|
|
6138
6187
|
dashboardUrl,
|
|
6139
|
-
managementApiKey
|
|
6188
|
+
managementApiKey,
|
|
6189
|
+
supabaseUrl
|
|
6140
6190
|
};
|
|
6141
6191
|
}
|
|
6142
6192
|
|
|
@@ -6513,6 +6563,112 @@ function createListCommand(config3) {
|
|
|
6513
6563
|
);
|
|
6514
6564
|
}
|
|
6515
6565
|
|
|
6566
|
+
// src/cli/push-config.ts
|
|
6567
|
+
async function pushToDashboard(output, dashboardUrl, siteId, apiKey, config3) {
|
|
6568
|
+
const pushUrl = `${dashboardUrl}/api/sites/${siteId}/sdk-config`;
|
|
6569
|
+
output.info(`Pushing config to ${pushUrl}...`);
|
|
6570
|
+
let response;
|
|
6571
|
+
try {
|
|
6572
|
+
response = await fetch(pushUrl, {
|
|
6573
|
+
method: "POST",
|
|
6574
|
+
headers: {
|
|
6575
|
+
"Content-Type": "application/json",
|
|
6576
|
+
"Authorization": `Bearer ${apiKey}`
|
|
6577
|
+
},
|
|
6578
|
+
body: JSON.stringify({ config: config3 }),
|
|
6579
|
+
signal: AbortSignal.timeout(3e4)
|
|
6580
|
+
});
|
|
6581
|
+
} catch (error) {
|
|
6582
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
6583
|
+
throw new Error(`Failed to connect to dashboard: ${message}`);
|
|
6584
|
+
}
|
|
6585
|
+
if (!response.ok) {
|
|
6586
|
+
let errorMessage = `Dashboard returned ${response.status}`;
|
|
6587
|
+
try {
|
|
6588
|
+
const errorBody = await response.json();
|
|
6589
|
+
if (errorBody.error) {
|
|
6590
|
+
errorMessage = errorBody.error;
|
|
6591
|
+
if (errorBody.details) {
|
|
6592
|
+
errorMessage += ":\n" + errorBody.details.map((d) => ` - ${d.path}: ${d.message}`).join("\n");
|
|
6593
|
+
}
|
|
6594
|
+
}
|
|
6595
|
+
} catch {
|
|
6596
|
+
}
|
|
6597
|
+
throw new Error(errorMessage);
|
|
6598
|
+
}
|
|
6599
|
+
}
|
|
6600
|
+
async function pushConfigAction(output, options) {
|
|
6601
|
+
try {
|
|
6602
|
+
const rawConfig = await loadConfigFile(options.config);
|
|
6603
|
+
output.info("Validating config...");
|
|
6604
|
+
const parseResult = riverbankSiteConfigSchema.safeParse(rawConfig);
|
|
6605
|
+
if (!parseResult.success) {
|
|
6606
|
+
output.error("Invalid config", {
|
|
6607
|
+
issues: parseResult.error.issues.map((issue) => ({
|
|
6608
|
+
path: issue.path.join("."),
|
|
6609
|
+
message: issue.message
|
|
6610
|
+
}))
|
|
6611
|
+
});
|
|
6612
|
+
}
|
|
6613
|
+
const conflicts = validateFieldIdConflicts(parseResult.data.blockFieldExtensions);
|
|
6614
|
+
if (conflicts.length > 0) {
|
|
6615
|
+
output.error("Field ID conflicts detected in blockFieldExtensions", {
|
|
6616
|
+
conflicts: conflicts.map((c) => c.message)
|
|
6617
|
+
});
|
|
6618
|
+
}
|
|
6619
|
+
const { siteId } = parseResult.data;
|
|
6620
|
+
const apiKey = resolveManagementApiKey(output, options.apiKey, options.isRemote);
|
|
6621
|
+
await pushToDashboard(output, options.dashboard, siteId, apiKey, parseResult.data);
|
|
6622
|
+
output.success("Config pushed successfully!");
|
|
6623
|
+
} catch (error) {
|
|
6624
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6625
|
+
output.error(message);
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
function resolveDashboardUrl(output, cliOption, isRemote) {
|
|
6629
|
+
const envVar = getEnvVarName("DASHBOARD_URL", isRemote);
|
|
6630
|
+
const url = cliOption || process.env[envVar];
|
|
6631
|
+
if (!url) {
|
|
6632
|
+
output.error("Dashboard URL is required", {
|
|
6633
|
+
suggestion: `Provide --dashboard <url> or set ${envVar} environment variable.`
|
|
6634
|
+
});
|
|
6635
|
+
}
|
|
6636
|
+
return url;
|
|
6637
|
+
}
|
|
6638
|
+
function resolveManagementApiKey(output, cliOption, isRemote) {
|
|
6639
|
+
const envVar = getEnvVarName("MGMT_API_KEY", isRemote);
|
|
6640
|
+
const apiKey = cliOption || process.env[envVar];
|
|
6641
|
+
if (!apiKey) {
|
|
6642
|
+
output.error("Management API key is required", {
|
|
6643
|
+
suggestion: `Provide --api-key <key> or set ${envVar} environment variable.`
|
|
6644
|
+
});
|
|
6645
|
+
}
|
|
6646
|
+
if (!apiKey.startsWith("bld_mgmt_sk_")) {
|
|
6647
|
+
output.error("Invalid management API key format", {
|
|
6648
|
+
expected: "Key starting with bld_mgmt_sk_",
|
|
6649
|
+
suggestion: `Check your ${envVar} environment variable.`
|
|
6650
|
+
});
|
|
6651
|
+
}
|
|
6652
|
+
return apiKey;
|
|
6653
|
+
}
|
|
6654
|
+
var pushConfigCommand = new commander.Command("push-config").description("Push SDK config to dashboard").option("--api-key <key>", "Management API key (or set RIVERBANK_*_MGMT_API_KEY)").option("--dashboard <url>", "Dashboard URL (or set RIVERBANK_*_DASHBOARD_URL env var)").option("--config <path>", "Path to config file (default: ./riverbank.config.ts)").addHelpText("after", `
|
|
6655
|
+
Description:
|
|
6656
|
+
Syncs your local riverbank.config.ts to the CMS dashboard, including:
|
|
6657
|
+
- Custom blocks
|
|
6658
|
+
- Block field extensions
|
|
6659
|
+
- Block field options
|
|
6660
|
+
- Content types, pages, entries, and navigation
|
|
6661
|
+
|
|
6662
|
+
Examples:
|
|
6663
|
+
$ npx riverbankcms push-config
|
|
6664
|
+
$ npx riverbankcms push-config --api-key bld_mgmt_sk_... --dashboard https://www.riverbankcms.com
|
|
6665
|
+
$ npx riverbankcms push-config --config ./src/riverbank.config.ts
|
|
6666
|
+
`).action((options, command) => {
|
|
6667
|
+
const { output, isRemote } = getOutputContext(command);
|
|
6668
|
+
const dashboard = resolveDashboardUrl(output, options.dashboard, isRemote);
|
|
6669
|
+
return pushConfigAction(output, { ...options, dashboard, isRemote });
|
|
6670
|
+
});
|
|
6671
|
+
|
|
6516
6672
|
// src/cli/sync/mapper.ts
|
|
6517
6673
|
function stripNavigationItemIds(items) {
|
|
6518
6674
|
return items.map((item) => {
|
|
@@ -6616,6 +6772,53 @@ async function writeSettings(contentDir, pulledSettings) {
|
|
|
6616
6772
|
await writeJsonFile(filePath, pulledSettings.settings);
|
|
6617
6773
|
return filePath;
|
|
6618
6774
|
}
|
|
6775
|
+
async function updateMetadataAfterPush(contentDir, remoteContent) {
|
|
6776
|
+
const metaDir = path9__namespace.join(contentDir, ".meta");
|
|
6777
|
+
await ensureDir(metaDir);
|
|
6778
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6779
|
+
for (const [contentType, entries] of Object.entries(remoteContent.entries)) {
|
|
6780
|
+
const metaPath = path9__namespace.join(metaDir, `${contentType}.json`);
|
|
6781
|
+
const entriesMeta = {};
|
|
6782
|
+
for (const entry of entries) {
|
|
6783
|
+
entriesMeta[entry.identifier] = {
|
|
6784
|
+
createdAt: entry.createdAt,
|
|
6785
|
+
updatedAt: entry.updatedAt
|
|
6786
|
+
};
|
|
6787
|
+
}
|
|
6788
|
+
await writeJsonFile(metaPath, {
|
|
6789
|
+
pulledAt: now,
|
|
6790
|
+
entries: entriesMeta
|
|
6791
|
+
});
|
|
6792
|
+
}
|
|
6793
|
+
if (remoteContent.pages.length > 0) {
|
|
6794
|
+
const metaPath = path9__namespace.join(metaDir, "pages.json");
|
|
6795
|
+
const pagesMeta = {};
|
|
6796
|
+
for (const page of remoteContent.pages) {
|
|
6797
|
+
pagesMeta[page.identifier] = {
|
|
6798
|
+
createdAt: page.createdAt,
|
|
6799
|
+
updatedAt: page.updatedAt
|
|
6800
|
+
};
|
|
6801
|
+
}
|
|
6802
|
+
await writeJsonFile(metaPath, {
|
|
6803
|
+
pulledAt: now,
|
|
6804
|
+
pages: pagesMeta
|
|
6805
|
+
});
|
|
6806
|
+
}
|
|
6807
|
+
if (remoteContent.navigation.length > 0) {
|
|
6808
|
+
const metaPath = path9__namespace.join(metaDir, "navigation.json");
|
|
6809
|
+
const menusMeta = {};
|
|
6810
|
+
for (const menu of remoteContent.navigation) {
|
|
6811
|
+
menusMeta[menu.name] = {
|
|
6812
|
+
createdAt: menu.createdAt,
|
|
6813
|
+
updatedAt: menu.updatedAt
|
|
6814
|
+
};
|
|
6815
|
+
}
|
|
6816
|
+
await writeJsonFile(metaPath, {
|
|
6817
|
+
pulledAt: now,
|
|
6818
|
+
menus: menusMeta
|
|
6819
|
+
});
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6619
6822
|
async function fileExists(filePath) {
|
|
6620
6823
|
try {
|
|
6621
6824
|
await fs3__namespace.access(filePath);
|
|
@@ -6764,6 +6967,107 @@ async function readNavigationMeta(contentDir) {
|
|
|
6764
6967
|
}
|
|
6765
6968
|
}
|
|
6766
6969
|
|
|
6970
|
+
// src/cli/sync/media.ts
|
|
6971
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
6972
|
+
maxRetries: 3,
|
|
6973
|
+
baseDelayMs: 1e3,
|
|
6974
|
+
// 1 second
|
|
6975
|
+
maxDelayMs: 1e4
|
|
6976
|
+
// 10 seconds
|
|
6977
|
+
};
|
|
6978
|
+
function sleep(ms) {
|
|
6979
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
6980
|
+
}
|
|
6981
|
+
function getBackoffDelay(attempt, baseDelayMs, maxDelayMs) {
|
|
6982
|
+
const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
|
|
6983
|
+
const jitter = exponentialDelay * (0.75 + Math.random() * 0.5);
|
|
6984
|
+
return Math.min(jitter, maxDelayMs);
|
|
6985
|
+
}
|
|
6986
|
+
async function withRetry(fn, options = {}) {
|
|
6987
|
+
const { maxRetries, baseDelayMs, maxDelayMs } = { ...DEFAULT_RETRY_CONFIG, ...options };
|
|
6988
|
+
let lastError;
|
|
6989
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
6990
|
+
try {
|
|
6991
|
+
return await fn();
|
|
6992
|
+
} catch (error) {
|
|
6993
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
6994
|
+
if (attempt < maxRetries) {
|
|
6995
|
+
const delay = getBackoffDelay(attempt, baseDelayMs, maxDelayMs);
|
|
6996
|
+
options.onRetry?.(attempt + 1, lastError);
|
|
6997
|
+
await sleep(delay);
|
|
6998
|
+
}
|
|
6999
|
+
}
|
|
7000
|
+
}
|
|
7001
|
+
throw lastError;
|
|
7002
|
+
}
|
|
7003
|
+
function extractMediaPaths(data) {
|
|
7004
|
+
const paths = /* @__PURE__ */ new Set();
|
|
7005
|
+
function walk(value) {
|
|
7006
|
+
if (value === null || value === void 0) {
|
|
7007
|
+
return;
|
|
7008
|
+
}
|
|
7009
|
+
if (Array.isArray(value)) {
|
|
7010
|
+
for (const item of value) {
|
|
7011
|
+
walk(item);
|
|
7012
|
+
}
|
|
7013
|
+
return;
|
|
7014
|
+
}
|
|
7015
|
+
if (typeof value === "object") {
|
|
7016
|
+
const obj = value;
|
|
7017
|
+
if (typeof obj.storagePath === "string" && obj.storagePath) {
|
|
7018
|
+
paths.add(obj.storagePath);
|
|
7019
|
+
}
|
|
7020
|
+
for (const key of Object.keys(obj)) {
|
|
7021
|
+
walk(obj[key]);
|
|
7022
|
+
}
|
|
7023
|
+
}
|
|
7024
|
+
}
|
|
7025
|
+
walk(data);
|
|
7026
|
+
return paths;
|
|
7027
|
+
}
|
|
7028
|
+
function buildStorageUrl(supabaseUrl, relativePath, siteId, bucket = "media") {
|
|
7029
|
+
const baseUrl = supabaseUrl.replace(/\/$/, "");
|
|
7030
|
+
const fullPath = `sites/${siteId}/${relativePath}`;
|
|
7031
|
+
return `${baseUrl}/storage/v1/object/public/${bucket}/${fullPath}`;
|
|
7032
|
+
}
|
|
7033
|
+
async function downloadMedia(url, options) {
|
|
7034
|
+
try {
|
|
7035
|
+
return await withRetry(
|
|
7036
|
+
async () => {
|
|
7037
|
+
const response = await fetch(url);
|
|
7038
|
+
if (!response.ok) {
|
|
7039
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
7040
|
+
throw new DownloadError(`HTTP ${response.status}`, response.status, false);
|
|
7041
|
+
}
|
|
7042
|
+
throw new DownloadError(`HTTP ${response.status}`, response.status, true);
|
|
7043
|
+
}
|
|
7044
|
+
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
7045
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
7046
|
+
const data = Buffer.from(arrayBuffer);
|
|
7047
|
+
return { data, contentType };
|
|
7048
|
+
},
|
|
7049
|
+
{
|
|
7050
|
+
maxRetries: options?.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
|
|
7051
|
+
onRetry: (attempt, error) => {
|
|
7052
|
+
console.warn(`[media-sync] Retry ${attempt} for ${url}: ${error.message}`);
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
);
|
|
7056
|
+
} catch (error) {
|
|
7057
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7058
|
+
console.error(`[media-sync] Download failed after retries: ${url} - ${message}`);
|
|
7059
|
+
return null;
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
var DownloadError = class extends Error {
|
|
7063
|
+
constructor(message, status, retryable) {
|
|
7064
|
+
super(message);
|
|
7065
|
+
this.status = status;
|
|
7066
|
+
this.retryable = retryable;
|
|
7067
|
+
this.name = "DownloadError";
|
|
7068
|
+
}
|
|
7069
|
+
};
|
|
7070
|
+
|
|
6767
7071
|
// src/cli/commands/pull.ts
|
|
6768
7072
|
var DEFAULT_PAGE_LIMIT = 500;
|
|
6769
7073
|
async function pullEntriesWithPagination(client, contentType, output) {
|
|
@@ -6848,21 +7152,105 @@ async function fetchAllContentPaginated(client, contentTypes, output) {
|
|
|
6848
7152
|
meta: { pulledAt: (/* @__PURE__ */ new Date()).toISOString(), entries: allMeta }
|
|
6849
7153
|
};
|
|
6850
7154
|
}
|
|
6851
|
-
|
|
7155
|
+
async function syncMediaFiles(content, sourceSupabaseUrl, sourceSiteId, targetSiteId, targetClient, output) {
|
|
7156
|
+
const mediaPaths = extractMediaPaths(content);
|
|
7157
|
+
if (mediaPaths.size === 0) {
|
|
7158
|
+
output.info("No media files found in content");
|
|
7159
|
+
return;
|
|
7160
|
+
}
|
|
7161
|
+
output.info(`Found ${mediaPaths.size} media files to sync`);
|
|
7162
|
+
let synced = 0;
|
|
7163
|
+
let skipped = 0;
|
|
7164
|
+
let failed = 0;
|
|
7165
|
+
for (const relativePath of mediaPaths) {
|
|
7166
|
+
const filename = relativePath.split("/").pop() ?? "file";
|
|
7167
|
+
const targetPath = `sites/${targetSiteId}/${relativePath}`;
|
|
7168
|
+
try {
|
|
7169
|
+
const exists = await targetClient.media.exists(targetPath);
|
|
7170
|
+
if (exists) {
|
|
7171
|
+
skipped++;
|
|
7172
|
+
continue;
|
|
7173
|
+
}
|
|
7174
|
+
} catch (error) {
|
|
7175
|
+
if (process.env.DEBUG) {
|
|
7176
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
7177
|
+
console.warn(`[media-sync] Could not check if ${filename} exists: ${message}`);
|
|
7178
|
+
}
|
|
7179
|
+
}
|
|
7180
|
+
const url = buildStorageUrl(sourceSupabaseUrl, relativePath, sourceSiteId);
|
|
7181
|
+
const downloaded = await downloadMedia(url);
|
|
7182
|
+
if (!downloaded) {
|
|
7183
|
+
output.warn(` Failed to download: ${filename}`);
|
|
7184
|
+
failed++;
|
|
7185
|
+
continue;
|
|
7186
|
+
}
|
|
7187
|
+
try {
|
|
7188
|
+
await targetClient.media.upload({
|
|
7189
|
+
data: downloaded.data,
|
|
7190
|
+
filename,
|
|
7191
|
+
contentType: downloaded.contentType,
|
|
7192
|
+
storagePath: targetPath
|
|
7193
|
+
});
|
|
7194
|
+
synced++;
|
|
7195
|
+
} catch (error) {
|
|
7196
|
+
output.warn(` Failed to upload: ${filename}`);
|
|
7197
|
+
failed++;
|
|
7198
|
+
}
|
|
7199
|
+
}
|
|
7200
|
+
output.info(`Media sync: ${synced} synced, ${skipped} skipped (already exist), ${failed} failed`);
|
|
7201
|
+
}
|
|
7202
|
+
var pullCommand = new commander.Command("pull").description("Pull content from CMS").argument("[scope]", "What to pull: entries, pages, navigation, settings, or all (default)").argument("[type]", 'Content type (when scope is "entries")').option("--output <dir>", "Output directory", "./content").option("--force", "Overwrite existing files without prompting").option("--yes", "Skip confirmation prompt (same as --force)").option("--sync-media", "Sync media files from source to target environment").addHelpText("after", `
|
|
6852
7203
|
Examples:
|
|
6853
7204
|
$ riverbankcms pull # Pull all content
|
|
6854
7205
|
$ riverbankcms pull --remote # Pull from production
|
|
7206
|
+
$ riverbankcms pull --remote --sync-media # Pull from production and sync media to local
|
|
6855
7207
|
$ riverbankcms pull entries # Pull all entries
|
|
6856
7208
|
$ riverbankcms pull entries blog-post # Pull specific content type
|
|
6857
7209
|
$ riverbankcms pull pages # Pull pages with blocks
|
|
6858
7210
|
$ riverbankcms pull navigation # Pull navigation menus
|
|
6859
7211
|
$ riverbankcms pull settings # Pull site settings
|
|
6860
7212
|
$ riverbankcms pull --output ./src/content # Custom output directory
|
|
7213
|
+
|
|
7214
|
+
Media Sync:
|
|
7215
|
+
When using --sync-media, media files are:
|
|
7216
|
+
1. Downloaded from the source environment's Supabase storage
|
|
7217
|
+
2. Uploaded to the target environment via the management API
|
|
7218
|
+
|
|
7219
|
+
The storage URL is automatically fetched from the source CMS API.
|
|
7220
|
+
No additional environment variables are required beyond the standard
|
|
7221
|
+
RIVERBANK_*_DASHBOARD_URL, RIVERBANK_*_SITE_ID, and RIVERBANK_*_MGMT_API_KEY.
|
|
6861
7222
|
`).action(
|
|
6862
7223
|
withErrorHandling(
|
|
6863
7224
|
async (scope, type, options, command) => {
|
|
6864
|
-
const { output, client } = createCommandContext(command);
|
|
7225
|
+
const { output, client, isRemote } = createCommandContext(command);
|
|
6865
7226
|
const contentDir = path9__namespace.resolve(options.output ?? "./content");
|
|
7227
|
+
let targetClient = null;
|
|
7228
|
+
let sourceSupabaseUrl = null;
|
|
7229
|
+
let sourceSiteId = null;
|
|
7230
|
+
let targetSiteId = null;
|
|
7231
|
+
if (options.syncMedia) {
|
|
7232
|
+
const sourceEnv = loadEnvironment(isRemote);
|
|
7233
|
+
const targetEnv = loadEnvironment(!isRemote);
|
|
7234
|
+
output.info("Fetching storage configuration from source...");
|
|
7235
|
+
try {
|
|
7236
|
+
const siteInfo = await client.pull.siteInfo();
|
|
7237
|
+
sourceSupabaseUrl = siteInfo.supabaseUrl;
|
|
7238
|
+
} catch (error) {
|
|
7239
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
7240
|
+
output.error(`Failed to get storage configuration from source: ${message}`, {
|
|
7241
|
+
suggestion: "Ensure the source CMS is running and accessible"
|
|
7242
|
+
});
|
|
7243
|
+
return;
|
|
7244
|
+
}
|
|
7245
|
+
sourceSiteId = sourceEnv.siteId;
|
|
7246
|
+
targetSiteId = targetEnv.siteId;
|
|
7247
|
+
targetClient = createManagementClient({
|
|
7248
|
+
dashboardUrl: targetEnv.dashboardUrl,
|
|
7249
|
+
managementApiKey: targetEnv.managementApiKey,
|
|
7250
|
+
siteId: targetEnv.siteId
|
|
7251
|
+
});
|
|
7252
|
+
output.info(`Media sync enabled: ${isRemote ? "remote" : "local"} -> ${isRemote ? "local" : "remote"}`);
|
|
7253
|
+
}
|
|
6866
7254
|
if (await contentDirExists(contentDir) && !options.force && !options.yes) {
|
|
6867
7255
|
if (!process.stdin.isTTY) {
|
|
6868
7256
|
output.error("Content directory already exists and --yes not specified", {
|
|
@@ -6945,6 +7333,17 @@ Examples:
|
|
|
6945
7333
|
const contentTypes = Object.keys(result.entries);
|
|
6946
7334
|
result = await fetchAllContentPaginated(client, contentTypes, output);
|
|
6947
7335
|
}
|
|
7336
|
+
if (targetClient && sourceSupabaseUrl && sourceSiteId && targetSiteId) {
|
|
7337
|
+
output.info("Syncing media files...");
|
|
7338
|
+
await syncMediaFiles(
|
|
7339
|
+
result,
|
|
7340
|
+
sourceSupabaseUrl,
|
|
7341
|
+
sourceSiteId,
|
|
7342
|
+
targetSiteId,
|
|
7343
|
+
targetClient,
|
|
7344
|
+
output
|
|
7345
|
+
);
|
|
7346
|
+
}
|
|
6948
7347
|
const { totalCount: totalEntries } = await writeAllEntries(
|
|
6949
7348
|
contentDir,
|
|
6950
7349
|
result.entries,
|
|
@@ -7943,6 +8342,43 @@ Safety:
|
|
|
7943
8342
|
output
|
|
7944
8343
|
);
|
|
7945
8344
|
reportSyncResults(output, result, result.errors.length > 0);
|
|
8345
|
+
if (result.errors.length === 0) {
|
|
8346
|
+
try {
|
|
8347
|
+
output.info("Updating local metadata...");
|
|
8348
|
+
const freshRemote = await client.pull.all();
|
|
8349
|
+
const entriesForMeta = {};
|
|
8350
|
+
if (freshRemote.meta.entries) {
|
|
8351
|
+
for (const [key, meta] of Object.entries(freshRemote.meta.entries)) {
|
|
8352
|
+
const [contentType, identifier] = key.split(":");
|
|
8353
|
+
if (contentType && identifier) {
|
|
8354
|
+
if (!entriesForMeta[contentType]) {
|
|
8355
|
+
entriesForMeta[contentType] = [];
|
|
8356
|
+
}
|
|
8357
|
+
entriesForMeta[contentType].push({
|
|
8358
|
+
identifier,
|
|
8359
|
+
updatedAt: meta.updatedAt,
|
|
8360
|
+
createdAt: meta.createdAt
|
|
8361
|
+
});
|
|
8362
|
+
}
|
|
8363
|
+
}
|
|
8364
|
+
}
|
|
8365
|
+
await updateMetadataAfterPush(contentDir, {
|
|
8366
|
+
entries: entriesForMeta,
|
|
8367
|
+
pages: freshRemote.pages.map((p) => ({
|
|
8368
|
+
identifier: p.identifier,
|
|
8369
|
+
updatedAt: p.updatedAt,
|
|
8370
|
+
createdAt: p.createdAt
|
|
8371
|
+
})),
|
|
8372
|
+
navigation: freshRemote.navigation.map((n) => ({
|
|
8373
|
+
name: n.name,
|
|
8374
|
+
updatedAt: n.updatedAt,
|
|
8375
|
+
createdAt: n.createdAt
|
|
8376
|
+
}))
|
|
8377
|
+
});
|
|
8378
|
+
} catch (metaError) {
|
|
8379
|
+
output.warn('Push succeeded but metadata update failed. Run "pull" to sync metadata.');
|
|
8380
|
+
}
|
|
8381
|
+
}
|
|
7946
8382
|
} catch (error) {
|
|
7947
8383
|
handleCommandError(error, output);
|
|
7948
8384
|
}
|
|
@@ -11042,7 +11478,7 @@ var SimpleCache = class {
|
|
|
11042
11478
|
};
|
|
11043
11479
|
|
|
11044
11480
|
// src/version.ts
|
|
11045
|
-
var SDK_VERSION = "0.8.
|
|
11481
|
+
var SDK_VERSION = "0.8.1";
|
|
11046
11482
|
|
|
11047
11483
|
// src/client/error.ts
|
|
11048
11484
|
var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
@@ -11189,7 +11625,7 @@ var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
|
11189
11625
|
};
|
|
11190
11626
|
|
|
11191
11627
|
// src/client/resilience.ts
|
|
11192
|
-
var
|
|
11628
|
+
var DEFAULT_RETRY_CONFIG2 = {
|
|
11193
11629
|
maxAttempts: 3,
|
|
11194
11630
|
baseDelayMs: 200,
|
|
11195
11631
|
maxDelayMs: 2e3,
|
|
@@ -11272,9 +11708,9 @@ function isTransientError(error) {
|
|
|
11272
11708
|
return true;
|
|
11273
11709
|
}
|
|
11274
11710
|
function calculateBackoff(attempt, config3) {
|
|
11275
|
-
const baseDelayMs = config3.baseDelayMs ??
|
|
11276
|
-
const maxDelayMs = config3.maxDelayMs ??
|
|
11277
|
-
const jitter = config3.jitter ??
|
|
11711
|
+
const baseDelayMs = config3.baseDelayMs ?? DEFAULT_RETRY_CONFIG2.baseDelayMs;
|
|
11712
|
+
const maxDelayMs = config3.maxDelayMs ?? DEFAULT_RETRY_CONFIG2.maxDelayMs;
|
|
11713
|
+
const jitter = config3.jitter ?? DEFAULT_RETRY_CONFIG2.jitter;
|
|
11278
11714
|
const exponential = baseDelayMs * Math.pow(2, attempt - 1);
|
|
11279
11715
|
const capped = Math.min(exponential, maxDelayMs);
|
|
11280
11716
|
if (jitter === "full") {
|
|
@@ -11378,7 +11814,7 @@ var CircuitBreaker = class {
|
|
|
11378
11814
|
}
|
|
11379
11815
|
};
|
|
11380
11816
|
async function fetchWithTimeoutAndRetry(fetcher, config3) {
|
|
11381
|
-
const maxAttempts = config3.maxAttempts ??
|
|
11817
|
+
const maxAttempts = config3.maxAttempts ?? DEFAULT_RETRY_CONFIG2.maxAttempts;
|
|
11382
11818
|
const requestTimeoutMs = config3.requestTimeoutMs ?? 8e3;
|
|
11383
11819
|
let lastError;
|
|
11384
11820
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
@@ -11399,7 +11835,7 @@ async function fetchWithTimeoutAndRetry(fetcher, config3) {
|
|
|
11399
11835
|
}
|
|
11400
11836
|
if (attempt < maxAttempts) {
|
|
11401
11837
|
const delay = getRetryDelay(error, attempt, config3);
|
|
11402
|
-
await
|
|
11838
|
+
await sleep2(delay);
|
|
11403
11839
|
}
|
|
11404
11840
|
}
|
|
11405
11841
|
}
|
|
@@ -11430,7 +11866,7 @@ function getRetryDelay(error, attempt, config3) {
|
|
|
11430
11866
|
}
|
|
11431
11867
|
return calculateBackoff(attempt, config3);
|
|
11432
11868
|
}
|
|
11433
|
-
function
|
|
11869
|
+
function sleep2(ms) {
|
|
11434
11870
|
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
11435
11871
|
}
|
|
11436
11872
|
var CircuitOpenError = class extends Error {
|
|
@@ -11565,10 +12001,10 @@ function createRiverbankClient(config3) {
|
|
|
11565
12001
|
const staleTtlMs = (config3.resilience?.staleTtlSec ?? 300) * 1e3;
|
|
11566
12002
|
const requestTimeoutMs = config3.resilience?.requestTimeoutMs ?? (typeof window !== "undefined" ? DEFAULT_BROWSER_TIMEOUT_MS : DEFAULT_SERVER_TIMEOUT_MS);
|
|
11567
12003
|
const retryConfig = {
|
|
11568
|
-
maxAttempts: config3.resilience?.retry?.maxAttempts ??
|
|
11569
|
-
baseDelayMs: config3.resilience?.retry?.baseDelayMs ??
|
|
11570
|
-
maxDelayMs: config3.resilience?.retry?.maxDelayMs ??
|
|
11571
|
-
jitter: config3.resilience?.retry?.jitter ??
|
|
12004
|
+
maxAttempts: config3.resilience?.retry?.maxAttempts ?? DEFAULT_RETRY_CONFIG2.maxAttempts,
|
|
12005
|
+
baseDelayMs: config3.resilience?.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG2.baseDelayMs,
|
|
12006
|
+
maxDelayMs: config3.resilience?.retry?.maxDelayMs ?? DEFAULT_RETRY_CONFIG2.maxDelayMs,
|
|
12007
|
+
jitter: config3.resilience?.retry?.jitter ?? DEFAULT_RETRY_CONFIG2.jitter,
|
|
11572
12008
|
retryOn: config3.resilience?.retry?.retryOn
|
|
11573
12009
|
};
|
|
11574
12010
|
const circuitBreakerConfig = {
|