@riverbankcms/sdk 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/dist/cli/index.js +3104 -120
- package/dist/cli/index.js.map +1 -1
- package/dist/client/analytics.js +1 -1
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/analytics.mjs +1 -1
- package/dist/client/analytics.mjs.map +1 -1
- package/dist/client/bookings.js +6 -6
- package/dist/client/bookings.js.map +1 -1
- package/dist/client/bookings.mjs +6 -6
- package/dist/client/bookings.mjs.map +1 -1
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +1368 -520
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +1368 -520
- 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 +26 -11
- package/dist/client/hooks.js.map +1 -1
- package/dist/client/hooks.mjs +26 -11
- package/dist/client/hooks.mjs.map +1 -1
- package/dist/client/rendering/client.js +24 -14
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +24 -14
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/usePage--fGlyrgj.d.mts +6439 -0
- package/dist/client/usePage-BTPnCuWC.d.mts +6511 -0
- package/dist/client/usePage-BafOS9UT.d.mts +6512 -0
- package/dist/client/usePage-Bnx-kA6x.d.mts +6670 -0
- package/dist/client/usePage-CE7X5NcN.d.ts +6439 -0
- package/dist/client/usePage-DoPI6b8V.d.ts +6511 -0
- package/dist/client/usePage-QNWArrVO.d.ts +6670 -0
- package/dist/client/usePage-fBgPB6Oq.d.ts +6512 -0
- package/dist/server/{Layout-kRv5sU81.d.ts → Layout-B-q2Py4v.d.ts} +4 -4
- package/dist/server/{Layout-ByUnm35V.d.mts → Layout-Cc5HUXAH.d.mts} +4 -4
- package/dist/server/{chunk-6JBKKV3G.js → chunk-2KCF2DNK.js} +30 -10
- package/dist/server/chunk-2KCF2DNK.js.map +1 -0
- package/dist/server/{chunk-N3PX76AP.mjs → chunk-4HIRA33Z.mjs} +247 -135
- package/dist/server/chunk-4HIRA33Z.mjs.map +1 -0
- package/dist/server/chunk-5STV4MWD.js +189 -0
- package/dist/server/chunk-5STV4MWD.js.map +1 -0
- package/dist/server/{chunk-R5B6IOFQ.js → chunk-6OSNCH4F.js} +247 -135
- package/dist/server/chunk-6OSNCH4F.js.map +1 -0
- package/dist/server/{chunk-TKMA6D6U.js → chunk-7UPVCT3K.js} +1215 -497
- package/dist/server/chunk-7UPVCT3K.js.map +1 -0
- package/dist/server/{chunk-7DS4Q3GA.mjs → chunk-AEFWG657.mjs} +3 -3
- package/dist/server/chunk-AEFWG657.mjs.map +1 -0
- package/dist/server/{chunk-USQF2XTU.mjs → chunk-BYBJA6SP.mjs} +26 -11
- package/dist/server/chunk-BYBJA6SP.mjs.map +1 -0
- package/dist/server/{chunk-ZEAJW6T3.mjs → chunk-C6FIJC7T.mjs} +4 -3
- package/dist/server/chunk-C6FIJC7T.mjs.map +1 -0
- package/dist/server/{chunk-TO7FD6TQ.js → chunk-I2D7KOEA.js} +4 -4
- package/dist/server/{chunk-TO7FD6TQ.js.map → chunk-I2D7KOEA.js.map} +1 -1
- package/dist/server/chunk-KFLZGNPO.mjs +189 -0
- package/dist/server/chunk-KFLZGNPO.mjs.map +1 -0
- package/dist/server/chunk-L5EA4FXU.mjs +134 -0
- package/dist/server/chunk-L5EA4FXU.mjs.map +1 -0
- package/dist/server/{chunk-TNRADRPH.mjs → chunk-LNOUXALA.mjs} +1137 -419
- package/dist/server/chunk-LNOUXALA.mjs.map +1 -0
- package/dist/server/{chunk-SPXMMX3C.mjs → chunk-OSF34JTQ.mjs} +4 -4
- package/dist/server/{chunk-SWPHIUVE.js → chunk-P3NNN73G.js} +5 -4
- package/dist/server/chunk-P3NNN73G.js.map +1 -0
- package/dist/server/{chunk-I6K5REFT.mjs → chunk-P4K63SBZ.mjs} +24 -4
- package/dist/server/chunk-P4K63SBZ.mjs.map +1 -0
- package/dist/server/{chunk-HOY77YBF.js → chunk-RVDS7VSP.js} +5 -5
- package/dist/server/chunk-RVDS7VSP.js.map +1 -0
- package/dist/server/{chunk-NW5KHH4A.js → chunk-TT5JWA4X.js} +9 -9
- package/dist/server/{chunk-NW5KHH4A.js.map → chunk-TT5JWA4X.js.map} +1 -1
- package/dist/server/chunk-VSFQRHYZ.js +134 -0
- package/dist/server/chunk-VSFQRHYZ.js.map +1 -0
- package/dist/server/{chunk-EGTDJ4PL.js → chunk-YYO3RIFO.js} +26 -11
- package/dist/server/chunk-YYO3RIFO.js.map +1 -0
- package/dist/server/{chunk-OP2GHK27.mjs → chunk-Z5ZA6Q4D.mjs} +2 -2
- package/dist/server/{components-D1Z2mSDr.d.ts → components-CU46ZkAv.d.mts} +20 -75
- package/dist/server/{components-CY8jDQjv.d.mts → components-DvozDwRN.d.ts} +20 -75
- package/dist/server/components.d.mts +11 -8
- package/dist/server/components.d.ts +11 -8
- package/dist/server/components.js +5 -4
- package/dist/server/components.js.map +1 -1
- package/dist/server/components.mjs +4 -3
- package/dist/server/config-validation.d.mts +3 -3
- package/dist/server/config-validation.d.ts +3 -3
- package/dist/server/config-validation.js +9 -5
- package/dist/server/config-validation.js.map +1 -1
- package/dist/server/config-validation.mjs +8 -4
- package/dist/server/config.d.mts +243 -5
- package/dist/server/config.d.ts +243 -5
- package/dist/server/config.js +72 -5
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +72 -5
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/core-DsNWrl3o.d.mts +44 -0
- package/dist/server/core-DsNWrl3o.d.ts +44 -0
- package/dist/server/data.d.mts +4 -3
- package/dist/server/data.d.ts +4 -3
- package/dist/server/data.js +3 -3
- package/dist/server/data.mjs +2 -2
- package/dist/server/{index-DCIz9Ptv.d.ts → index-CJfMXZQr.d.ts} +2 -1
- package/dist/server/{index-DFQwtj3J.d.mts → index-Q7RLMAQ6.d.mts} +2 -1
- package/dist/server/index.d.mts +63 -6
- package/dist/server/index.d.ts +63 -6
- package/dist/server/index.js +91 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +90 -1
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/link-DjxLyC82.d.mts +23 -0
- package/dist/server/link-DjxLyC82.d.ts +23 -0
- package/dist/server/{loadContent-CWuE8FCx.d.mts → loadContent-DgpSKWqY.d.mts} +4 -4
- package/dist/server/{loadContent-DynBuR5f.d.ts → loadContent-GPvUI1bN.d.ts} +4 -4
- package/dist/server/{loadPage-B8RmlYgV.d.mts → loadPage-DGnIK7s4.d.mts} +17 -47
- package/dist/server/loadPage-DNQTTRHL.mjs +11 -0
- package/dist/server/{loadPage-BTkKpizX.d.ts → loadPage-DW9WB-u9.d.ts} +17 -47
- package/dist/server/loadPage-IDGVDFBB.js +11 -0
- package/dist/server/{loadPage-DUHBXDEW.js.map → loadPage-IDGVDFBB.js.map} +1 -1
- package/dist/server/metadata.d.mts +6 -4
- package/dist/server/metadata.d.ts +6 -4
- package/dist/server/navigation.d.mts +199 -29
- package/dist/server/navigation.d.ts +199 -29
- package/dist/server/navigation.js +27 -43
- package/dist/server/navigation.js.map +1 -1
- package/dist/server/navigation.mjs +20 -36
- package/dist/server/navigation.mjs.map +1 -1
- package/dist/server/rendering/server.d.mts +8 -6
- package/dist/server/rendering/server.d.ts +8 -6
- package/dist/server/rendering/server.js +7 -6
- package/dist/server/rendering/server.js.map +1 -1
- package/dist/server/rendering/server.mjs +6 -5
- package/dist/server/rendering.d.mts +14 -10
- package/dist/server/rendering.d.ts +14 -10
- package/dist/server/rendering.js +9 -8
- package/dist/server/rendering.js.map +1 -1
- package/dist/server/rendering.mjs +8 -7
- package/dist/server/richTextSchema-DURiozvD.d.mts +62 -0
- package/dist/server/richTextSchema-DURiozvD.d.ts +62 -0
- package/dist/server/routing.d.mts +178 -11
- package/dist/server/routing.d.ts +178 -11
- package/dist/server/routing.js +95 -2
- package/dist/server/routing.js.map +1 -1
- package/dist/server/routing.mjs +94 -1
- package/dist/server/routing.mjs.map +1 -1
- package/dist/server/{schema-Bpy9N5ZI.d.ts → schema-Z6-afHJG.d.mts} +1 -1
- package/dist/server/{schema-Bpy9N5ZI.d.mts → schema-Z6-afHJG.d.ts} +1 -1
- package/dist/server/server.d.mts +9 -7
- package/dist/server/server.d.ts +9 -7
- package/dist/server/server.js +6 -6
- package/dist/server/server.mjs +5 -5
- package/dist/server/theme-bridge.js +8 -8
- package/dist/server/theme-bridge.mjs +2 -2
- package/dist/server/{types-oCM-fw4O.d.ts → types-0f4PIlgx.d.mts} +55 -2
- package/dist/server/{types-txWsSxN7.d.mts → types-BjgZt8xJ.d.mts} +63 -2
- package/dist/server/{types-BiRZnxDx.d.ts → types-C28kMfa1.d.ts} +256 -82
- package/dist/server/{types-CL916r6x.d.ts → types-DLBhEPSt.d.ts} +63 -2
- package/dist/server/{types-CdrJqlKx.d.mts → types-DuzJZKJI.d.mts} +256 -82
- package/dist/server/{types-DkKEctWn.d.mts → types-kOQyCFXO.d.ts} +55 -2
- package/dist/server/{validation-DzvDwwRo.d.mts → validation-BGuRo8P1.d.mts} +18 -5
- package/dist/server/{validation-CoU8uAiu.d.ts → validation-DU2YE7u5.d.ts} +18 -5
- package/package.json +5 -3
- package/dist/server/chunk-6JBKKV3G.js.map +0 -1
- package/dist/server/chunk-7BOIO2S7.mjs +0 -833
- package/dist/server/chunk-7BOIO2S7.mjs.map +0 -1
- package/dist/server/chunk-7DS4Q3GA.mjs.map +0 -1
- package/dist/server/chunk-BLKVTULP.js +0 -833
- package/dist/server/chunk-BLKVTULP.js.map +0 -1
- package/dist/server/chunk-EGTDJ4PL.js.map +0 -1
- package/dist/server/chunk-HOY77YBF.js.map +0 -1
- package/dist/server/chunk-I6K5REFT.mjs.map +0 -1
- package/dist/server/chunk-N3PX76AP.mjs.map +0 -1
- package/dist/server/chunk-R5B6IOFQ.js.map +0 -1
- package/dist/server/chunk-SWPHIUVE.js.map +0 -1
- package/dist/server/chunk-TKMA6D6U.js.map +0 -1
- package/dist/server/chunk-TNRADRPH.mjs.map +0 -1
- package/dist/server/chunk-USQF2XTU.mjs.map +0 -1
- package/dist/server/chunk-ZEAJW6T3.mjs.map +0 -1
- package/dist/server/loadPage-DUHBXDEW.js +0 -11
- package/dist/server/loadPage-LYVKY3WZ.mjs +0 -11
- /package/dist/server/{chunk-SPXMMX3C.mjs.map → chunk-OSF34JTQ.mjs.map} +0 -0
- /package/dist/server/{chunk-OP2GHK27.mjs.map → chunk-Z5ZA6Q4D.mjs.map} +0 -0
- /package/dist/server/{loadPage-LYVKY3WZ.mjs.map → loadPage-DNQTTRHL.mjs.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -1,14 +1,105 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
var jiti = require('jiti');
|
|
5
|
+
var path9 = require('path');
|
|
6
|
+
var fs = require('fs');
|
|
4
7
|
var dotenv = require('dotenv');
|
|
5
8
|
var commander = require('commander');
|
|
6
9
|
var zod = require('zod');
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
+
var fs5 = require('fs/promises');
|
|
11
|
+
var readline = require('readline');
|
|
12
|
+
var equal = require('fast-deep-equal');
|
|
13
|
+
var os = require('os');
|
|
14
|
+
var child_process = require('child_process');
|
|
10
15
|
|
|
11
16
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
17
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
|
+
|
|
19
|
+
function _interopNamespace(e) {
|
|
20
|
+
if (e && e.__esModule) return e;
|
|
21
|
+
var n = Object.create(null);
|
|
22
|
+
if (e) {
|
|
23
|
+
Object.keys(e).forEach(function (k) {
|
|
24
|
+
if (k !== 'default') {
|
|
25
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
26
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: function () { return e[k]; }
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
n.default = e;
|
|
34
|
+
return Object.freeze(n);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
var path9__namespace = /*#__PURE__*/_interopNamespace(path9);
|
|
38
|
+
var fs5__namespace = /*#__PURE__*/_interopNamespace(fs5);
|
|
39
|
+
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
40
|
+
var equal__default = /*#__PURE__*/_interopDefault(equal);
|
|
41
|
+
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
42
|
+
|
|
43
|
+
var __defProp = Object.defineProperty;
|
|
44
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
45
|
+
var __esm = (fn, res) => function __init() {
|
|
46
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
47
|
+
};
|
|
48
|
+
var __export = (target, all) => {
|
|
49
|
+
for (var name in all)
|
|
50
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/cli/load-config.ts
|
|
54
|
+
var load_config_exports = {};
|
|
55
|
+
__export(load_config_exports, {
|
|
56
|
+
loadConfigFile: () => loadConfigFile
|
|
57
|
+
});
|
|
58
|
+
async function loadConfigFile(configPath) {
|
|
59
|
+
const resolvedPath = resolveConfigPath(configPath);
|
|
60
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Config file not found: ${resolvedPath}
|
|
63
|
+
Create a riverbank.config.ts file or specify a path with --config`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
console.log(`Loading config from ${resolvedPath}...`);
|
|
67
|
+
const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)), {
|
|
68
|
+
// Disable caching for CLI tool
|
|
69
|
+
fsCache: false,
|
|
70
|
+
moduleCache: false
|
|
71
|
+
});
|
|
72
|
+
try {
|
|
73
|
+
const configModule = await jiti$1.import(resolvedPath);
|
|
74
|
+
const config3 = configModule.default ?? configModule.config ?? configModule;
|
|
75
|
+
if (!config3 || typeof config3 !== "object") {
|
|
76
|
+
throw new Error("Config file must export a configuration object");
|
|
77
|
+
}
|
|
78
|
+
return config3;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error instanceof Error) {
|
|
81
|
+
throw new Error(`Failed to load config: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function resolveConfigPath(configPath) {
|
|
87
|
+
if (!configPath) {
|
|
88
|
+
return path9.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);
|
|
89
|
+
}
|
|
90
|
+
const resolved = path9.resolve(configPath);
|
|
91
|
+
if (fs.existsSync(resolved) && !resolved.endsWith(".ts") && !resolved.endsWith(".js")) {
|
|
92
|
+
return path9.resolve(resolved, DEFAULT_CONFIG_FILENAME);
|
|
93
|
+
}
|
|
94
|
+
return resolved;
|
|
95
|
+
}
|
|
96
|
+
var DEFAULT_CONFIG_FILENAME;
|
|
97
|
+
var init_load_config = __esm({
|
|
98
|
+
"src/cli/load-config.ts"() {
|
|
99
|
+
DEFAULT_CONFIG_FILENAME = "riverbank.config.ts";
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
12
103
|
// ../blocks/src/system/manifest/augmentManifest.ts
|
|
13
104
|
function augmentManifest(manifest) {
|
|
14
105
|
let augmentedFields = manifest.fields ?? [];
|
|
@@ -481,11 +572,11 @@ function bind(from, options) {
|
|
|
481
572
|
}
|
|
482
573
|
});
|
|
483
574
|
}
|
|
484
|
-
function when(
|
|
575
|
+
function when(path11, options) {
|
|
485
576
|
return (node) => ({
|
|
486
577
|
...node,
|
|
487
578
|
$when: {
|
|
488
|
-
when: { from:
|
|
579
|
+
when: { from: path11 },
|
|
489
580
|
...options?.equals !== void 0 ? { equals: options.equals } : {},
|
|
490
581
|
...options?.not ? { not: true } : {}
|
|
491
582
|
}
|
|
@@ -558,25 +649,25 @@ function devValidate(node) {
|
|
|
558
649
|
}
|
|
559
650
|
|
|
560
651
|
// ../blocks/src/system/node/fragments/backgroundLayer.ts
|
|
561
|
-
function backgroundLayer(
|
|
652
|
+
function backgroundLayer(path11, options = {}) {
|
|
562
653
|
const {
|
|
563
654
|
styleClassName = "absolute inset-0 -z-10 h-full w-full pointer-events-none",
|
|
564
655
|
imageClassName
|
|
565
656
|
} = options;
|
|
566
657
|
const styleLayer = el("div", {
|
|
567
|
-
className: { $bind: { from:
|
|
568
|
-
style: { $bind: { from:
|
|
658
|
+
className: { $bind: { from: path11, transforms: [{ id: "background.resolveClass", options: { baseClass: styleClassName } }] } },
|
|
659
|
+
style: { $bind: { from: path11, transforms: [{ id: "background.resolveStyle" }] } }
|
|
569
660
|
});
|
|
570
|
-
const imageLayer = createBackgroundImageNode(
|
|
661
|
+
const imageLayer = createBackgroundImageNode(path11, imageClassName);
|
|
571
662
|
return [styleLayer, imageLayer];
|
|
572
663
|
}
|
|
573
|
-
function createBackgroundImageNode(
|
|
574
|
-
const imagePath = `${
|
|
664
|
+
function createBackgroundImageNode(path11, baseClassName = "absolute -z-10") {
|
|
665
|
+
const imagePath = `${path11}.image`;
|
|
575
666
|
return media(
|
|
576
667
|
{
|
|
577
668
|
className: {
|
|
578
669
|
$bind: {
|
|
579
|
-
from:
|
|
670
|
+
from: path11,
|
|
580
671
|
transforms: [{
|
|
581
672
|
id: "background.resolveImageClassName",
|
|
582
673
|
options: { baseClass: `background-image ${baseClassName}` }
|
|
@@ -585,7 +676,7 @@ function createBackgroundImageNode(path, baseClassName = "absolute -z-10") {
|
|
|
585
676
|
},
|
|
586
677
|
style: {
|
|
587
678
|
$bind: {
|
|
588
|
-
from:
|
|
679
|
+
from: path11,
|
|
589
680
|
transforms: [{ id: "background.resolveImageStyle" }]
|
|
590
681
|
}
|
|
591
682
|
}
|
|
@@ -604,7 +695,7 @@ function parseToken(source) {
|
|
|
604
695
|
if (source.includes("/")) {
|
|
605
696
|
const [token, opacity] = source.split("/");
|
|
606
697
|
const alpha = Number(opacity) / 100;
|
|
607
|
-
if (!Number.isNaN(alpha)) {
|
|
698
|
+
if (!Number.isNaN(alpha) && token) {
|
|
608
699
|
return { token, alpha };
|
|
609
700
|
}
|
|
610
701
|
return { token: source };
|
|
@@ -644,7 +735,8 @@ function headingGroup(opts) {
|
|
|
644
735
|
containerClass = "text-center",
|
|
645
736
|
className,
|
|
646
737
|
eyebrowClass = "heading-eyebrow text-sm font-semibold tracking-wide",
|
|
647
|
-
|
|
738
|
+
// h2 now gets size/weight from theme typography CSS
|
|
739
|
+
titleClass = "heading-title",
|
|
648
740
|
eyebrowStyle = textColorStyle("neutral-500"),
|
|
649
741
|
titleStyle = textColorStyle("neutral-900")
|
|
650
742
|
} = opts;
|
|
@@ -686,7 +778,7 @@ function sectionContainer(children, opts) {
|
|
|
686
778
|
var DEFAULT_SECTION_SPACING = "medium";
|
|
687
779
|
|
|
688
780
|
// ../blocks/src/system/node/fragments/styledSection.ts
|
|
689
|
-
function styledSection(
|
|
781
|
+
function styledSection(config3) {
|
|
690
782
|
const {
|
|
691
783
|
children,
|
|
692
784
|
baseClass = "px-6",
|
|
@@ -694,7 +786,7 @@ function styledSection(config2) {
|
|
|
694
786
|
background = "background/base",
|
|
695
787
|
bindFrom = "_sectionStyles",
|
|
696
788
|
imageClassName = "absolute -z-10"
|
|
697
|
-
} =
|
|
789
|
+
} = config3;
|
|
698
790
|
const backgroundNodes = backgroundLayer(`${bindFrom}.background`, {
|
|
699
791
|
imageClassName
|
|
700
792
|
});
|
|
@@ -835,8 +927,8 @@ var FragmentConfigError = class extends Error {
|
|
|
835
927
|
this.name = "FragmentConfigError";
|
|
836
928
|
}
|
|
837
929
|
};
|
|
838
|
-
function defineFragment(
|
|
839
|
-
const parsed = fragmentConfigSchema.parse(
|
|
930
|
+
function defineFragment(config3) {
|
|
931
|
+
const parsed = fragmentConfigSchema.parse(config3);
|
|
840
932
|
validateFieldDefinitions(parsed.fields, parsed.id);
|
|
841
933
|
return {
|
|
842
934
|
...parsed,
|
|
@@ -1012,27 +1104,27 @@ function scopePropValue(value, scope) {
|
|
|
1012
1104
|
}
|
|
1013
1105
|
return value;
|
|
1014
1106
|
}
|
|
1015
|
-
function scopeContentPath(
|
|
1107
|
+
function scopeContentPath(path11, scope) {
|
|
1016
1108
|
if (!scope || scope.length === 0) {
|
|
1017
|
-
return
|
|
1109
|
+
return path11;
|
|
1018
1110
|
}
|
|
1019
|
-
if (
|
|
1111
|
+
if (path11 === "content") {
|
|
1020
1112
|
return `content.${scope}`;
|
|
1021
1113
|
}
|
|
1022
|
-
if (
|
|
1023
|
-
const remainder =
|
|
1114
|
+
if (path11.startsWith("content.")) {
|
|
1115
|
+
const remainder = path11.slice("content.".length);
|
|
1024
1116
|
return remainder.length > 0 ? `content.${scope}.${remainder}` : `content.${scope}`;
|
|
1025
1117
|
}
|
|
1026
|
-
if (
|
|
1027
|
-
return
|
|
1118
|
+
if (path11.startsWith("content[")) {
|
|
1119
|
+
return path11.replace(/^content/, `content.${scope}`);
|
|
1028
1120
|
}
|
|
1029
|
-
if (
|
|
1030
|
-
return
|
|
1121
|
+
if (path11.startsWith("$root.")) {
|
|
1122
|
+
return path11;
|
|
1031
1123
|
}
|
|
1032
|
-
if (
|
|
1033
|
-
return
|
|
1124
|
+
if (path11.includes(".")) {
|
|
1125
|
+
return path11;
|
|
1034
1126
|
}
|
|
1035
|
-
return `content.${scope}.${
|
|
1127
|
+
return `content.${scope}.${path11}`;
|
|
1036
1128
|
}
|
|
1037
1129
|
|
|
1038
1130
|
// ../blocks/src/system/fragments/builder.ts
|
|
@@ -1043,26 +1135,26 @@ var FragmentCompositionError = class extends Error {
|
|
|
1043
1135
|
}
|
|
1044
1136
|
};
|
|
1045
1137
|
var DOT_SCOPE_REGEX = /^[A-Za-z0-9_.-]*$/;
|
|
1046
|
-
function materializeFragment(
|
|
1047
|
-
const scope = normalizeScope(
|
|
1048
|
-
const fields4 = scopeFragmentFields(
|
|
1049
|
-
const layout = scopeFragmentLayout(
|
|
1050
|
-
const fieldPriority =
|
|
1138
|
+
function materializeFragment(config3) {
|
|
1139
|
+
const scope = normalizeScope(config3.scope);
|
|
1140
|
+
const fields4 = scopeFragmentFields(config3.fragment, scope);
|
|
1141
|
+
const layout = scopeFragmentLayout(config3.fragment, scope);
|
|
1142
|
+
const fieldPriority = config3.fieldPriority ?? 0;
|
|
1051
1143
|
let data;
|
|
1052
|
-
if (
|
|
1053
|
-
const key =
|
|
1144
|
+
if (config3.fragment.data) {
|
|
1145
|
+
const key = config3.dataKey ?? config3.fragment.data.key;
|
|
1054
1146
|
if (!key || typeof key !== "string") {
|
|
1055
1147
|
throw new FragmentCompositionError(
|
|
1056
|
-
`Fragment "${
|
|
1148
|
+
`Fragment "${config3.fragment.id}" requires a data key to compose.`
|
|
1057
1149
|
);
|
|
1058
1150
|
}
|
|
1059
1151
|
data = {
|
|
1060
1152
|
key,
|
|
1061
|
-
loader:
|
|
1153
|
+
loader: config3.fragment.data.loader ? { ...config3.fragment.data.loader } : void 0
|
|
1062
1154
|
};
|
|
1063
1155
|
}
|
|
1064
1156
|
return {
|
|
1065
|
-
fragment:
|
|
1157
|
+
fragment: config3.fragment,
|
|
1066
1158
|
scope,
|
|
1067
1159
|
fields: fields4,
|
|
1068
1160
|
layout,
|
|
@@ -1185,6 +1277,8 @@ var bodyCopyFragment = defineFragment({
|
|
|
1185
1277
|
text(
|
|
1186
1278
|
{
|
|
1187
1279
|
as: "h2",
|
|
1280
|
+
// h2 now gets size/weight from theme typography CSS
|
|
1281
|
+
// Only dynamic class here is for text alignment
|
|
1188
1282
|
className: {
|
|
1189
1283
|
$bind: {
|
|
1190
1284
|
from: "content.alignment",
|
|
@@ -1192,11 +1286,11 @@ var bodyCopyFragment = defineFragment({
|
|
|
1192
1286
|
{
|
|
1193
1287
|
id: "ui.headingClassFromAlignment",
|
|
1194
1288
|
options: {
|
|
1195
|
-
base: "
|
|
1289
|
+
base: ""
|
|
1196
1290
|
}
|
|
1197
1291
|
}
|
|
1198
1292
|
],
|
|
1199
|
-
fallback: "
|
|
1293
|
+
fallback: ""
|
|
1200
1294
|
}
|
|
1201
1295
|
},
|
|
1202
1296
|
style: textColorStyle("neutral-900")
|
|
@@ -1269,7 +1363,9 @@ var heroCopyFragment = defineFragment({
|
|
|
1269
1363
|
text(
|
|
1270
1364
|
{
|
|
1271
1365
|
as: "h1",
|
|
1272
|
-
|
|
1366
|
+
// heading-display: uses fluid typography from theme (--fs-h1-display-fluid)
|
|
1367
|
+
// Removes hardcoded text-4xl/5xl/6xl - size now controlled by theme
|
|
1368
|
+
className: "hero-headline heading-display",
|
|
1273
1369
|
style: textColorStyle("neutral-900")
|
|
1274
1370
|
},
|
|
1275
1371
|
bind("content.headline")
|
|
@@ -1448,7 +1544,8 @@ var testimonialsHeadingFragment = defineFragment({
|
|
|
1448
1544
|
{ gap: "md", className: "mx-auto max-w-2xl text-center" },
|
|
1449
1545
|
[
|
|
1450
1546
|
text(
|
|
1451
|
-
|
|
1547
|
+
// h2 now gets size/weight from theme typography CSS
|
|
1548
|
+
{ as: "h2", style: textColorStyle("neutral-900") },
|
|
1452
1549
|
when("content.heading"),
|
|
1453
1550
|
bind("content.heading")
|
|
1454
1551
|
),
|
|
@@ -1820,7 +1917,7 @@ var blogFeaturedPostFragment = defineFragment({
|
|
|
1820
1917
|
text(
|
|
1821
1918
|
{
|
|
1822
1919
|
as: "h2",
|
|
1823
|
-
|
|
1920
|
+
// h2 now gets size/weight from theme typography CSS
|
|
1824
1921
|
style: textColorStyle("neutral-900")
|
|
1825
1922
|
},
|
|
1826
1923
|
bind("title", { fallback: "Latest post" })
|
|
@@ -2217,7 +2314,8 @@ var faqHeadingFragment = defineFragment({
|
|
|
2217
2314
|
text(
|
|
2218
2315
|
{
|
|
2219
2316
|
as: "h2",
|
|
2220
|
-
|
|
2317
|
+
// h2 now gets size/weight from theme typography CSS
|
|
2318
|
+
className: "faq-title",
|
|
2221
2319
|
style: textColorStyle("neutral-900")
|
|
2222
2320
|
},
|
|
2223
2321
|
when("content.title"),
|
|
@@ -2846,13 +2944,13 @@ function sectionStylesField(options = {}) {
|
|
|
2846
2944
|
}
|
|
2847
2945
|
|
|
2848
2946
|
// ../blocks/src/system/defineBlock.ts
|
|
2849
|
-
function createBlockManifest(
|
|
2850
|
-
const composition2 =
|
|
2947
|
+
function createBlockManifest(config3) {
|
|
2948
|
+
const composition2 = config3.fragments ? composeFragments(config3.fragments) : { fields: []};
|
|
2851
2949
|
const allFields = [
|
|
2852
2950
|
...composition2.fields,
|
|
2853
|
-
...
|
|
2951
|
+
...config3.additionalFields ?? []
|
|
2854
2952
|
];
|
|
2855
|
-
if (!
|
|
2953
|
+
if (!config3.skipSectionStyles) {
|
|
2856
2954
|
allFields.push(
|
|
2857
2955
|
sectionStylesField({
|
|
2858
2956
|
id: "_sectionStyles",
|
|
@@ -2861,36 +2959,36 @@ function createBlockManifest(config2) {
|
|
|
2861
2959
|
);
|
|
2862
2960
|
}
|
|
2863
2961
|
const fields4 = fieldSchema.array().parse(allFields);
|
|
2864
|
-
const layout =
|
|
2865
|
-
const variants =
|
|
2866
|
-
let behaviours =
|
|
2867
|
-
if (!behaviours &&
|
|
2962
|
+
const layout = config3.layout;
|
|
2963
|
+
const variants = config3.variants;
|
|
2964
|
+
let behaviours = config3.behaviours;
|
|
2965
|
+
if (!behaviours && config3.paletteHidden !== void 0) {
|
|
2868
2966
|
behaviours = {
|
|
2869
2967
|
supportsThemeSwitching: true,
|
|
2870
2968
|
inlineEditing: true,
|
|
2871
2969
|
animation: true,
|
|
2872
|
-
paletteHidden:
|
|
2970
|
+
paletteHidden: config3.paletteHidden
|
|
2873
2971
|
};
|
|
2874
2972
|
}
|
|
2875
2973
|
const manifest = {
|
|
2876
|
-
name:
|
|
2974
|
+
name: config3.id,
|
|
2877
2975
|
version: "0.1.0",
|
|
2878
|
-
title:
|
|
2879
|
-
titleSource:
|
|
2880
|
-
description:
|
|
2881
|
-
component:
|
|
2976
|
+
title: config3.title,
|
|
2977
|
+
titleSource: config3.titleSource,
|
|
2978
|
+
description: config3.description ?? "",
|
|
2979
|
+
component: config3.component ?? deriveComponentName(config3.id),
|
|
2882
2980
|
fields: fields4,
|
|
2883
2981
|
slots: [],
|
|
2884
2982
|
// Always empty
|
|
2885
|
-
styleTokens:
|
|
2983
|
+
styleTokens: config3.styleTokens,
|
|
2886
2984
|
behaviours,
|
|
2887
|
-
category:
|
|
2888
|
-
contentTypes:
|
|
2889
|
-
tags:
|
|
2890
|
-
icon:
|
|
2985
|
+
category: config3.category,
|
|
2986
|
+
contentTypes: config3.contentTypes,
|
|
2987
|
+
tags: config3.tags ?? [],
|
|
2988
|
+
icon: config3.icon ?? "Box",
|
|
2891
2989
|
layout,
|
|
2892
2990
|
variants,
|
|
2893
|
-
defaultVariant:
|
|
2991
|
+
defaultVariant: config3.defaultVariant
|
|
2894
2992
|
};
|
|
2895
2993
|
return augmentManifest(manifest);
|
|
2896
2994
|
}
|
|
@@ -3591,7 +3689,11 @@ var simpleFooterLayout = stack(
|
|
|
3591
3689
|
}
|
|
3592
3690
|
},
|
|
3593
3691
|
[
|
|
3594
|
-
navRow({
|
|
3692
|
+
navRow({
|
|
3693
|
+
align: "center",
|
|
3694
|
+
className: "flex flex-wrap justify-center gap-x-6 gap-y-3",
|
|
3695
|
+
linkClassName: "footer-nav-link inline-flex items-center px-4 py-2 text-sm font-medium transition-theme-standard"
|
|
3696
|
+
}),
|
|
3595
3697
|
...bottomTextLayout()
|
|
3596
3698
|
],
|
|
3597
3699
|
when("$root.theme.footer.variant", { equals: "simple" })
|
|
@@ -3619,7 +3721,11 @@ var columnsFooterLayout = stack(
|
|
|
3619
3721
|
{ className: "flex w-full flex-wrap items-center justify-between gap-4" },
|
|
3620
3722
|
[
|
|
3621
3723
|
text({ as: "span", className: "text-sm font-semibold", style: textColorStyle("text") }, bind("site.title", { fallback: "Your Site" })),
|
|
3622
|
-
navRow({
|
|
3724
|
+
navRow({
|
|
3725
|
+
className: "flex flex-wrap justify-end gap-x-6 gap-y-3",
|
|
3726
|
+
align: "end",
|
|
3727
|
+
linkClassName: "footer-nav-link inline-flex items-center px-4 py-2 text-sm font-medium transition-theme-standard"
|
|
3728
|
+
})
|
|
3623
3729
|
]
|
|
3624
3730
|
),
|
|
3625
3731
|
...bottomTextLayout()
|
|
@@ -4840,6 +4946,10 @@ function getBlockDefinition(name) {
|
|
|
4840
4946
|
ensureDefaults();
|
|
4841
4947
|
return blockStore.get(name);
|
|
4842
4948
|
}
|
|
4949
|
+
function listBlockDefinitions() {
|
|
4950
|
+
ensureDefaults();
|
|
4951
|
+
return Array.from(blockStore.values());
|
|
4952
|
+
}
|
|
4843
4953
|
|
|
4844
4954
|
// ../blocks/src/system/constants/blockKinds.ts
|
|
4845
4955
|
var SYSTEM_BLOCK_KINDS = [
|
|
@@ -5021,7 +5131,7 @@ var contentConfigSchema = contentConfigBaseSchema.superRefine((data, ctx) => {
|
|
|
5021
5131
|
});
|
|
5022
5132
|
}
|
|
5023
5133
|
const paths = data.pages.map((p) => p.path);
|
|
5024
|
-
const duplicatePaths = paths.filter((
|
|
5134
|
+
const duplicatePaths = paths.filter((path11, i) => paths.indexOf(path11) !== i);
|
|
5025
5135
|
if (duplicatePaths.length > 0) {
|
|
5026
5136
|
ctx.addIssue({
|
|
5027
5137
|
code: zod.z.ZodIssueCode.custom,
|
|
@@ -5083,6 +5193,15 @@ var sdkThemePaletteSchema = zod.z.record(zod.z.string(), zod.z.string());
|
|
|
5083
5193
|
var sdkThemeConfigSchema = zod.z.object({
|
|
5084
5194
|
palette: sdkThemePaletteSchema
|
|
5085
5195
|
});
|
|
5196
|
+
var sdkSiteUrlSchema = zod.z.string().url("Must be a valid URL").refine((url) => {
|
|
5197
|
+
try {
|
|
5198
|
+
const parsed = new URL(url);
|
|
5199
|
+
const isLocal = ["localhost", "lvh.me"].includes(parsed.hostname) || parsed.hostname.endsWith(".local") || parsed.hostname.endsWith(".test") || parsed.hostname.endsWith(".lvh.me");
|
|
5200
|
+
return parsed.protocol === "https:" || isLocal;
|
|
5201
|
+
} catch {
|
|
5202
|
+
return false;
|
|
5203
|
+
}
|
|
5204
|
+
}, { message: "Production URLs must use HTTPS" }).transform((url) => url.replace(/\/$/, ""));
|
|
5086
5205
|
var sectionBackgroundSchema = zod.z.object({
|
|
5087
5206
|
id: zod.z.string(),
|
|
5088
5207
|
label: zod.z.string(),
|
|
@@ -5216,6 +5335,10 @@ function validateFieldIdConflicts(blockFieldExtensions) {
|
|
|
5216
5335
|
}
|
|
5217
5336
|
return conflicts;
|
|
5218
5337
|
}
|
|
5338
|
+
var syncConfigSchema = zod.z.object({
|
|
5339
|
+
existingEntries: zod.z.enum(["skip", "update"]).optional(),
|
|
5340
|
+
contentTarget: zod.z.enum(["draft", "publish"]).optional()
|
|
5341
|
+
}).optional();
|
|
5219
5342
|
var sdkCustomBlockSchema = zod.z.object({
|
|
5220
5343
|
// Block ID must start with 'custom.'
|
|
5221
5344
|
id: zod.z.string().min(8).regex(/^custom\.[a-z][a-z0-9-]*$/, {
|
|
@@ -5244,6 +5367,8 @@ var sdkCustomBlockSchema = zod.z.object({
|
|
|
5244
5367
|
);
|
|
5245
5368
|
var riverbankSiteConfigSchema = zod.z.object({
|
|
5246
5369
|
siteId: zod.z.string().uuid(),
|
|
5370
|
+
previewUrl: sdkSiteUrlSchema.optional(),
|
|
5371
|
+
liveUrl: sdkSiteUrlSchema.optional(),
|
|
5247
5372
|
theme: sdkThemeConfigSchema.optional(),
|
|
5248
5373
|
styles: siteStyleConfigSchema,
|
|
5249
5374
|
customBlocks: zod.z.array(sdkCustomBlockSchema).max(20, "Maximum 20 custom blocks per site").refine(
|
|
@@ -5256,50 +5381,15 @@ var riverbankSiteConfigSchema = zod.z.object({
|
|
|
5256
5381
|
).optional(),
|
|
5257
5382
|
blockFieldOptions: blockFieldOptionsSchema,
|
|
5258
5383
|
blockFieldExtensions: blockFieldExtensionsSchema,
|
|
5259
|
-
content: contentConfigSchema.optional()
|
|
5384
|
+
content: contentConfigSchema.optional(),
|
|
5385
|
+
// CLI-related configuration (Phase 4)
|
|
5386
|
+
contentDir: zod.z.string().optional(),
|
|
5387
|
+
sync: syncConfigSchema
|
|
5260
5388
|
}).strict();
|
|
5261
|
-
var DEFAULT_CONFIG_FILENAME = "riverbank.config.ts";
|
|
5262
|
-
async function loadConfigFile(configPath) {
|
|
5263
|
-
const resolvedPath = resolveConfigPath(configPath);
|
|
5264
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
5265
|
-
throw new Error(
|
|
5266
|
-
`Config file not found: ${resolvedPath}
|
|
5267
|
-
Create a riverbank.config.ts file or specify a path with --config`
|
|
5268
|
-
);
|
|
5269
|
-
}
|
|
5270
|
-
console.log(`Loading config from ${resolvedPath}...`);
|
|
5271
|
-
const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)), {
|
|
5272
|
-
// Disable caching for CLI tool
|
|
5273
|
-
fsCache: false,
|
|
5274
|
-
moduleCache: false
|
|
5275
|
-
});
|
|
5276
|
-
try {
|
|
5277
|
-
const configModule = await jiti$1.import(resolvedPath);
|
|
5278
|
-
const config2 = configModule.default ?? configModule.config ?? configModule;
|
|
5279
|
-
if (!config2 || typeof config2 !== "object") {
|
|
5280
|
-
throw new Error("Config file must export a configuration object");
|
|
5281
|
-
}
|
|
5282
|
-
return config2;
|
|
5283
|
-
} catch (error) {
|
|
5284
|
-
if (error instanceof Error) {
|
|
5285
|
-
throw new Error(`Failed to load config: ${error.message}`);
|
|
5286
|
-
}
|
|
5287
|
-
throw error;
|
|
5288
|
-
}
|
|
5289
|
-
}
|
|
5290
|
-
function resolveConfigPath(configPath) {
|
|
5291
|
-
if (!configPath) {
|
|
5292
|
-
return path.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);
|
|
5293
|
-
}
|
|
5294
|
-
const resolved = path.resolve(configPath);
|
|
5295
|
-
if (fs.existsSync(resolved) && !resolved.endsWith(".ts") && !resolved.endsWith(".js")) {
|
|
5296
|
-
return path.resolve(resolved, DEFAULT_CONFIG_FILENAME);
|
|
5297
|
-
}
|
|
5298
|
-
return resolved;
|
|
5299
|
-
}
|
|
5300
5389
|
|
|
5301
5390
|
// src/cli/push-config.ts
|
|
5302
|
-
|
|
5391
|
+
init_load_config();
|
|
5392
|
+
async function pushToDashboard(dashboardUrl, siteId, apiKey, config3) {
|
|
5303
5393
|
const pushUrl = `${dashboardUrl}/api/sites/${siteId}/sdk-config`;
|
|
5304
5394
|
console.log(`Pushing config to ${pushUrl}...`);
|
|
5305
5395
|
let response;
|
|
@@ -5310,7 +5400,7 @@ async function pushToDashboard(dashboardUrl, siteId, apiKey, config2) {
|
|
|
5310
5400
|
"Content-Type": "application/json",
|
|
5311
5401
|
"X-API-Key": apiKey
|
|
5312
5402
|
},
|
|
5313
|
-
body: JSON.stringify({ config:
|
|
5403
|
+
body: JSON.stringify({ config: config3 }),
|
|
5314
5404
|
signal: AbortSignal.timeout(3e4)
|
|
5315
5405
|
});
|
|
5316
5406
|
} catch (error) {
|
|
@@ -5390,19 +5480,2913 @@ Examples:
|
|
|
5390
5480
|
const dashboard = resolveDashboardUrl(options.dashboard);
|
|
5391
5481
|
return pushConfigAction({ ...options, dashboard });
|
|
5392
5482
|
});
|
|
5483
|
+
async function ensureDir(dirPath) {
|
|
5484
|
+
try {
|
|
5485
|
+
await fs5__namespace.mkdir(dirPath, { recursive: true });
|
|
5486
|
+
} catch (error) {
|
|
5487
|
+
if (error.code !== "EEXIST") {
|
|
5488
|
+
throw error;
|
|
5489
|
+
}
|
|
5490
|
+
}
|
|
5491
|
+
}
|
|
5492
|
+
async function writeJsonFile(filePath, data) {
|
|
5493
|
+
const content = JSON.stringify(data, null, 2) + "\n";
|
|
5494
|
+
await fs5__namespace.writeFile(filePath, content, "utf-8");
|
|
5495
|
+
}
|
|
5496
|
+
async function writeEntries(contentDir, pulledEntries) {
|
|
5497
|
+
const entriesDir = path9__namespace.join(contentDir, "entries");
|
|
5498
|
+
const metaDir = path9__namespace.join(contentDir, ".meta");
|
|
5499
|
+
await ensureDir(entriesDir);
|
|
5500
|
+
await ensureDir(metaDir);
|
|
5501
|
+
const { contentType, entries, meta } = pulledEntries;
|
|
5502
|
+
const entriesFile = {
|
|
5503
|
+
contentType,
|
|
5504
|
+
entries: entries.map((entry) => ({
|
|
5505
|
+
identifier: entry.identifier,
|
|
5506
|
+
data: entry.data,
|
|
5507
|
+
status: entry.status,
|
|
5508
|
+
hasUnpublishedChanges: entry.hasUnpublishedChanges,
|
|
5509
|
+
slug: entry.slug
|
|
5510
|
+
}))
|
|
5511
|
+
};
|
|
5512
|
+
const filePath = path9__namespace.join(entriesDir, `${contentType}.json`);
|
|
5513
|
+
await writeJsonFile(filePath, entriesFile);
|
|
5514
|
+
const metaPath = path9__namespace.join(metaDir, `${contentType}.json`);
|
|
5515
|
+
await writeJsonFile(metaPath, meta);
|
|
5516
|
+
return { filePath, metaPath };
|
|
5517
|
+
}
|
|
5518
|
+
async function writePages(contentDir, pulledPages) {
|
|
5519
|
+
const pagesDir = path9__namespace.join(contentDir, "pages");
|
|
5520
|
+
await ensureDir(pagesDir);
|
|
5521
|
+
const filePaths = [];
|
|
5522
|
+
for (const page of pulledPages.pages) {
|
|
5523
|
+
const filePath = path9__namespace.join(pagesDir, `${page.identifier}.json`);
|
|
5524
|
+
await writeJsonFile(filePath, page);
|
|
5525
|
+
filePaths.push(filePath);
|
|
5526
|
+
}
|
|
5527
|
+
return filePaths;
|
|
5528
|
+
}
|
|
5529
|
+
async function writeNavigation(contentDir, pulledNavigation) {
|
|
5530
|
+
await ensureDir(contentDir);
|
|
5531
|
+
const filePath = path9__namespace.join(contentDir, "navigation.json");
|
|
5532
|
+
await writeJsonFile(filePath, {
|
|
5533
|
+
menus: pulledNavigation.menus
|
|
5534
|
+
});
|
|
5535
|
+
return filePath;
|
|
5536
|
+
}
|
|
5537
|
+
async function writeSettings(contentDir, pulledSettings) {
|
|
5538
|
+
await ensureDir(contentDir);
|
|
5539
|
+
const filePath = path9__namespace.join(contentDir, "settings.json");
|
|
5540
|
+
await writeJsonFile(filePath, pulledSettings.settings);
|
|
5541
|
+
return filePath;
|
|
5542
|
+
}
|
|
5393
5543
|
|
|
5394
|
-
// src/
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5544
|
+
// src/client/management/http.ts
|
|
5545
|
+
var ManagementApiError = class extends Error {
|
|
5546
|
+
constructor(message, code, details, statusCode) {
|
|
5547
|
+
super(message);
|
|
5548
|
+
this.name = "ManagementApiError";
|
|
5549
|
+
this.code = code;
|
|
5550
|
+
this.details = details;
|
|
5551
|
+
this.statusCode = statusCode;
|
|
5552
|
+
}
|
|
5553
|
+
};
|
|
5554
|
+
function createHttpClient(config3) {
|
|
5555
|
+
const baseUrl = `${config3.dashboardUrl}/api/sdk/${config3.siteId}`;
|
|
5556
|
+
const timeout = config3.timeout ?? 3e4;
|
|
5557
|
+
async function request(method, path11, options) {
|
|
5558
|
+
let url = `${baseUrl}${path11}`;
|
|
5559
|
+
if (options?.params && Object.keys(options.params).length > 0) {
|
|
5560
|
+
const searchParams = new URLSearchParams(options.params);
|
|
5561
|
+
url = `${url}?${searchParams.toString()}`;
|
|
5562
|
+
}
|
|
5563
|
+
const headers = {
|
|
5564
|
+
"Authorization": `Bearer ${config3.managementApiKey}`,
|
|
5565
|
+
"Content-Type": "application/json"
|
|
5566
|
+
};
|
|
5567
|
+
const fetchOptions = {
|
|
5568
|
+
method,
|
|
5569
|
+
headers,
|
|
5570
|
+
signal: AbortSignal.timeout(timeout)
|
|
5571
|
+
};
|
|
5572
|
+
if (options?.body && method !== "GET") {
|
|
5573
|
+
fetchOptions.body = JSON.stringify(options.body);
|
|
5574
|
+
}
|
|
5575
|
+
let response;
|
|
5576
|
+
try {
|
|
5577
|
+
response = await fetch(url, fetchOptions);
|
|
5578
|
+
} catch (error) {
|
|
5579
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
5580
|
+
throw new ManagementApiError(
|
|
5581
|
+
`Request timed out after ${timeout}ms`,
|
|
5582
|
+
"sdk:timeout",
|
|
5583
|
+
void 0,
|
|
5584
|
+
void 0
|
|
5585
|
+
);
|
|
5586
|
+
}
|
|
5587
|
+
throw new ManagementApiError(
|
|
5588
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
5589
|
+
"sdk:network-error",
|
|
5590
|
+
void 0,
|
|
5591
|
+
void 0
|
|
5592
|
+
);
|
|
5593
|
+
}
|
|
5594
|
+
const contentType = response.headers.get("content-type");
|
|
5595
|
+
if (!contentType?.includes("application/json")) {
|
|
5596
|
+
if (!response.ok) {
|
|
5597
|
+
throw new ManagementApiError(
|
|
5598
|
+
`Request failed with status ${response.status}`,
|
|
5599
|
+
"sdk:http-error",
|
|
5600
|
+
void 0,
|
|
5601
|
+
response.status
|
|
5602
|
+
);
|
|
5603
|
+
}
|
|
5604
|
+
return void 0;
|
|
5605
|
+
}
|
|
5606
|
+
const json = await response.json();
|
|
5607
|
+
if (!response.ok || !json.success) {
|
|
5608
|
+
if ("error" in json && json.error) {
|
|
5609
|
+
throw new ManagementApiError(
|
|
5610
|
+
json.error.message,
|
|
5611
|
+
json.error.code,
|
|
5612
|
+
json.error.details,
|
|
5613
|
+
response.status
|
|
5614
|
+
);
|
|
5615
|
+
}
|
|
5616
|
+
throw new ManagementApiError(
|
|
5617
|
+
`Request failed with status ${response.status}`,
|
|
5618
|
+
"sdk:http-error",
|
|
5619
|
+
void 0,
|
|
5620
|
+
response.status
|
|
5621
|
+
);
|
|
5622
|
+
}
|
|
5623
|
+
return json.data;
|
|
5624
|
+
}
|
|
5625
|
+
return {
|
|
5626
|
+
async get(path11, params) {
|
|
5627
|
+
return request("GET", path11, { params });
|
|
5628
|
+
},
|
|
5629
|
+
async post(path11, body) {
|
|
5630
|
+
return request("POST", path11, { body });
|
|
5631
|
+
},
|
|
5632
|
+
async patch(path11, body) {
|
|
5633
|
+
return request("PATCH", path11, { body });
|
|
5634
|
+
},
|
|
5635
|
+
async delete(path11, body) {
|
|
5636
|
+
await request("DELETE", path11, { body });
|
|
5637
|
+
}
|
|
5638
|
+
};
|
|
5639
|
+
}
|
|
5640
|
+
|
|
5641
|
+
// src/client/management/entries.ts
|
|
5642
|
+
function createEntryOperations(http) {
|
|
5643
|
+
return {
|
|
5644
|
+
async list(contentType, options) {
|
|
5645
|
+
const params = {};
|
|
5646
|
+
if (options?.page) params.page = String(options.page);
|
|
5647
|
+
if (options?.limit) params.limit = String(options.limit);
|
|
5648
|
+
return http.get(
|
|
5649
|
+
`/entries/${encodeURIComponent(contentType)}`,
|
|
5650
|
+
params
|
|
5651
|
+
);
|
|
5652
|
+
},
|
|
5653
|
+
async get(contentType, identifier) {
|
|
5654
|
+
try {
|
|
5655
|
+
return await http.get(
|
|
5656
|
+
`/entries/${encodeURIComponent(contentType)}/${encodeURIComponent(identifier)}`
|
|
5657
|
+
);
|
|
5658
|
+
} catch (error) {
|
|
5659
|
+
if (error instanceof Error && "statusCode" in error && error.statusCode === 404) {
|
|
5660
|
+
return null;
|
|
5661
|
+
}
|
|
5662
|
+
throw error;
|
|
5663
|
+
}
|
|
5664
|
+
},
|
|
5665
|
+
async upsert(input) {
|
|
5666
|
+
return http.post("/entries", input);
|
|
5667
|
+
},
|
|
5668
|
+
async publish(contentType, identifier) {
|
|
5669
|
+
await http.post(
|
|
5670
|
+
`/entries/${encodeURIComponent(contentType)}/${encodeURIComponent(identifier)}/publish`
|
|
5671
|
+
);
|
|
5672
|
+
},
|
|
5673
|
+
async unpublish(contentType, identifier) {
|
|
5674
|
+
await http.post(
|
|
5675
|
+
`/entries/${encodeURIComponent(contentType)}/${encodeURIComponent(identifier)}/unpublish`
|
|
5676
|
+
);
|
|
5677
|
+
},
|
|
5678
|
+
async delete(contentType, identifier) {
|
|
5679
|
+
await http.delete(
|
|
5680
|
+
`/entries/${encodeURIComponent(contentType)}/${encodeURIComponent(identifier)}`
|
|
5681
|
+
);
|
|
5682
|
+
}
|
|
5683
|
+
};
|
|
5684
|
+
}
|
|
5685
|
+
|
|
5686
|
+
// src/client/management/pages.ts
|
|
5687
|
+
function createPageOperations(http) {
|
|
5688
|
+
return {
|
|
5689
|
+
async list(options) {
|
|
5690
|
+
const params = {};
|
|
5691
|
+
if (options?.page) params.page = String(options.page);
|
|
5692
|
+
if (options?.limit) params.limit = String(options.limit);
|
|
5693
|
+
return http.get("/pages", params);
|
|
5694
|
+
},
|
|
5695
|
+
async get(identifier) {
|
|
5696
|
+
try {
|
|
5697
|
+
return await http.get(
|
|
5698
|
+
`/pages/${encodeURIComponent(identifier)}`
|
|
5699
|
+
);
|
|
5700
|
+
} catch (error) {
|
|
5701
|
+
if (error instanceof Error && "statusCode" in error && error.statusCode === 404) {
|
|
5702
|
+
return null;
|
|
5703
|
+
}
|
|
5704
|
+
throw error;
|
|
5705
|
+
}
|
|
5706
|
+
},
|
|
5707
|
+
async upsert(input) {
|
|
5708
|
+
return http.post("/pages", input);
|
|
5709
|
+
},
|
|
5710
|
+
async publish(identifier) {
|
|
5711
|
+
await http.post(`/pages/${encodeURIComponent(identifier)}/publish`);
|
|
5712
|
+
},
|
|
5713
|
+
async unpublish(identifier) {
|
|
5714
|
+
await http.post(`/pages/${encodeURIComponent(identifier)}/unpublish`);
|
|
5715
|
+
}
|
|
5716
|
+
};
|
|
5717
|
+
}
|
|
5718
|
+
|
|
5719
|
+
// src/client/management/blocks.ts
|
|
5720
|
+
function createBlockOperations(http) {
|
|
5721
|
+
return {
|
|
5722
|
+
async list(pageIdentifier) {
|
|
5723
|
+
const result = await http.get(
|
|
5724
|
+
`/pages/${encodeURIComponent(pageIdentifier)}/blocks`
|
|
5725
|
+
);
|
|
5726
|
+
return result.blocks;
|
|
5727
|
+
},
|
|
5728
|
+
async get(pageIdentifier, blockIdentifier) {
|
|
5729
|
+
try {
|
|
5730
|
+
return await http.get(
|
|
5731
|
+
`/pages/${encodeURIComponent(pageIdentifier)}/blocks/${encodeURIComponent(blockIdentifier)}`
|
|
5732
|
+
);
|
|
5733
|
+
} catch (error) {
|
|
5734
|
+
if (error instanceof Error && "statusCode" in error && error.statusCode === 404) {
|
|
5735
|
+
return null;
|
|
5736
|
+
}
|
|
5737
|
+
throw error;
|
|
5738
|
+
}
|
|
5739
|
+
},
|
|
5740
|
+
async upsert(pageIdentifier, input) {
|
|
5741
|
+
return http.post(
|
|
5742
|
+
`/pages/${encodeURIComponent(pageIdentifier)}/blocks`,
|
|
5743
|
+
input
|
|
5744
|
+
);
|
|
5745
|
+
},
|
|
5746
|
+
async reorder(pageIdentifier, blockIdentifiers) {
|
|
5747
|
+
await http.post(
|
|
5748
|
+
`/pages/${encodeURIComponent(pageIdentifier)}/blocks/reorder`,
|
|
5749
|
+
{ order: blockIdentifiers }
|
|
5750
|
+
);
|
|
5751
|
+
},
|
|
5752
|
+
async delete(pageIdentifier, blockIdentifier) {
|
|
5753
|
+
await http.delete(
|
|
5754
|
+
`/pages/${encodeURIComponent(pageIdentifier)}/blocks/${encodeURIComponent(blockIdentifier)}`
|
|
5755
|
+
);
|
|
5756
|
+
}
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
5759
|
+
|
|
5760
|
+
// src/client/management/navigation.ts
|
|
5761
|
+
function createNavigationOperations(http) {
|
|
5762
|
+
return {
|
|
5763
|
+
async list() {
|
|
5764
|
+
const result = await http.get("/navigation");
|
|
5765
|
+
return result.menus;
|
|
5766
|
+
},
|
|
5767
|
+
async get(name) {
|
|
5768
|
+
try {
|
|
5769
|
+
return await http.get(
|
|
5770
|
+
`/navigation/${encodeURIComponent(name)}`
|
|
5771
|
+
);
|
|
5772
|
+
} catch (error) {
|
|
5773
|
+
if (error instanceof Error && "statusCode" in error && error.statusCode === 404) {
|
|
5774
|
+
return null;
|
|
5775
|
+
}
|
|
5776
|
+
throw error;
|
|
5777
|
+
}
|
|
5778
|
+
},
|
|
5779
|
+
async upsert(input) {
|
|
5780
|
+
return http.post("/navigation", input);
|
|
5781
|
+
}
|
|
5782
|
+
};
|
|
5783
|
+
}
|
|
5784
|
+
|
|
5785
|
+
// src/client/management/settings.ts
|
|
5786
|
+
function createSettingsOperations(http) {
|
|
5787
|
+
return {
|
|
5788
|
+
async get() {
|
|
5789
|
+
const result = await http.get("/pull/settings");
|
|
5790
|
+
return result.settings;
|
|
5791
|
+
},
|
|
5792
|
+
async update(input) {
|
|
5793
|
+
const result = await http.patch("/settings", input);
|
|
5794
|
+
return result.settings;
|
|
5795
|
+
}
|
|
5796
|
+
};
|
|
5797
|
+
}
|
|
5798
|
+
|
|
5799
|
+
// src/client/management/pull.ts
|
|
5800
|
+
function createPullOperations(http) {
|
|
5801
|
+
return {
|
|
5802
|
+
async entries(contentType, options) {
|
|
5803
|
+
const params = {};
|
|
5804
|
+
if (options?.page) params.page = String(options.page);
|
|
5805
|
+
if (options?.limit) params.limit = String(options.limit);
|
|
5806
|
+
if (contentType) {
|
|
5807
|
+
return http.get(
|
|
5808
|
+
`/pull/entries/${encodeURIComponent(contentType)}`,
|
|
5809
|
+
params
|
|
5810
|
+
);
|
|
5811
|
+
}
|
|
5812
|
+
return http.get("/pull/entries", params);
|
|
5813
|
+
},
|
|
5814
|
+
async pages() {
|
|
5815
|
+
return http.get("/pull/pages");
|
|
5816
|
+
},
|
|
5817
|
+
async navigation() {
|
|
5818
|
+
return http.get("/pull/navigation");
|
|
5819
|
+
},
|
|
5820
|
+
async settings() {
|
|
5821
|
+
return http.get("/pull/settings");
|
|
5822
|
+
},
|
|
5823
|
+
async all() {
|
|
5824
|
+
const [entriesResult, pagesResult, navigationResult, settingsResult] = await Promise.all([
|
|
5825
|
+
http.get("/pull/entries/all"),
|
|
5826
|
+
http.get("/pull/pages"),
|
|
5827
|
+
http.get("/pull/navigation"),
|
|
5828
|
+
http.get("/pull/settings")
|
|
5829
|
+
]);
|
|
5830
|
+
return {
|
|
5831
|
+
entries: entriesResult.entries,
|
|
5832
|
+
pages: pagesResult.pages,
|
|
5833
|
+
navigation: navigationResult.menus,
|
|
5834
|
+
settings: settingsResult.settings,
|
|
5835
|
+
meta: {
|
|
5836
|
+
pulledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5837
|
+
}
|
|
5838
|
+
};
|
|
5839
|
+
}
|
|
5840
|
+
};
|
|
5841
|
+
}
|
|
5842
|
+
|
|
5843
|
+
// src/client/management/preview.ts
|
|
5844
|
+
function createPreviewOperations(http) {
|
|
5845
|
+
return {
|
|
5846
|
+
async block(input) {
|
|
5847
|
+
return http.post("/preview/block", input);
|
|
5848
|
+
}
|
|
5849
|
+
};
|
|
5850
|
+
}
|
|
5851
|
+
|
|
5852
|
+
// src/client/management/index.ts
|
|
5853
|
+
function createManagementClient(config3) {
|
|
5854
|
+
if (!config3.dashboardUrl) {
|
|
5855
|
+
throw new Error(
|
|
5856
|
+
"dashboardUrl is required when creating a management client. Example: http://localhost:4000 or https://dashboard.riverbankcms.com"
|
|
5857
|
+
);
|
|
5858
|
+
}
|
|
5859
|
+
if (!config3.managementApiKey) {
|
|
5860
|
+
throw new Error(
|
|
5861
|
+
"managementApiKey is required when creating a management client. A management API key starts with bld_mgmt_sk_"
|
|
5862
|
+
);
|
|
5863
|
+
}
|
|
5864
|
+
if (!config3.managementApiKey.startsWith("bld_mgmt_sk_")) {
|
|
5865
|
+
throw new Error(
|
|
5866
|
+
"Invalid management API key format. A management API key must start with bld_mgmt_sk_"
|
|
5867
|
+
);
|
|
5868
|
+
}
|
|
5869
|
+
if (!config3.siteId) {
|
|
5870
|
+
throw new Error(
|
|
5871
|
+
"siteId is required when creating a management client."
|
|
5872
|
+
);
|
|
5873
|
+
}
|
|
5874
|
+
const http = createHttpClient(config3);
|
|
5875
|
+
return {
|
|
5876
|
+
entries: createEntryOperations(http),
|
|
5877
|
+
pages: createPageOperations(http),
|
|
5878
|
+
blocks: createBlockOperations(http),
|
|
5879
|
+
navigation: createNavigationOperations(http),
|
|
5880
|
+
settings: createSettingsOperations(http),
|
|
5881
|
+
pull: createPullOperations(http),
|
|
5882
|
+
preview: createPreviewOperations(http)
|
|
5883
|
+
};
|
|
5884
|
+
}
|
|
5885
|
+
|
|
5886
|
+
// src/cli/env.ts
|
|
5887
|
+
function getEnvPrefix(target) {
|
|
5888
|
+
return target === "remote" ? "RIVERBANK_REMOTE" : "RIVERBANK_LOCAL";
|
|
5889
|
+
}
|
|
5890
|
+
function requireEnv(name) {
|
|
5891
|
+
const value = process.env[name];
|
|
5892
|
+
if (!value) {
|
|
5893
|
+
throw new Error(`Missing required environment variable: ${name}`);
|
|
5894
|
+
}
|
|
5895
|
+
return value;
|
|
5896
|
+
}
|
|
5897
|
+
function loadEnvironment(remote) {
|
|
5898
|
+
const target = remote ? "remote" : "local";
|
|
5899
|
+
const prefix = getEnvPrefix(target);
|
|
5900
|
+
const siteId = requireEnv(`${prefix}_SITE_ID`);
|
|
5901
|
+
const dashboardUrl = requireEnv(`${prefix}_DASHBOARD_URL`);
|
|
5902
|
+
const managementApiKey = requireEnv(`${prefix}_MGMT_API_KEY`);
|
|
5903
|
+
if (!managementApiKey.startsWith("bld_mgmt_sk_")) {
|
|
5904
|
+
throw new Error(
|
|
5905
|
+
`Invalid management API key format for ${prefix}_MGMT_API_KEY. Expected key starting with bld_mgmt_sk_`
|
|
5906
|
+
);
|
|
5907
|
+
}
|
|
5908
|
+
try {
|
|
5909
|
+
new URL(dashboardUrl);
|
|
5910
|
+
} catch {
|
|
5911
|
+
throw new Error(
|
|
5912
|
+
`Invalid dashboard URL in ${prefix}_DASHBOARD_URL: ${dashboardUrl}. Expected format: http://localhost:4000 or https://dashboard.example.com`
|
|
5913
|
+
);
|
|
5914
|
+
}
|
|
5915
|
+
return {
|
|
5916
|
+
siteId,
|
|
5917
|
+
dashboardUrl,
|
|
5918
|
+
managementApiKey
|
|
5919
|
+
};
|
|
5920
|
+
}
|
|
5921
|
+
|
|
5922
|
+
// src/cli/output.ts
|
|
5923
|
+
function createOutput(options) {
|
|
5924
|
+
const { json: jsonMode = false, quiet = false } = options;
|
|
5925
|
+
return {
|
|
5926
|
+
success(message, data) {
|
|
5927
|
+
if (jsonMode) {
|
|
5928
|
+
console.log(JSON.stringify({ success: true, message, data }, null, 2));
|
|
5929
|
+
} else if (!quiet) {
|
|
5930
|
+
console.log(`\u2713 ${message}`);
|
|
5931
|
+
if (data !== void 0) {
|
|
5932
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
},
|
|
5936
|
+
error(message, details) {
|
|
5937
|
+
if (jsonMode) {
|
|
5938
|
+
console.error(JSON.stringify({ success: false, error: message, details }, null, 2));
|
|
5939
|
+
} else {
|
|
5940
|
+
console.error(`\u2717 ${message}`);
|
|
5941
|
+
if (details !== void 0) {
|
|
5942
|
+
if (typeof details === "object" && details !== null) {
|
|
5943
|
+
console.error(JSON.stringify(details, null, 2));
|
|
5944
|
+
} else {
|
|
5945
|
+
console.error(String(details));
|
|
5946
|
+
}
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
process.exit(1);
|
|
5950
|
+
},
|
|
5951
|
+
warn(message, data) {
|
|
5952
|
+
if (jsonMode) {
|
|
5953
|
+
console.error(JSON.stringify({ warning: message, data }, null, 2));
|
|
5954
|
+
} else if (!quiet) {
|
|
5955
|
+
console.warn(`\u26A0 ${message}`);
|
|
5956
|
+
if (data !== void 0) {
|
|
5957
|
+
console.warn(JSON.stringify(data, null, 2));
|
|
5958
|
+
}
|
|
5959
|
+
}
|
|
5960
|
+
},
|
|
5961
|
+
info(message, data) {
|
|
5962
|
+
if (!jsonMode && !quiet) {
|
|
5963
|
+
console.log(`\u2139 ${message}`);
|
|
5964
|
+
if (data !== void 0) {
|
|
5965
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
},
|
|
5969
|
+
table(headers, rows) {
|
|
5970
|
+
if (jsonMode) {
|
|
5971
|
+
const objects = rows.map((row) => {
|
|
5972
|
+
const obj = {};
|
|
5973
|
+
headers.forEach((header, i) => {
|
|
5974
|
+
obj[header] = row[i] ?? "";
|
|
5975
|
+
});
|
|
5976
|
+
return obj;
|
|
5977
|
+
});
|
|
5978
|
+
console.log(JSON.stringify(objects, null, 2));
|
|
5979
|
+
} else if (!quiet) {
|
|
5980
|
+
const widths = headers.map((h, i) => {
|
|
5981
|
+
const maxRowWidth = Math.max(...rows.map((r) => (r[i] ?? "").length));
|
|
5982
|
+
return Math.max(h.length, maxRowWidth);
|
|
5983
|
+
});
|
|
5984
|
+
const headerRow = headers.map((h, i) => h.padEnd(widths[i])).join(" ");
|
|
5985
|
+
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
|
5986
|
+
console.log(headerRow);
|
|
5987
|
+
console.log(separator);
|
|
5988
|
+
for (const row of rows) {
|
|
5989
|
+
const formattedRow = row.map((cell, i) => (cell ?? "").padEnd(widths[i])).join(" ");
|
|
5990
|
+
console.log(formattedRow);
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
},
|
|
5994
|
+
json(data) {
|
|
5995
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5996
|
+
},
|
|
5997
|
+
progress(current, total, message) {
|
|
5998
|
+
if (!jsonMode && !quiet) {
|
|
5999
|
+
const percent = Math.round(current / total * 100);
|
|
6000
|
+
const bar = "\u2588".repeat(Math.floor(percent / 5)) + "\u2591".repeat(20 - Math.floor(percent / 5));
|
|
6001
|
+
process.stdout.write(`\r${bar} ${percent}% ${message}`);
|
|
6002
|
+
if (current === total) {
|
|
6003
|
+
console.log();
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
6007
|
+
};
|
|
6008
|
+
}
|
|
6009
|
+
|
|
6010
|
+
// src/cli/errors.ts
|
|
6011
|
+
var ErrorCodes = {
|
|
6012
|
+
// Network errors
|
|
6013
|
+
NETWORK_ERROR: "sdk:network-error",
|
|
6014
|
+
// General errors
|
|
6015
|
+
UNKNOWN: "sdk:unknown"
|
|
6016
|
+
};
|
|
6017
|
+
var errorSuggestions = {
|
|
6018
|
+
"sdk:missing-env": "Check your .env.local file and ensure all RIVERBANK_* variables are set.",
|
|
6019
|
+
"sdk:unauthorized": "Verify your management API key is correct and not expired.",
|
|
6020
|
+
"sdk:forbidden": "Ensure your API key has the correct permissions for this operation.",
|
|
6021
|
+
"sdk:missing-content-type": "Run `riverbankcms push-config` to sync your content type definitions.",
|
|
6022
|
+
"sdk:unknown-block-kind": "Run `riverbankcms push-config` to sync your custom block definitions.",
|
|
6023
|
+
"sdk:network-error": "Check your network connection and ensure the dashboard is accessible.",
|
|
6024
|
+
"sdk:timeout": "The request timed out. Try again or check if the dashboard is responding.",
|
|
6025
|
+
"auth:forbidden": "Your API key is not authorized for this site or operation.",
|
|
6026
|
+
"auth:invalid": "Invalid API key. Generate a new management key from the dashboard."
|
|
6027
|
+
};
|
|
6028
|
+
function formatError(error) {
|
|
6029
|
+
if (error instanceof ManagementApiError) {
|
|
6030
|
+
return {
|
|
6031
|
+
code: error.code,
|
|
6032
|
+
message: error.message,
|
|
6033
|
+
details: error.details,
|
|
6034
|
+
suggestion: errorSuggestions[error.code]
|
|
6035
|
+
};
|
|
6036
|
+
}
|
|
6037
|
+
if (error instanceof Error) {
|
|
6038
|
+
if (error.message.includes("ECONNREFUSED")) {
|
|
6039
|
+
return {
|
|
6040
|
+
code: ErrorCodes.NETWORK_ERROR,
|
|
6041
|
+
message: "Could not connect to the dashboard",
|
|
6042
|
+
details: error.message,
|
|
6043
|
+
suggestion: errorSuggestions[ErrorCodes.NETWORK_ERROR]
|
|
6044
|
+
};
|
|
6045
|
+
}
|
|
6046
|
+
if (error.message.includes("fetch failed")) {
|
|
6047
|
+
return {
|
|
6048
|
+
code: ErrorCodes.NETWORK_ERROR,
|
|
6049
|
+
message: "Network request failed",
|
|
6050
|
+
details: error.message,
|
|
6051
|
+
suggestion: errorSuggestions[ErrorCodes.NETWORK_ERROR]
|
|
6052
|
+
};
|
|
6053
|
+
}
|
|
6054
|
+
return {
|
|
6055
|
+
code: ErrorCodes.UNKNOWN,
|
|
6056
|
+
message: error.message
|
|
6057
|
+
};
|
|
6058
|
+
}
|
|
6059
|
+
return {
|
|
6060
|
+
code: ErrorCodes.UNKNOWN,
|
|
6061
|
+
message: String(error)
|
|
6062
|
+
};
|
|
6063
|
+
}
|
|
6064
|
+
|
|
6065
|
+
// src/cli/helpers.ts
|
|
6066
|
+
function parsePaginationOptions(options) {
|
|
6067
|
+
return {
|
|
6068
|
+
limit: parseInt(options.limit ?? "20", 10),
|
|
6069
|
+
page: parseInt(options.page ?? "1", 10)
|
|
6070
|
+
};
|
|
6071
|
+
}
|
|
6072
|
+
function formatDateShort(isoDate) {
|
|
6073
|
+
return isoDate.split("T")[0] ?? "";
|
|
6074
|
+
}
|
|
6075
|
+
async function parseJsonData(options) {
|
|
6076
|
+
if (options.file) {
|
|
6077
|
+
const filePath = path9__namespace.resolve(options.file);
|
|
6078
|
+
const content = await fs5__namespace.readFile(filePath, "utf-8");
|
|
6079
|
+
return JSON.parse(content);
|
|
6080
|
+
}
|
|
6081
|
+
if (options.data) {
|
|
6082
|
+
return JSON.parse(options.data);
|
|
6083
|
+
}
|
|
6084
|
+
throw new Error("Either --data or --file is required");
|
|
6085
|
+
}
|
|
6086
|
+
async function parseJsonArray(options) {
|
|
6087
|
+
const parsed = await parseJsonData(options);
|
|
6088
|
+
if ("items" in parsed && Array.isArray(parsed.items)) {
|
|
6089
|
+
return parsed.items;
|
|
6090
|
+
}
|
|
6091
|
+
if (Array.isArray(parsed)) {
|
|
6092
|
+
return parsed;
|
|
6093
|
+
}
|
|
6094
|
+
throw new Error('Expected an array or an object with an "items" array');
|
|
6095
|
+
}
|
|
6096
|
+
async function confirmAction(message, options, isRemote) {
|
|
6097
|
+
if (isRemote && !options.yes) {
|
|
6098
|
+
console.error("Remote operations require --yes flag for safety");
|
|
6099
|
+
return false;
|
|
6100
|
+
}
|
|
6101
|
+
if (options.yes) {
|
|
6102
|
+
return true;
|
|
6103
|
+
}
|
|
6104
|
+
const rl = readline__namespace.createInterface({
|
|
6105
|
+
input: process.stdin,
|
|
6106
|
+
output: process.stdout
|
|
6107
|
+
});
|
|
6108
|
+
return new Promise((resolve8) => {
|
|
6109
|
+
rl.question(`${message} (y/N): `, (answer) => {
|
|
6110
|
+
rl.close();
|
|
6111
|
+
resolve8(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
6112
|
+
});
|
|
6113
|
+
});
|
|
6114
|
+
}
|
|
6115
|
+
function createCommandContext(command) {
|
|
6116
|
+
const globalOpts = command.optsWithGlobals();
|
|
6117
|
+
const isRemote = globalOpts.remote ?? false;
|
|
6118
|
+
const isJsonOutput = globalOpts.json ?? false;
|
|
6119
|
+
const output = createOutput({
|
|
6120
|
+
json: globalOpts.json,
|
|
6121
|
+
quiet: globalOpts.quiet
|
|
6122
|
+
});
|
|
6123
|
+
const env = loadEnvironment(isRemote);
|
|
6124
|
+
const client = createManagementClient({
|
|
6125
|
+
dashboardUrl: env.dashboardUrl,
|
|
6126
|
+
managementApiKey: env.managementApiKey,
|
|
6127
|
+
siteId: env.siteId
|
|
6128
|
+
});
|
|
6129
|
+
return { output, client, isRemote, isJsonOutput };
|
|
6130
|
+
}
|
|
6131
|
+
function getOutputContext(command) {
|
|
6132
|
+
const globalOpts = command.optsWithGlobals();
|
|
6133
|
+
const isRemote = globalOpts.remote ?? false;
|
|
6134
|
+
const output = createOutput({
|
|
6135
|
+
json: globalOpts.json,
|
|
6136
|
+
quiet: globalOpts.quiet
|
|
6137
|
+
});
|
|
6138
|
+
return { output, isRemote, globalOpts };
|
|
6139
|
+
}
|
|
6140
|
+
function handleCommandError(error, output) {
|
|
6141
|
+
if (error instanceof SyntaxError) {
|
|
6142
|
+
output.error("Invalid JSON data", { suggestion: "Check your JSON syntax" });
|
|
6143
|
+
return;
|
|
6144
|
+
}
|
|
6145
|
+
const formatted = formatError(error);
|
|
6146
|
+
output.error(formatted.message, {
|
|
6147
|
+
code: formatted.code,
|
|
6148
|
+
details: formatted.details,
|
|
6149
|
+
suggestion: formatted.suggestion
|
|
6150
|
+
});
|
|
6151
|
+
}
|
|
6152
|
+
function withErrorHandling(action) {
|
|
6153
|
+
return async (...args) => {
|
|
6154
|
+
const command = args[args.length - 1];
|
|
6155
|
+
const { output } = getOutputContext(command);
|
|
6156
|
+
try {
|
|
6157
|
+
await action(...args);
|
|
6158
|
+
} catch (error) {
|
|
6159
|
+
handleCommandError(error, output);
|
|
6160
|
+
}
|
|
6161
|
+
};
|
|
6162
|
+
}
|
|
6163
|
+
function withConfirmation(getMessage, action) {
|
|
6164
|
+
return async (...allArgs) => {
|
|
6165
|
+
const command = allArgs[allArgs.length - 1];
|
|
6166
|
+
const options = allArgs[allArgs.length - 2];
|
|
6167
|
+
const args = allArgs.slice(0, -2);
|
|
6168
|
+
const ctx = createCommandContext(command);
|
|
6169
|
+
const { output, isRemote } = ctx;
|
|
6170
|
+
const confirmed = await confirmAction(getMessage(...args), options, isRemote);
|
|
6171
|
+
if (!confirmed) {
|
|
6172
|
+
output.info("Operation cancelled");
|
|
6173
|
+
process.exit(0);
|
|
6174
|
+
}
|
|
6175
|
+
try {
|
|
6176
|
+
await action(ctx, ...args);
|
|
6177
|
+
} catch (error) {
|
|
6178
|
+
handleCommandError(error, output);
|
|
6179
|
+
}
|
|
6180
|
+
};
|
|
6181
|
+
}
|
|
6182
|
+
function capitalize(str) {
|
|
6183
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
6184
|
+
}
|
|
6185
|
+
function createPublishCommand(config3) {
|
|
6186
|
+
const cmd = new commander.Command("publish").description(`Publish a ${config3.resourceName}`);
|
|
6187
|
+
for (const arg of config3.args) {
|
|
6188
|
+
cmd.argument(`<${arg}>`, `${capitalize(arg.replace("-", " "))}`);
|
|
6189
|
+
}
|
|
6190
|
+
return cmd.action(
|
|
6191
|
+
withErrorHandling(async (...actionArgs) => {
|
|
6192
|
+
const args = actionArgs.slice(0, config3.args.length);
|
|
6193
|
+
const command = actionArgs[actionArgs.length - 1];
|
|
6194
|
+
const { output, client } = createCommandContext(command);
|
|
6195
|
+
const label = args.join("/");
|
|
6196
|
+
output.info(`Publishing ${config3.resourceName}: ${label}`);
|
|
6197
|
+
await config3.action(client, ...args);
|
|
6198
|
+
output.success(`${capitalize(config3.resourceName)} published: ${label}`);
|
|
6199
|
+
})
|
|
6200
|
+
);
|
|
6201
|
+
}
|
|
6202
|
+
function createUnpublishCommand(config3) {
|
|
6203
|
+
const cmd = new commander.Command("unpublish").description(`Unpublish a ${config3.resourceName}`);
|
|
6204
|
+
for (const arg of config3.args) {
|
|
6205
|
+
cmd.argument(`<${arg}>`, `${capitalize(arg.replace("-", " "))}`);
|
|
6206
|
+
}
|
|
6207
|
+
return cmd.action(
|
|
6208
|
+
withErrorHandling(async (...actionArgs) => {
|
|
6209
|
+
const args = actionArgs.slice(0, config3.args.length);
|
|
6210
|
+
const command = actionArgs[actionArgs.length - 1];
|
|
6211
|
+
const { output, client } = createCommandContext(command);
|
|
6212
|
+
const label = args.join("/");
|
|
6213
|
+
output.info(`Unpublishing ${config3.resourceName}: ${label}`);
|
|
6214
|
+
await config3.action(client, ...args);
|
|
6215
|
+
output.success(`${capitalize(config3.resourceName)} unpublished: ${label}`);
|
|
6216
|
+
})
|
|
6217
|
+
);
|
|
6218
|
+
}
|
|
6219
|
+
function createGetCommand(config3) {
|
|
6220
|
+
const cmd = new commander.Command("get").description(`Get a single ${config3.resourceName}`);
|
|
6221
|
+
for (const arg of config3.args) {
|
|
6222
|
+
cmd.argument(`<${arg}>`, `${capitalize(arg.replace("-", " "))}`);
|
|
6223
|
+
}
|
|
6224
|
+
return cmd.action(
|
|
6225
|
+
withErrorHandling(async (...actionArgs) => {
|
|
6226
|
+
const args = actionArgs.slice(0, config3.args.length);
|
|
6227
|
+
const command = actionArgs[actionArgs.length - 1];
|
|
6228
|
+
const { output, client, isJsonOutput } = createCommandContext(command);
|
|
6229
|
+
const label = args.join("/");
|
|
6230
|
+
const item = await config3.get(client, ...args);
|
|
6231
|
+
if (!item) {
|
|
6232
|
+
return output.error(`${capitalize(config3.resourceName)} not found: ${label}`, {
|
|
6233
|
+
suggestion: `Check that the ${config3.args.join(" and ")} ${config3.args.length > 1 ? "are" : "is"} correct`
|
|
6234
|
+
});
|
|
6235
|
+
}
|
|
6236
|
+
if (isJsonOutput) {
|
|
6237
|
+
output.json(item);
|
|
6238
|
+
} else {
|
|
6239
|
+
output.success(`${capitalize(config3.resourceName)}: ${label}`, config3.formatOutput(item));
|
|
6240
|
+
}
|
|
6241
|
+
})
|
|
6242
|
+
);
|
|
6243
|
+
}
|
|
6244
|
+
function createListCommand(config3) {
|
|
6245
|
+
const cmd = new commander.Command("list").description(`List ${config3.resourceNamePlural}`);
|
|
6246
|
+
for (const arg of config3.args) {
|
|
6247
|
+
cmd.argument(`<${arg}>`, `${capitalize(arg.replace("-", " "))}`);
|
|
6248
|
+
}
|
|
6249
|
+
if (config3.hasPagination) {
|
|
6250
|
+
cmd.option("--limit <n>", "Number of items per page", "20");
|
|
6251
|
+
cmd.option("--page <n>", "Page number", "1");
|
|
6252
|
+
}
|
|
6253
|
+
return cmd.action(
|
|
6254
|
+
withErrorHandling(async (...actionArgs) => {
|
|
6255
|
+
const args = actionArgs.slice(0, config3.args.length);
|
|
6256
|
+
const options = actionArgs[config3.args.length];
|
|
6257
|
+
const command = actionArgs[actionArgs.length - 1];
|
|
6258
|
+
const { output, client, isJsonOutput } = createCommandContext(command);
|
|
6259
|
+
const pagination = config3.hasPagination ? parsePaginationOptions(options) : void 0;
|
|
6260
|
+
const result = await config3.list(client, args, pagination);
|
|
6261
|
+
if (isJsonOutput) {
|
|
6262
|
+
output.json({
|
|
6263
|
+
[config3.resourceNamePlural]: result.items,
|
|
6264
|
+
...result.pagination && { pagination: result.pagination }
|
|
6265
|
+
});
|
|
6266
|
+
} else if (result.items.length === 0) {
|
|
6267
|
+
const context = args.length > 0 ? ` for ${args.join("/")}` : "";
|
|
6268
|
+
output.info(`No ${config3.resourceNamePlural} found${context}`);
|
|
6269
|
+
} else {
|
|
6270
|
+
output.table(config3.tableHeaders, result.items.map(config3.formatRow));
|
|
6271
|
+
if (result.pagination) {
|
|
6272
|
+
output.info(
|
|
6273
|
+
`Showing ${result.items.length} of ${result.pagination.total} ${config3.resourceNamePlural} (page ${result.pagination.page})`
|
|
6274
|
+
);
|
|
6275
|
+
} else {
|
|
6276
|
+
output.info(`${result.items.length} ${config3.resourceNamePlural}`);
|
|
6277
|
+
}
|
|
6278
|
+
}
|
|
6279
|
+
})
|
|
6280
|
+
);
|
|
6281
|
+
}
|
|
6282
|
+
|
|
6283
|
+
// src/cli/commands/pull.ts
|
|
6284
|
+
var DEFAULT_PAGE_LIMIT = 500;
|
|
6285
|
+
async function pullEntriesWithPagination(client, contentType, output) {
|
|
6286
|
+
const allEntries = [];
|
|
6287
|
+
let page = 1;
|
|
6288
|
+
let hasMore = true;
|
|
6289
|
+
let lastResult = null;
|
|
6290
|
+
while (hasMore) {
|
|
6291
|
+
const result = await client.pull.entries(contentType, {
|
|
6292
|
+
page,
|
|
6293
|
+
limit: DEFAULT_PAGE_LIMIT
|
|
6294
|
+
});
|
|
6295
|
+
allEntries.push(...result.entries);
|
|
6296
|
+
lastResult = result;
|
|
6297
|
+
hasMore = result.pagination.hasMore;
|
|
6298
|
+
if (hasMore) {
|
|
6299
|
+
output.info(`Fetched ${allEntries.length} entries (page ${page})...`);
|
|
6300
|
+
}
|
|
6301
|
+
page++;
|
|
6302
|
+
}
|
|
6303
|
+
return {
|
|
6304
|
+
contentType,
|
|
6305
|
+
entries: allEntries,
|
|
6306
|
+
meta: lastResult?.meta ?? { pulledAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} },
|
|
6307
|
+
pagination: {
|
|
6308
|
+
page: 1,
|
|
6309
|
+
limit: allEntries.length,
|
|
6310
|
+
total: allEntries.length,
|
|
6311
|
+
hasMore: false
|
|
6312
|
+
}
|
|
6313
|
+
};
|
|
6314
|
+
}
|
|
6315
|
+
async function writeAllEntries(contentDir, entriesByType, pulledAt) {
|
|
6316
|
+
let totalCount = 0;
|
|
6317
|
+
const files = [];
|
|
6318
|
+
for (const [contentType, entries] of Object.entries(entriesByType)) {
|
|
6319
|
+
const { filePath } = await writeEntries(contentDir, {
|
|
6320
|
+
contentType,
|
|
6321
|
+
entries: entries.map((e) => ({
|
|
6322
|
+
identifier: e.identifier,
|
|
6323
|
+
data: e.data,
|
|
6324
|
+
status: e.status,
|
|
6325
|
+
hasUnpublishedChanges: e.hasUnpublishedChanges,
|
|
6326
|
+
slug: e.slug
|
|
6327
|
+
})),
|
|
6328
|
+
meta: { pulledAt, entries: {} },
|
|
6329
|
+
pagination: { limit: entries.length, total: entries.length}
|
|
6330
|
+
});
|
|
6331
|
+
totalCount += entries.length;
|
|
6332
|
+
files.push(filePath);
|
|
6333
|
+
}
|
|
6334
|
+
return { totalCount, files };
|
|
6335
|
+
}
|
|
6336
|
+
async function fetchAllContentPaginated(client, contentTypes, output) {
|
|
6337
|
+
const allEntries = {};
|
|
6338
|
+
for (const contentType of contentTypes) {
|
|
6339
|
+
output.info(`Fetching ${contentType} entries...`);
|
|
6340
|
+
const result = await pullEntriesWithPagination(client, contentType, output);
|
|
6341
|
+
allEntries[contentType] = result.entries.map((e) => ({
|
|
6342
|
+
identifier: e.identifier,
|
|
6343
|
+
data: e.data,
|
|
6344
|
+
status: e.status,
|
|
6345
|
+
hasUnpublishedChanges: e.hasUnpublishedChanges,
|
|
6346
|
+
slug: e.slug
|
|
6347
|
+
}));
|
|
6348
|
+
output.info(` ${result.entries.length} entries`);
|
|
6349
|
+
}
|
|
6350
|
+
output.info("Fetching pages...");
|
|
6351
|
+
const pagesResult = await client.pull.pages();
|
|
6352
|
+
output.info("Fetching navigation...");
|
|
6353
|
+
const navigationResult = await client.pull.navigation();
|
|
6354
|
+
output.info("Fetching settings...");
|
|
6355
|
+
const settingsResult = await client.pull.settings();
|
|
6356
|
+
return {
|
|
6357
|
+
entries: allEntries,
|
|
6358
|
+
pages: pagesResult.pages,
|
|
6359
|
+
navigation: navigationResult.menus,
|
|
6360
|
+
settings: settingsResult.settings,
|
|
6361
|
+
meta: { pulledAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
6362
|
+
};
|
|
6363
|
+
}
|
|
6364
|
+
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").addHelpText("after", `
|
|
6365
|
+
Examples:
|
|
6366
|
+
$ riverbankcms pull # Pull all content
|
|
6367
|
+
$ riverbankcms pull --remote # Pull from production
|
|
6368
|
+
$ riverbankcms pull entries # Pull all entries
|
|
6369
|
+
$ riverbankcms pull entries blog-post # Pull specific content type
|
|
6370
|
+
$ riverbankcms pull pages # Pull pages with blocks
|
|
6371
|
+
$ riverbankcms pull navigation # Pull navigation menus
|
|
6372
|
+
$ riverbankcms pull settings # Pull site settings
|
|
6373
|
+
$ riverbankcms pull --output ./src/content # Custom output directory
|
|
6374
|
+
`).action(
|
|
6375
|
+
withErrorHandling(
|
|
6376
|
+
async (scope, type, options, command) => {
|
|
6377
|
+
const { output, client } = createCommandContext(command);
|
|
6378
|
+
const contentDir = path9__namespace.resolve(options.output ?? "./content");
|
|
6379
|
+
const pullScope = scope?.toLowerCase() ?? "all";
|
|
6380
|
+
switch (pullScope) {
|
|
6381
|
+
case "entries": {
|
|
6382
|
+
if (type) {
|
|
6383
|
+
output.info(`Pulling entries for content type: ${type}`);
|
|
6384
|
+
const result = await pullEntriesWithPagination(client, type, output);
|
|
6385
|
+
const { filePath, metaPath } = await writeEntries(contentDir, result);
|
|
6386
|
+
output.success(`Pulled ${result.entries.length} entries`, {
|
|
6387
|
+
contentType: type,
|
|
6388
|
+
file: filePath,
|
|
6389
|
+
meta: metaPath
|
|
6390
|
+
});
|
|
6391
|
+
} else {
|
|
6392
|
+
output.info("Pulling all entries");
|
|
6393
|
+
let result = await client.pull.all();
|
|
6394
|
+
if (result.meta.truncated) {
|
|
6395
|
+
output.warn("Content was truncated due to size limits.");
|
|
6396
|
+
output.info("Fetching complete data via pagination...");
|
|
6397
|
+
const contentTypes = Object.keys(result.entries);
|
|
6398
|
+
result = await fetchAllContentPaginated(client, contentTypes, output);
|
|
6399
|
+
}
|
|
6400
|
+
const { totalCount, files } = await writeAllEntries(
|
|
6401
|
+
contentDir,
|
|
6402
|
+
result.entries,
|
|
6403
|
+
result.meta.pulledAt
|
|
6404
|
+
);
|
|
6405
|
+
output.success(`Pulled ${totalCount} entries across ${Object.keys(result.entries).length} content types`, {
|
|
6406
|
+
files
|
|
6407
|
+
});
|
|
6408
|
+
}
|
|
6409
|
+
break;
|
|
6410
|
+
}
|
|
6411
|
+
case "pages": {
|
|
6412
|
+
output.info("Pulling pages");
|
|
6413
|
+
const result = await client.pull.pages();
|
|
6414
|
+
const files = await writePages(contentDir, result);
|
|
6415
|
+
output.success(`Pulled ${result.pages.length} pages`, { files });
|
|
6416
|
+
break;
|
|
6417
|
+
}
|
|
6418
|
+
case "navigation": {
|
|
6419
|
+
output.info("Pulling navigation menus");
|
|
6420
|
+
const result = await client.pull.navigation();
|
|
6421
|
+
const filePath = await writeNavigation(contentDir, result);
|
|
6422
|
+
output.success(`Pulled ${result.menus.length} navigation menus`, { file: filePath });
|
|
6423
|
+
break;
|
|
6424
|
+
}
|
|
6425
|
+
case "settings": {
|
|
6426
|
+
output.info("Pulling site settings");
|
|
6427
|
+
const result = await client.pull.settings();
|
|
6428
|
+
const filePath = await writeSettings(contentDir, result);
|
|
6429
|
+
output.success("Pulled site settings", { file: filePath });
|
|
6430
|
+
break;
|
|
6431
|
+
}
|
|
6432
|
+
case "all":
|
|
6433
|
+
default: {
|
|
6434
|
+
output.info("Pulling all content");
|
|
6435
|
+
let result = await client.pull.all();
|
|
6436
|
+
if (result.meta.truncated) {
|
|
6437
|
+
output.warn("Content was truncated due to size limits.");
|
|
6438
|
+
output.info("Fetching complete data via pagination...");
|
|
6439
|
+
const contentTypes = Object.keys(result.entries);
|
|
6440
|
+
result = await fetchAllContentPaginated(client, contentTypes, output);
|
|
6441
|
+
}
|
|
6442
|
+
const { totalCount: totalEntries } = await writeAllEntries(
|
|
6443
|
+
contentDir,
|
|
6444
|
+
result.entries,
|
|
6445
|
+
result.meta.pulledAt
|
|
6446
|
+
);
|
|
6447
|
+
await writePages(contentDir, { pages: result.pages, meta: result.meta });
|
|
6448
|
+
await writeNavigation(contentDir, { menus: result.navigation, meta: result.meta });
|
|
6449
|
+
await writeSettings(contentDir, { settings: result.settings, meta: result.meta });
|
|
6450
|
+
output.success("Pull complete", {
|
|
6451
|
+
entries: { count: totalEntries, types: Object.keys(result.entries).length },
|
|
6452
|
+
pages: { count: result.pages.length },
|
|
6453
|
+
navigation: { count: result.navigation.length },
|
|
6454
|
+
directory: contentDir
|
|
6455
|
+
});
|
|
6456
|
+
break;
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6460
|
+
)
|
|
6461
|
+
);
|
|
6462
|
+
|
|
6463
|
+
// src/cli/config-loader.ts
|
|
6464
|
+
init_load_config();
|
|
6465
|
+
var DEFAULT_SYNC_CONFIG = {
|
|
6466
|
+
existingEntries: "skip",
|
|
6467
|
+
contentTarget: "draft"
|
|
6468
|
+
};
|
|
6469
|
+
async function loadCliConfig(configPath) {
|
|
6470
|
+
try {
|
|
6471
|
+
const rawConfig = await loadConfigFile(configPath);
|
|
6472
|
+
const configObj = rawConfig;
|
|
6473
|
+
const contentDir = typeof configObj.contentDir === "string" ? configObj.contentDir : "./content";
|
|
6474
|
+
const syncConfig = configObj.sync;
|
|
6475
|
+
return {
|
|
6476
|
+
contentDir,
|
|
6477
|
+
sync: {
|
|
6478
|
+
existingEntries: syncConfig?.existingEntries ?? DEFAULT_SYNC_CONFIG.existingEntries,
|
|
6479
|
+
contentTarget: syncConfig?.contentTarget ?? DEFAULT_SYNC_CONFIG.contentTarget
|
|
6480
|
+
}
|
|
6481
|
+
};
|
|
6482
|
+
} catch (error) {
|
|
6483
|
+
if (error instanceof Error && error.message.includes("Config file not found")) {
|
|
6484
|
+
return {
|
|
6485
|
+
contentDir: "./content",
|
|
6486
|
+
sync: { ...DEFAULT_SYNC_CONFIG }
|
|
6487
|
+
};
|
|
6488
|
+
}
|
|
6489
|
+
throw error;
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
async function fileExists(filePath) {
|
|
6493
|
+
try {
|
|
6494
|
+
await fs5__namespace.access(filePath);
|
|
6495
|
+
return true;
|
|
6496
|
+
} catch {
|
|
6497
|
+
return false;
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
async function readJsonFile(filePath) {
|
|
6501
|
+
const content = await fs5__namespace.readFile(filePath, "utf-8");
|
|
6502
|
+
return JSON.parse(content);
|
|
6503
|
+
}
|
|
6504
|
+
async function listFiles(dirPath, extension) {
|
|
6505
|
+
try {
|
|
6506
|
+
const files = await fs5__namespace.readdir(dirPath);
|
|
6507
|
+
return files.filter((f) => f.endsWith(extension)).map((f) => path9__namespace.join(dirPath, f));
|
|
6508
|
+
} catch {
|
|
6509
|
+
return [];
|
|
6510
|
+
}
|
|
6511
|
+
}
|
|
6512
|
+
async function readEntries(contentDir, contentType) {
|
|
6513
|
+
const entriesDir = path9__namespace.join(contentDir, "entries");
|
|
6514
|
+
const result = /* @__PURE__ */ new Map();
|
|
6515
|
+
{
|
|
6516
|
+
const files = await listFiles(entriesDir, ".json");
|
|
6517
|
+
for (const filePath of files) {
|
|
6518
|
+
try {
|
|
6519
|
+
const file = await readJsonFile(filePath);
|
|
6520
|
+
result.set(file.contentType, file.entries);
|
|
6521
|
+
} catch (error) {
|
|
6522
|
+
console.warn(`Warning: Could not parse ${filePath}:`, error);
|
|
6523
|
+
}
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
return result;
|
|
6527
|
+
}
|
|
6528
|
+
async function readPages(contentDir) {
|
|
6529
|
+
const pagesDir = path9__namespace.join(contentDir, "pages");
|
|
6530
|
+
const pages = [];
|
|
6531
|
+
const files = await listFiles(pagesDir, ".json");
|
|
6532
|
+
for (const filePath of files) {
|
|
6533
|
+
try {
|
|
6534
|
+
const page = await readJsonFile(filePath);
|
|
6535
|
+
pages.push(page);
|
|
6536
|
+
} catch (error) {
|
|
6537
|
+
console.warn(`Warning: Could not parse ${filePath}:`, error);
|
|
6538
|
+
}
|
|
6539
|
+
}
|
|
6540
|
+
return pages;
|
|
6541
|
+
}
|
|
6542
|
+
async function readNavigation(contentDir) {
|
|
6543
|
+
const filePath = path9__namespace.join(contentDir, "navigation.json");
|
|
6544
|
+
if (!await fileExists(filePath)) {
|
|
6545
|
+
return null;
|
|
6546
|
+
}
|
|
6547
|
+
return readJsonFile(filePath);
|
|
6548
|
+
}
|
|
6549
|
+
async function readSettings(contentDir) {
|
|
6550
|
+
const filePath = path9__namespace.join(contentDir, "settings.json");
|
|
6551
|
+
if (!await fileExists(filePath)) {
|
|
6552
|
+
return null;
|
|
6553
|
+
}
|
|
6554
|
+
return readJsonFile(filePath);
|
|
6555
|
+
}
|
|
6556
|
+
async function readAllContent(contentDir) {
|
|
6557
|
+
const [entries, pages, navigation, settings] = await Promise.all([
|
|
6558
|
+
readEntries(contentDir),
|
|
6559
|
+
readPages(contentDir),
|
|
6560
|
+
readNavigation(contentDir),
|
|
6561
|
+
readSettings(contentDir)
|
|
6562
|
+
]);
|
|
6563
|
+
return { entries, pages, navigation, settings };
|
|
6564
|
+
}
|
|
6565
|
+
async function contentDirExists(contentDir) {
|
|
6566
|
+
return fileExists(contentDir);
|
|
6567
|
+
}
|
|
6568
|
+
async function getContentSummary(contentDir) {
|
|
6569
|
+
const content = await readAllContent(contentDir);
|
|
6570
|
+
const entryTypes = Array.from(content.entries.keys());
|
|
6571
|
+
let totalEntries = 0;
|
|
6572
|
+
for (const entries of content.entries.values()) {
|
|
6573
|
+
totalEntries += entries.length;
|
|
6574
|
+
}
|
|
6575
|
+
return {
|
|
6576
|
+
hasEntries: content.entries.size > 0,
|
|
6577
|
+
entryTypes,
|
|
6578
|
+
totalEntries,
|
|
6579
|
+
hasPages: content.pages.length > 0,
|
|
6580
|
+
pageCount: content.pages.length,
|
|
6581
|
+
hasNavigation: content.navigation !== null && content.navigation.menus.length > 0,
|
|
6582
|
+
menuCount: content.navigation?.menus.length ?? 0,
|
|
6583
|
+
hasSettings: content.settings !== null
|
|
6584
|
+
};
|
|
6585
|
+
}
|
|
6586
|
+
|
|
6587
|
+
// src/cli/sync/mapper.ts
|
|
6588
|
+
function stripNavigationItemIds(items) {
|
|
6589
|
+
return items.map((item) => {
|
|
6590
|
+
const result = {
|
|
6591
|
+
label: item.label,
|
|
6592
|
+
url: item.url
|
|
6593
|
+
};
|
|
6594
|
+
if (item.children && item.children.length > 0) {
|
|
6595
|
+
result.children = stripNavigationItemIds(item.children);
|
|
6596
|
+
}
|
|
6597
|
+
return result;
|
|
6598
|
+
});
|
|
6599
|
+
}
|
|
6600
|
+
|
|
6601
|
+
// src/cli/sync/diff.ts
|
|
6602
|
+
function findChangedFields(local, remote, prefix = "") {
|
|
6603
|
+
const changes = [];
|
|
6604
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(local), ...Object.keys(remote)]);
|
|
6605
|
+
for (const key of allKeys) {
|
|
6606
|
+
const path11 = prefix ? `${prefix}.${key}` : key;
|
|
6607
|
+
const localVal = local[key];
|
|
6608
|
+
const remoteVal = remote[key];
|
|
6609
|
+
if (!equal__default.default(localVal, remoteVal)) {
|
|
6610
|
+
changes.push(path11);
|
|
6611
|
+
}
|
|
6612
|
+
}
|
|
6613
|
+
return changes;
|
|
6614
|
+
}
|
|
6615
|
+
function buildJsonDiff(diff, local, remote, mode) {
|
|
6616
|
+
const summary = { creates: 0, updates: 0, deletes: 0 };
|
|
6617
|
+
const changes = [];
|
|
6618
|
+
const localPages = new Map(local.pages.map((page) => [page.identifier, page]));
|
|
6619
|
+
const remotePages = new Map(remote.pages.map((page) => [page.identifier, page]));
|
|
6620
|
+
const localEntries = local.entries;
|
|
6621
|
+
const remoteEntries = remote.entries;
|
|
6622
|
+
const localMenus = new Map((local.navigation?.menus ?? []).map((menu) => [menu.name, menu]));
|
|
6623
|
+
const remoteMenus = new Map(remote.navigation.map((menu) => [menu.name, menu]));
|
|
6624
|
+
const addChange = (change) => {
|
|
6625
|
+
if (change.operation === "create") summary.creates += 1;
|
|
6626
|
+
if (change.operation === "update") summary.updates += 1;
|
|
6627
|
+
if (change.operation === "delete") summary.deletes += 1;
|
|
6628
|
+
changes.push(change);
|
|
6629
|
+
};
|
|
6630
|
+
for (const [contentType, entryDiffs] of diff.entries) {
|
|
6631
|
+
for (const entryDiff of entryDiffs) {
|
|
6632
|
+
if (entryDiff.type === "unchanged") continue;
|
|
6633
|
+
if (entryDiff.type !== "create" && entryDiff.type !== "update") continue;
|
|
6634
|
+
const identifier = `${contentType}/${entryDiff.identifier}`;
|
|
6635
|
+
const localEntry = localEntries.get(contentType)?.find((entry) => entry.identifier === entryDiff.identifier);
|
|
6636
|
+
const remoteEntry = remoteEntries[contentType]?.find((entry) => entry.identifier === entryDiff.identifier);
|
|
6637
|
+
const change = {
|
|
6638
|
+
type: "entry",
|
|
6639
|
+
identifier,
|
|
6640
|
+
operation: entryDiff.type
|
|
6641
|
+
};
|
|
6642
|
+
if (mode === "full") {
|
|
6643
|
+
change.before = remoteEntry ?? null;
|
|
6644
|
+
change.after = localEntry ?? null;
|
|
6645
|
+
if (entryDiff.changes) change.diff = { changes: entryDiff.changes };
|
|
6646
|
+
}
|
|
6647
|
+
addChange(change);
|
|
6648
|
+
}
|
|
6649
|
+
}
|
|
6650
|
+
for (const pageDiff of diff.pages) {
|
|
6651
|
+
if (pageDiff.type === "unchanged") continue;
|
|
6652
|
+
if (pageDiff.type !== "create" && pageDiff.type !== "update") continue;
|
|
6653
|
+
const identifier = pageDiff.identifier;
|
|
6654
|
+
const localPage = localPages.get(identifier);
|
|
6655
|
+
const remotePage = remotePages.get(identifier);
|
|
6656
|
+
const change = {
|
|
6657
|
+
type: "page",
|
|
6658
|
+
identifier,
|
|
6659
|
+
operation: pageDiff.type
|
|
6660
|
+
};
|
|
6661
|
+
if (mode === "full") {
|
|
6662
|
+
change.before = remotePage ?? null;
|
|
6663
|
+
change.after = localPage ?? null;
|
|
6664
|
+
if (pageDiff.changes) change.diff = { changes: pageDiff.changes };
|
|
6665
|
+
}
|
|
6666
|
+
addChange(change);
|
|
6667
|
+
if (pageDiff.blocks) {
|
|
6668
|
+
for (const blockDiff of pageDiff.blocks) {
|
|
6669
|
+
if (blockDiff.type === "unchanged") continue;
|
|
6670
|
+
const pageId = blockDiff.pageIdentifier;
|
|
6671
|
+
const localBlock = localPages.get(pageId)?.blocks?.find((block) => block.identifier === blockDiff.identifier);
|
|
6672
|
+
const remoteBlock = remotePages.get(pageId)?.blocks?.find((block) => block.identifier === blockDiff.identifier);
|
|
6673
|
+
if (blockDiff.type === "reorder") {
|
|
6674
|
+
const localOrder = localPages.get(pageId)?.blocks?.map((block) => block.identifier) ?? [];
|
|
6675
|
+
addChange({
|
|
6676
|
+
type: "block",
|
|
6677
|
+
identifier: `${pageId}::reorder`,
|
|
6678
|
+
operation: "update",
|
|
6679
|
+
...mode === "full" ? { diff: { reorder: true, order: localOrder } } : {}
|
|
6680
|
+
});
|
|
6681
|
+
continue;
|
|
6682
|
+
}
|
|
6683
|
+
const operation = blockDiff.type === "delete" ? "delete" : blockDiff.type;
|
|
6684
|
+
const identifier2 = `${pageId}/${blockDiff.identifier}`;
|
|
6685
|
+
const change2 = {
|
|
6686
|
+
type: "block",
|
|
6687
|
+
identifier: identifier2,
|
|
6688
|
+
operation
|
|
6689
|
+
};
|
|
6690
|
+
if (mode === "full") {
|
|
6691
|
+
change2.before = remoteBlock ?? null;
|
|
6692
|
+
change2.after = localBlock ?? null;
|
|
6693
|
+
if (blockDiff.changes) change2.diff = { changes: blockDiff.changes };
|
|
6694
|
+
}
|
|
6695
|
+
addChange(change2);
|
|
6696
|
+
}
|
|
6697
|
+
}
|
|
6698
|
+
}
|
|
6699
|
+
for (const navDiff of diff.navigation) {
|
|
6700
|
+
if (navDiff.type === "unchanged") continue;
|
|
6701
|
+
if (navDiff.type !== "create" && navDiff.type !== "update") continue;
|
|
6702
|
+
const identifier = navDiff.name;
|
|
6703
|
+
const localMenu = localMenus.get(navDiff.name);
|
|
6704
|
+
const remoteMenu = remoteMenus.get(navDiff.name);
|
|
6705
|
+
const change = {
|
|
6706
|
+
type: "navigation",
|
|
6707
|
+
identifier,
|
|
6708
|
+
operation: navDiff.type
|
|
6709
|
+
};
|
|
6710
|
+
if (mode === "full") {
|
|
6711
|
+
change.before = remoteMenu ? { ...remoteMenu, items: stripNavigationItemIds(remoteMenu.items) } : null;
|
|
6712
|
+
change.after = localMenu ?? null;
|
|
6713
|
+
if (navDiff.changes) change.diff = { changes: navDiff.changes };
|
|
6714
|
+
}
|
|
6715
|
+
addChange(change);
|
|
6716
|
+
}
|
|
6717
|
+
if (diff.settings) {
|
|
6718
|
+
const change = {
|
|
6719
|
+
type: "settings",
|
|
6720
|
+
identifier: "settings",
|
|
6721
|
+
operation: "update"
|
|
6722
|
+
};
|
|
6723
|
+
if (mode === "full") {
|
|
6724
|
+
change.before = remote.settings ?? null;
|
|
6725
|
+
change.after = local.settings ?? null;
|
|
6726
|
+
if (diff.settings.changes) change.diff = { changes: diff.settings.changes };
|
|
6727
|
+
}
|
|
6728
|
+
addChange(change);
|
|
6729
|
+
}
|
|
6730
|
+
return { summary, changes };
|
|
6731
|
+
}
|
|
6732
|
+
function calculateEntryDiffs(localEntries, remoteEntries, options) {
|
|
6733
|
+
const diffs = /* @__PURE__ */ new Map();
|
|
6734
|
+
const summary = { create: 0, update: 0, unchanged: 0 };
|
|
6735
|
+
const remoteLookup = /* @__PURE__ */ new Map();
|
|
6736
|
+
for (const [contentType, entries] of Object.entries(remoteEntries)) {
|
|
6737
|
+
const typeMap = /* @__PURE__ */ new Map();
|
|
6738
|
+
for (const entry of entries) {
|
|
6739
|
+
typeMap.set(entry.identifier, entry.data);
|
|
6740
|
+
}
|
|
6741
|
+
remoteLookup.set(contentType, typeMap);
|
|
6742
|
+
}
|
|
6743
|
+
for (const [contentType, localList] of localEntries) {
|
|
6744
|
+
const typeDiffs = [];
|
|
6745
|
+
const remoteTypeMap = remoteLookup.get(contentType) ?? /* @__PURE__ */ new Map();
|
|
6746
|
+
for (const localEntry of localList) {
|
|
6747
|
+
const remoteData = remoteTypeMap.get(localEntry.identifier);
|
|
6748
|
+
if (!remoteData) {
|
|
6749
|
+
typeDiffs.push({
|
|
6750
|
+
type: "create",
|
|
6751
|
+
identifier: localEntry.identifier,
|
|
6752
|
+
contentType
|
|
6753
|
+
});
|
|
6754
|
+
summary.create++;
|
|
6755
|
+
} else if (options.existingEntries === "skip") {
|
|
6756
|
+
typeDiffs.push({
|
|
6757
|
+
type: "unchanged",
|
|
6758
|
+
identifier: localEntry.identifier,
|
|
6759
|
+
contentType
|
|
6760
|
+
});
|
|
6761
|
+
summary.unchanged++;
|
|
6762
|
+
} else if (!equal__default.default(localEntry.data, remoteData)) {
|
|
6763
|
+
const changes = findChangedFields(
|
|
6764
|
+
localEntry.data,
|
|
6765
|
+
remoteData
|
|
6766
|
+
);
|
|
6767
|
+
typeDiffs.push({
|
|
6768
|
+
type: "update",
|
|
6769
|
+
identifier: localEntry.identifier,
|
|
6770
|
+
contentType,
|
|
6771
|
+
changes
|
|
6772
|
+
});
|
|
6773
|
+
summary.update++;
|
|
6774
|
+
} else {
|
|
6775
|
+
typeDiffs.push({
|
|
6776
|
+
type: "unchanged",
|
|
6777
|
+
identifier: localEntry.identifier,
|
|
6778
|
+
contentType
|
|
6779
|
+
});
|
|
6780
|
+
summary.unchanged++;
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6783
|
+
if (typeDiffs.length > 0) {
|
|
6784
|
+
diffs.set(contentType, typeDiffs);
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6787
|
+
return { diffs, summary };
|
|
6788
|
+
}
|
|
6789
|
+
function calculatePageDiffs(localPages, remotePages, options) {
|
|
6790
|
+
const diffs = [];
|
|
6791
|
+
const summary = { create: 0, update: 0, unchanged: 0 };
|
|
6792
|
+
const blockSummary = { create: 0, update: 0, delete: 0, unchanged: 0 };
|
|
6793
|
+
const remoteLookup = /* @__PURE__ */ new Map();
|
|
6794
|
+
for (const page of remotePages) {
|
|
6795
|
+
remoteLookup.set(page.identifier, page);
|
|
6796
|
+
}
|
|
6797
|
+
for (const localPage of localPages) {
|
|
6798
|
+
const remotePage = remoteLookup.get(localPage.identifier);
|
|
6799
|
+
if (!remotePage) {
|
|
6800
|
+
const blockDiffs = (localPage.blocks ?? []).map((block) => {
|
|
6801
|
+
blockSummary.create++;
|
|
6802
|
+
return {
|
|
6803
|
+
type: "create",
|
|
6804
|
+
identifier: block.identifier,
|
|
6805
|
+
pageIdentifier: localPage.identifier
|
|
6806
|
+
};
|
|
6807
|
+
});
|
|
6808
|
+
diffs.push({
|
|
6809
|
+
type: "create",
|
|
6810
|
+
identifier: localPage.identifier,
|
|
6811
|
+
blocks: blockDiffs
|
|
6812
|
+
});
|
|
6813
|
+
summary.create++;
|
|
6814
|
+
} else if (options.existingEntries === "skip") {
|
|
6815
|
+
diffs.push({
|
|
6816
|
+
type: "unchanged",
|
|
6817
|
+
identifier: localPage.identifier
|
|
6818
|
+
});
|
|
6819
|
+
summary.unchanged++;
|
|
6820
|
+
} else {
|
|
6821
|
+
const pageChanged = localPage.title !== remotePage.title || localPage.path !== remotePage.path;
|
|
6822
|
+
const blockDiffs = calculateBlockDiffs(
|
|
6823
|
+
localPage.blocks ?? [],
|
|
6824
|
+
remotePage.blocks ?? [],
|
|
6825
|
+
localPage.identifier,
|
|
6826
|
+
blockSummary
|
|
6827
|
+
);
|
|
6828
|
+
if (pageChanged || blockDiffs.some((b) => b.type !== "unchanged")) {
|
|
6829
|
+
const changes = [];
|
|
6830
|
+
if (localPage.title !== remotePage.title) changes.push("title");
|
|
6831
|
+
if (localPage.path !== remotePage.path) changes.push("path");
|
|
6832
|
+
diffs.push({
|
|
6833
|
+
type: "update",
|
|
6834
|
+
identifier: localPage.identifier,
|
|
6835
|
+
changes: changes.length > 0 ? changes : void 0,
|
|
6836
|
+
blocks: blockDiffs
|
|
6837
|
+
});
|
|
6838
|
+
summary.update++;
|
|
6839
|
+
} else {
|
|
6840
|
+
diffs.push({
|
|
6841
|
+
type: "unchanged",
|
|
6842
|
+
identifier: localPage.identifier
|
|
6843
|
+
});
|
|
6844
|
+
summary.unchanged++;
|
|
6845
|
+
}
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
return { diffs, summary, blockSummary };
|
|
6849
|
+
}
|
|
6850
|
+
function calculateBlockDiffs(localBlocks, remoteBlocks, pageIdentifier, summary) {
|
|
6851
|
+
const diffs = [];
|
|
6852
|
+
const remoteLookup = /* @__PURE__ */ new Map();
|
|
6853
|
+
for (const block of remoteBlocks) {
|
|
6854
|
+
remoteLookup.set(block.identifier, block);
|
|
6855
|
+
}
|
|
6856
|
+
const seenRemoteIds = /* @__PURE__ */ new Set();
|
|
6857
|
+
for (const localBlock of localBlocks) {
|
|
6858
|
+
const remoteBlock = remoteLookup.get(localBlock.identifier);
|
|
6859
|
+
seenRemoteIds.add(localBlock.identifier);
|
|
6860
|
+
if (!remoteBlock) {
|
|
6861
|
+
diffs.push({
|
|
6862
|
+
type: "create",
|
|
6863
|
+
identifier: localBlock.identifier,
|
|
6864
|
+
pageIdentifier
|
|
6865
|
+
});
|
|
6866
|
+
summary.create++;
|
|
6867
|
+
} else if (!equal__default.default(localBlock.data, remoteBlock.data) || localBlock.kind !== remoteBlock.kind) {
|
|
6868
|
+
const changes = findChangedFields(
|
|
6869
|
+
localBlock.data,
|
|
6870
|
+
remoteBlock.data
|
|
6871
|
+
);
|
|
6872
|
+
if (localBlock.kind !== remoteBlock.kind) {
|
|
6873
|
+
changes.push("kind");
|
|
6874
|
+
}
|
|
6875
|
+
diffs.push({
|
|
6876
|
+
type: "update",
|
|
6877
|
+
identifier: localBlock.identifier,
|
|
6878
|
+
pageIdentifier,
|
|
6879
|
+
changes
|
|
6880
|
+
});
|
|
6881
|
+
summary.update++;
|
|
6882
|
+
} else {
|
|
6883
|
+
diffs.push({
|
|
6884
|
+
type: "unchanged",
|
|
6885
|
+
identifier: localBlock.identifier,
|
|
6886
|
+
pageIdentifier
|
|
6887
|
+
});
|
|
6888
|
+
summary.unchanged++;
|
|
6889
|
+
}
|
|
6890
|
+
}
|
|
6891
|
+
for (const remoteBlock of remoteBlocks) {
|
|
6892
|
+
if (!seenRemoteIds.has(remoteBlock.identifier)) {
|
|
6893
|
+
diffs.push({
|
|
6894
|
+
type: "delete",
|
|
6895
|
+
identifier: remoteBlock.identifier,
|
|
6896
|
+
pageIdentifier
|
|
6897
|
+
});
|
|
6898
|
+
summary.delete++;
|
|
6899
|
+
}
|
|
6900
|
+
}
|
|
6901
|
+
const localOrder = localBlocks.filter((b) => remoteLookup.has(b.identifier)).map((b) => b.identifier);
|
|
6902
|
+
const remoteOrder = remoteBlocks.filter((b) => seenRemoteIds.has(b.identifier)).sort((a, b) => a.position - b.position).map((b) => b.identifier);
|
|
6903
|
+
if (localOrder.length > 1 && !equal__default.default(localOrder, remoteOrder)) {
|
|
6904
|
+
diffs.push({
|
|
6905
|
+
type: "reorder",
|
|
6906
|
+
identifier: pageIdentifier,
|
|
6907
|
+
pageIdentifier
|
|
6908
|
+
});
|
|
6909
|
+
}
|
|
6910
|
+
return diffs;
|
|
6911
|
+
}
|
|
6912
|
+
function calculateNavigationDiffs(localNavigation, remoteNavigation) {
|
|
6913
|
+
const diffs = [];
|
|
6914
|
+
const summary = { create: 0, update: 0, unchanged: 0 };
|
|
6915
|
+
if (!localNavigation || !localNavigation.menus) {
|
|
6916
|
+
return { diffs, summary };
|
|
6917
|
+
}
|
|
6918
|
+
const remoteLookup = /* @__PURE__ */ new Map();
|
|
6919
|
+
for (const menu of remoteNavigation) {
|
|
6920
|
+
remoteLookup.set(menu.name, menu);
|
|
6921
|
+
}
|
|
6922
|
+
for (const localMenu of localNavigation.menus) {
|
|
6923
|
+
const remoteMenu = remoteLookup.get(localMenu.name);
|
|
6924
|
+
if (!remoteMenu) {
|
|
6925
|
+
diffs.push({
|
|
6926
|
+
type: "create",
|
|
6927
|
+
name: localMenu.name
|
|
6928
|
+
});
|
|
6929
|
+
summary.create++;
|
|
6930
|
+
} else if (!equal__default.default(localMenu.items, stripNavigationItemIds(remoteMenu.items))) {
|
|
6931
|
+
diffs.push({
|
|
6932
|
+
type: "update",
|
|
6933
|
+
name: localMenu.name,
|
|
6934
|
+
changes: ["items"]
|
|
6935
|
+
});
|
|
6936
|
+
summary.update++;
|
|
6937
|
+
} else {
|
|
6938
|
+
diffs.push({
|
|
6939
|
+
type: "unchanged",
|
|
6940
|
+
name: localMenu.name
|
|
6941
|
+
});
|
|
6942
|
+
summary.unchanged++;
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
return { diffs, summary };
|
|
6946
|
+
}
|
|
6947
|
+
function calculateSettingsDiff(localSettings, remoteSettings) {
|
|
6948
|
+
const summary = { update: 0, unchanged: 0 };
|
|
6949
|
+
if (!localSettings) {
|
|
6950
|
+
return { diff: null, summary };
|
|
6951
|
+
}
|
|
6952
|
+
const changes = [];
|
|
6953
|
+
if (localSettings.homepageId !== remoteSettings.homepageId) {
|
|
6954
|
+
changes.push("homepageId");
|
|
6955
|
+
}
|
|
6956
|
+
if (!equal__default.default(localSettings.seoDefaults, remoteSettings.seoDefaults)) {
|
|
6957
|
+
changes.push("seoDefaults");
|
|
6958
|
+
}
|
|
6959
|
+
if (changes.length > 0) {
|
|
6960
|
+
summary.update = 1;
|
|
6961
|
+
return {
|
|
6962
|
+
diff: { type: "update", changes },
|
|
6963
|
+
summary
|
|
6964
|
+
};
|
|
6965
|
+
}
|
|
6966
|
+
summary.unchanged = 1;
|
|
6967
|
+
return { diff: null, summary };
|
|
6968
|
+
}
|
|
6969
|
+
function calculateDiff(local, remote, options) {
|
|
6970
|
+
const { diffs: entryDiffs, summary: entrySummary } = calculateEntryDiffs(
|
|
6971
|
+
local.entries,
|
|
6972
|
+
remote.entries,
|
|
6973
|
+
options
|
|
6974
|
+
);
|
|
6975
|
+
const { diffs: pageDiffs, summary: pageSummary, blockSummary } = calculatePageDiffs(
|
|
6976
|
+
local.pages,
|
|
6977
|
+
remote.pages,
|
|
6978
|
+
options
|
|
6979
|
+
);
|
|
6980
|
+
const { diffs: navDiffs, summary: navSummary } = calculateNavigationDiffs(
|
|
6981
|
+
local.navigation,
|
|
6982
|
+
remote.navigation
|
|
6983
|
+
);
|
|
6984
|
+
const { diff: settingsDiff, summary: settingsSummary } = calculateSettingsDiff(
|
|
6985
|
+
local.settings,
|
|
6986
|
+
remote.settings
|
|
6987
|
+
);
|
|
6988
|
+
return {
|
|
6989
|
+
entries: entryDiffs,
|
|
6990
|
+
pages: pageDiffs,
|
|
6991
|
+
navigation: navDiffs,
|
|
6992
|
+
settings: settingsDiff,
|
|
6993
|
+
summary: {
|
|
6994
|
+
entries: entrySummary,
|
|
6995
|
+
pages: pageSummary,
|
|
6996
|
+
blocks: blockSummary,
|
|
6997
|
+
navigation: navSummary,
|
|
6998
|
+
settings: settingsSummary
|
|
6999
|
+
}
|
|
7000
|
+
};
|
|
7001
|
+
}
|
|
7002
|
+
function hasPendingChanges(diff) {
|
|
7003
|
+
const { summary } = diff;
|
|
7004
|
+
return summary.entries.create > 0 || summary.entries.update > 0 || summary.pages.create > 0 || summary.pages.update > 0 || summary.blocks.create > 0 || summary.blocks.update > 0 || summary.blocks.delete > 0 || summary.navigation.create > 0 || summary.navigation.update > 0 || summary.settings.update > 0;
|
|
7005
|
+
}
|
|
7006
|
+
function formatDiffSummary(diff) {
|
|
7007
|
+
const lines = [];
|
|
7008
|
+
const { summary } = diff;
|
|
7009
|
+
if (summary.entries.create > 0 || summary.entries.update > 0) {
|
|
7010
|
+
lines.push(`Entries: +${summary.entries.create} ~${summary.entries.update}`);
|
|
7011
|
+
}
|
|
7012
|
+
if (summary.pages.create > 0 || summary.pages.update > 0) {
|
|
7013
|
+
lines.push(`Pages: +${summary.pages.create} ~${summary.pages.update}`);
|
|
7014
|
+
}
|
|
7015
|
+
if (summary.blocks.create > 0 || summary.blocks.update > 0 || summary.blocks.delete > 0) {
|
|
7016
|
+
lines.push(`Blocks: +${summary.blocks.create} ~${summary.blocks.update} -${summary.blocks.delete}`);
|
|
7017
|
+
}
|
|
7018
|
+
if (summary.navigation.create > 0 || summary.navigation.update > 0) {
|
|
7019
|
+
lines.push(`Navigation: +${summary.navigation.create} ~${summary.navigation.update}`);
|
|
7020
|
+
}
|
|
7021
|
+
if (summary.settings.update > 0) {
|
|
7022
|
+
lines.push(`Settings: ~${summary.settings.update}`);
|
|
7023
|
+
}
|
|
7024
|
+
if (lines.length === 0) {
|
|
7025
|
+
return "No changes detected";
|
|
7026
|
+
}
|
|
7027
|
+
return lines.join("\n");
|
|
7028
|
+
}
|
|
7029
|
+
function formatDiffDetail(diff) {
|
|
7030
|
+
const lines = [];
|
|
7031
|
+
for (const [contentType, entryDiffs] of diff.entries) {
|
|
7032
|
+
const creates = entryDiffs.filter((d) => d.type === "create");
|
|
7033
|
+
const updates = entryDiffs.filter((d) => d.type === "update");
|
|
7034
|
+
if (creates.length > 0) {
|
|
7035
|
+
lines.push(`
|
|
7036
|
+
${contentType} (new):`);
|
|
7037
|
+
for (const d of creates) {
|
|
7038
|
+
lines.push(` + ${d.identifier}`);
|
|
7039
|
+
}
|
|
7040
|
+
}
|
|
7041
|
+
if (updates.length > 0) {
|
|
7042
|
+
lines.push(`
|
|
7043
|
+
${contentType} (updated):`);
|
|
7044
|
+
for (const d of updates) {
|
|
7045
|
+
lines.push(` ~ ${d.identifier}${d.changes ? ` [${d.changes.join(", ")}]` : ""}`);
|
|
7046
|
+
}
|
|
7047
|
+
}
|
|
7048
|
+
}
|
|
7049
|
+
const pageCreates = diff.pages.filter((d) => d.type === "create");
|
|
7050
|
+
const pageUpdates = diff.pages.filter((d) => d.type === "update");
|
|
7051
|
+
if (pageCreates.length > 0) {
|
|
7052
|
+
lines.push("\nPages (new):");
|
|
7053
|
+
for (const d of pageCreates) {
|
|
7054
|
+
lines.push(` + ${d.identifier}`);
|
|
7055
|
+
}
|
|
7056
|
+
}
|
|
7057
|
+
if (pageUpdates.length > 0) {
|
|
7058
|
+
lines.push("\nPages (updated):");
|
|
7059
|
+
for (const d of pageUpdates) {
|
|
7060
|
+
lines.push(` ~ ${d.identifier}${d.changes ? ` [${d.changes.join(", ")}]` : ""}`);
|
|
7061
|
+
}
|
|
7062
|
+
}
|
|
7063
|
+
const navCreates = diff.navigation.filter((d) => d.type === "create");
|
|
7064
|
+
const navUpdates = diff.navigation.filter((d) => d.type === "update");
|
|
7065
|
+
if (navCreates.length > 0) {
|
|
7066
|
+
lines.push("\nNavigation (new):");
|
|
7067
|
+
for (const d of navCreates) {
|
|
7068
|
+
lines.push(` + ${d.name}`);
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
7071
|
+
if (navUpdates.length > 0) {
|
|
7072
|
+
lines.push("\nNavigation (updated):");
|
|
7073
|
+
for (const d of navUpdates) {
|
|
7074
|
+
lines.push(` ~ ${d.name}`);
|
|
7075
|
+
}
|
|
7076
|
+
}
|
|
7077
|
+
if (diff.settings) {
|
|
7078
|
+
lines.push("\nSettings (updated):");
|
|
7079
|
+
lines.push(` ~ [${diff.settings.changes.join(", ")}]`);
|
|
7080
|
+
}
|
|
7081
|
+
return lines.join("\n");
|
|
7082
|
+
}
|
|
7083
|
+
|
|
7084
|
+
// src/cli/sync/executor.ts
|
|
7085
|
+
function createEmptyResult() {
|
|
7086
|
+
return {
|
|
7087
|
+
entries: { created: 0, updated: 0, failed: 0 },
|
|
7088
|
+
pages: { created: 0, updated: 0, failed: 0 },
|
|
7089
|
+
blocks: { created: 0, updated: 0, deleted: 0, failed: 0 },
|
|
7090
|
+
navigation: { created: 0, updated: 0, failed: 0 },
|
|
7091
|
+
settings: { updated: 0, failed: 0 },
|
|
7092
|
+
errors: []
|
|
7093
|
+
};
|
|
7094
|
+
}
|
|
7095
|
+
async function syncEntries(client, diff, local, options, result) {
|
|
7096
|
+
for (const [contentType, entryDiffs] of diff.entries) {
|
|
7097
|
+
const localEntries = local.entries.get(contentType) ?? [];
|
|
7098
|
+
const entryLookup = new Map(localEntries.map((e) => [e.identifier, e]));
|
|
7099
|
+
for (const entryDiff of entryDiffs) {
|
|
7100
|
+
if (entryDiff.type === "unchanged") continue;
|
|
7101
|
+
const localEntry = entryLookup.get(entryDiff.identifier);
|
|
7102
|
+
if (!localEntry) continue;
|
|
7103
|
+
try {
|
|
7104
|
+
if (options.dryRun) ;
|
|
7105
|
+
await client.entries.upsert({
|
|
7106
|
+
contentType,
|
|
7107
|
+
identifier: localEntry.identifier,
|
|
7108
|
+
data: localEntry.data,
|
|
7109
|
+
slug: localEntry.slug
|
|
7110
|
+
});
|
|
7111
|
+
if (options.contentTarget === "publish") {
|
|
7112
|
+
await client.entries.publish(contentType, localEntry.identifier);
|
|
7113
|
+
}
|
|
7114
|
+
if (entryDiff.type === "create") {
|
|
7115
|
+
result.entries.created++;
|
|
7116
|
+
} else {
|
|
7117
|
+
result.entries.updated++;
|
|
7118
|
+
}
|
|
7119
|
+
} catch (error) {
|
|
7120
|
+
result.entries.failed++;
|
|
7121
|
+
result.errors.push({
|
|
7122
|
+
resource: "entry",
|
|
7123
|
+
identifier: `${contentType}/${entryDiff.identifier}`,
|
|
7124
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7125
|
+
});
|
|
7126
|
+
}
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7129
|
+
}
|
|
7130
|
+
async function syncPages(client, diff, local, options, result) {
|
|
7131
|
+
const pageLookup = new Map(local.pages.map((p) => [p.identifier, p]));
|
|
7132
|
+
for (const pageDiff of diff.pages) {
|
|
7133
|
+
if (pageDiff.type === "unchanged") continue;
|
|
7134
|
+
const localPage = pageLookup.get(pageDiff.identifier);
|
|
7135
|
+
if (!localPage) continue;
|
|
7136
|
+
try {
|
|
7137
|
+
if (options.dryRun) ;
|
|
7138
|
+
await client.pages.upsert({
|
|
7139
|
+
identifier: localPage.identifier,
|
|
7140
|
+
title: localPage.title,
|
|
7141
|
+
path: localPage.path,
|
|
7142
|
+
seoTitle: localPage.seoTitle,
|
|
7143
|
+
seoDescription: localPage.seoDescription
|
|
7144
|
+
});
|
|
7145
|
+
if (pageDiff.blocks && localPage.blocks) {
|
|
7146
|
+
await syncBlocks(
|
|
7147
|
+
client,
|
|
7148
|
+
pageDiff.identifier,
|
|
7149
|
+
pageDiff.blocks,
|
|
7150
|
+
localPage.blocks,
|
|
7151
|
+
options,
|
|
7152
|
+
result
|
|
7153
|
+
);
|
|
7154
|
+
}
|
|
7155
|
+
if (options.contentTarget === "publish") {
|
|
7156
|
+
await client.pages.publish(localPage.identifier);
|
|
7157
|
+
}
|
|
7158
|
+
if (pageDiff.type === "create") {
|
|
7159
|
+
result.pages.created++;
|
|
7160
|
+
} else {
|
|
7161
|
+
result.pages.updated++;
|
|
7162
|
+
}
|
|
7163
|
+
} catch (error) {
|
|
7164
|
+
result.pages.failed++;
|
|
7165
|
+
result.errors.push({
|
|
7166
|
+
resource: "page",
|
|
7167
|
+
identifier: pageDiff.identifier,
|
|
7168
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7169
|
+
});
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
7172
|
+
}
|
|
7173
|
+
async function syncBlocks(client, pageIdentifier, blockDiffs, localBlocks, options, result) {
|
|
7174
|
+
const blockLookup = new Map(localBlocks.map((b) => [b.identifier, b]));
|
|
7175
|
+
for (const blockDiff of blockDiffs) {
|
|
7176
|
+
if (blockDiff.type === "unchanged") continue;
|
|
7177
|
+
try {
|
|
7178
|
+
if (blockDiff.type === "delete") {
|
|
7179
|
+
await client.blocks.delete(pageIdentifier, blockDiff.identifier);
|
|
7180
|
+
result.blocks.deleted++;
|
|
7181
|
+
} else {
|
|
7182
|
+
const localBlock = blockLookup.get(blockDiff.identifier);
|
|
7183
|
+
if (!localBlock) continue;
|
|
7184
|
+
await client.blocks.upsert(pageIdentifier, {
|
|
7185
|
+
identifier: localBlock.identifier,
|
|
7186
|
+
kind: localBlock.kind,
|
|
7187
|
+
data: localBlock.data
|
|
7188
|
+
});
|
|
7189
|
+
if (blockDiff.type === "create") {
|
|
7190
|
+
result.blocks.created++;
|
|
7191
|
+
} else {
|
|
7192
|
+
result.blocks.updated++;
|
|
7193
|
+
}
|
|
7194
|
+
}
|
|
7195
|
+
} catch (error) {
|
|
7196
|
+
result.blocks.failed++;
|
|
7197
|
+
result.errors.push({
|
|
7198
|
+
resource: "block",
|
|
7199
|
+
identifier: `${pageIdentifier}/${blockDiff.identifier}`,
|
|
7200
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7201
|
+
});
|
|
7202
|
+
}
|
|
7203
|
+
}
|
|
7204
|
+
const hasReorderDiff = blockDiffs.some((d) => d.type === "reorder");
|
|
7205
|
+
if (hasReorderDiff) {
|
|
7206
|
+
const localIdentifiers = localBlocks.map((b) => b.identifier);
|
|
7207
|
+
if (localIdentifiers.length > 1) {
|
|
7208
|
+
try {
|
|
7209
|
+
await client.blocks.reorder(pageIdentifier, localIdentifiers);
|
|
7210
|
+
} catch {
|
|
7211
|
+
}
|
|
7212
|
+
}
|
|
7213
|
+
}
|
|
7214
|
+
}
|
|
7215
|
+
async function syncNavigation(client, diff, local, options, result) {
|
|
7216
|
+
if (!local.navigation?.menus) return;
|
|
7217
|
+
const menuLookup = new Map(local.navigation.menus.map((m) => [m.name, m]));
|
|
7218
|
+
for (const navDiff of diff.navigation) {
|
|
7219
|
+
if (navDiff.type === "unchanged") continue;
|
|
7220
|
+
const localMenu = menuLookup.get(navDiff.name);
|
|
7221
|
+
if (!localMenu) continue;
|
|
7222
|
+
try {
|
|
7223
|
+
if (options.dryRun) ;
|
|
7224
|
+
await client.navigation.upsert({
|
|
7225
|
+
name: localMenu.name,
|
|
7226
|
+
items: localMenu.items
|
|
7227
|
+
});
|
|
7228
|
+
if (navDiff.type === "create") {
|
|
7229
|
+
result.navigation.created++;
|
|
7230
|
+
} else {
|
|
7231
|
+
result.navigation.updated++;
|
|
7232
|
+
}
|
|
7233
|
+
} catch (error) {
|
|
7234
|
+
result.navigation.failed++;
|
|
7235
|
+
result.errors.push({
|
|
7236
|
+
resource: "navigation",
|
|
7237
|
+
identifier: navDiff.name,
|
|
7238
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7239
|
+
});
|
|
7240
|
+
}
|
|
7241
|
+
}
|
|
7242
|
+
}
|
|
7243
|
+
async function syncSettings(client, diff, local, options, result) {
|
|
7244
|
+
if (!diff.settings || !local.settings) return;
|
|
7245
|
+
try {
|
|
7246
|
+
if (options.dryRun) ;
|
|
7247
|
+
await client.settings.update({
|
|
7248
|
+
homepageId: local.settings.homepageId,
|
|
7249
|
+
seoDefaults: local.settings.seoDefaults
|
|
7250
|
+
});
|
|
7251
|
+
result.settings.updated++;
|
|
7252
|
+
} catch (error) {
|
|
7253
|
+
result.settings.failed++;
|
|
7254
|
+
result.errors.push({
|
|
7255
|
+
resource: "settings",
|
|
7256
|
+
identifier: "site",
|
|
7257
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7258
|
+
});
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
7261
|
+
async function executeSyncPlan(client, diff, local, options, output) {
|
|
7262
|
+
const result = createEmptyResult();
|
|
7263
|
+
const totalOps = diff.summary.entries.create + diff.summary.entries.update + diff.summary.pages.create + diff.summary.pages.update + diff.summary.blocks.create + diff.summary.blocks.update + diff.summary.blocks.delete + diff.summary.navigation.create + diff.summary.navigation.update + diff.summary.settings.update;
|
|
7264
|
+
if (totalOps === 0) {
|
|
7265
|
+
output.info("No changes to sync");
|
|
7266
|
+
return result;
|
|
7267
|
+
}
|
|
7268
|
+
const prefix = "Syncing";
|
|
7269
|
+
output.info(`${prefix} ${totalOps} operations...`);
|
|
7270
|
+
await syncEntries(client, diff, local, options, result);
|
|
7271
|
+
await syncPages(client, diff, local, options, result);
|
|
7272
|
+
await syncNavigation(client, diff, local, options, result);
|
|
7273
|
+
await syncSettings(client, diff, local, options, result);
|
|
7274
|
+
return result;
|
|
7275
|
+
}
|
|
7276
|
+
function formatSyncResult(result, dryRun) {
|
|
7277
|
+
const lines = [];
|
|
7278
|
+
const verb = "";
|
|
7279
|
+
if (result.entries.created > 0 || result.entries.updated > 0) {
|
|
7280
|
+
lines.push(
|
|
7281
|
+
`Entries: ${verb} created ${result.entries.created}, updated ${result.entries.updated}` + (result.entries.failed > 0 ? `, failed ${result.entries.failed}` : "")
|
|
7282
|
+
);
|
|
7283
|
+
}
|
|
7284
|
+
if (result.pages.created > 0 || result.pages.updated > 0) {
|
|
7285
|
+
lines.push(
|
|
7286
|
+
`Pages: ${verb} created ${result.pages.created}, updated ${result.pages.updated}` + (result.pages.failed > 0 ? `, failed ${result.pages.failed}` : "")
|
|
7287
|
+
);
|
|
7288
|
+
}
|
|
7289
|
+
if (result.blocks.created > 0 || result.blocks.updated > 0 || result.blocks.deleted > 0) {
|
|
7290
|
+
lines.push(
|
|
7291
|
+
`Blocks: ${verb} created ${result.blocks.created}, updated ${result.blocks.updated}, deleted ${result.blocks.deleted}` + (result.blocks.failed > 0 ? `, failed ${result.blocks.failed}` : "")
|
|
7292
|
+
);
|
|
7293
|
+
}
|
|
7294
|
+
if (result.navigation.created > 0 || result.navigation.updated > 0) {
|
|
7295
|
+
lines.push(
|
|
7296
|
+
`Navigation: ${verb} created ${result.navigation.created}, updated ${result.navigation.updated}` + (result.navigation.failed > 0 ? `, failed ${result.navigation.failed}` : "")
|
|
7297
|
+
);
|
|
7298
|
+
}
|
|
7299
|
+
if (result.settings.updated > 0) {
|
|
7300
|
+
lines.push(
|
|
7301
|
+
`Settings: ${verb} updated ${result.settings.updated}` + (result.settings.failed > 0 ? `, failed ${result.settings.failed}` : "")
|
|
7302
|
+
);
|
|
7303
|
+
}
|
|
7304
|
+
if (result.errors.length > 0) {
|
|
7305
|
+
lines.push("\nErrors:");
|
|
7306
|
+
for (const error of result.errors) {
|
|
7307
|
+
lines.push(` ${error.resource}/${error.identifier}: ${error.error}`);
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
if (lines.length === 0) {
|
|
7311
|
+
return "Everything is in sync - no changes needed";
|
|
7312
|
+
}
|
|
7313
|
+
return lines.join("\n");
|
|
7314
|
+
}
|
|
7315
|
+
|
|
7316
|
+
// src/cli/commands/push.ts
|
|
7317
|
+
function filterLocalContent(localContent, scope, contentType) {
|
|
7318
|
+
if (scope === "entries") {
|
|
7319
|
+
if (contentType) {
|
|
7320
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
7321
|
+
const entries = localContent.entries.get(contentType);
|
|
7322
|
+
if (entries) {
|
|
7323
|
+
filtered.set(contentType, entries);
|
|
7324
|
+
}
|
|
7325
|
+
return { ...localContent, entries: filtered, pages: [], navigation: null };
|
|
7326
|
+
}
|
|
7327
|
+
return { ...localContent, pages: [], navigation: null };
|
|
7328
|
+
}
|
|
7329
|
+
if (scope === "pages") {
|
|
7330
|
+
return { ...localContent, entries: /* @__PURE__ */ new Map(), navigation: null };
|
|
7331
|
+
}
|
|
7332
|
+
if (scope === "navigation") {
|
|
7333
|
+
return { ...localContent, entries: /* @__PURE__ */ new Map(), pages: [] };
|
|
7334
|
+
}
|
|
7335
|
+
return localContent;
|
|
7336
|
+
}
|
|
7337
|
+
function displayDiff(output, diff, config3, dryRun, isRemote, jsonOutput, jsonDiffMode, localContent, remoteContent) {
|
|
7338
|
+
const envLabel = isRemote ? "REMOTE" : "LOCAL";
|
|
7339
|
+
if (jsonDiffMode) {
|
|
7340
|
+
const jsonDiff = buildJsonDiff(diff, localContent, remoteContent, jsonDiffMode);
|
|
7341
|
+
output.json(jsonDiff);
|
|
7342
|
+
return;
|
|
7343
|
+
}
|
|
7344
|
+
if (jsonOutput) {
|
|
7345
|
+
output.json({
|
|
7346
|
+
summary: diff.summary,
|
|
7347
|
+
syncConfig: config3.sync,
|
|
7348
|
+
dryRun
|
|
7349
|
+
});
|
|
7350
|
+
} else {
|
|
7351
|
+
if (dryRun) {
|
|
7352
|
+
output.info(`[DRY RUN] Changes that would be pushed to ${envLabel}:`);
|
|
7353
|
+
} else {
|
|
7354
|
+
output.info(`Changes to push to ${envLabel}:`);
|
|
7355
|
+
}
|
|
7356
|
+
output.info(formatDiffSummary(diff));
|
|
7357
|
+
output.info(formatDiffDetail(diff));
|
|
7358
|
+
}
|
|
7359
|
+
}
|
|
7360
|
+
function reportSyncResults(output, result, hasErrors) {
|
|
7361
|
+
if (hasErrors) {
|
|
7362
|
+
output.warn("Push completed with errors");
|
|
7363
|
+
} else {
|
|
7364
|
+
output.success("Push complete");
|
|
7365
|
+
}
|
|
7366
|
+
output.info(formatSyncResult(result));
|
|
7367
|
+
}
|
|
7368
|
+
var pushCommand = new commander.Command("push").description("Push content to CMS").argument("[scope]", "What to push: entries, pages, navigation, or all (default)").argument("[type]", 'Content type (when scope is "entries")').option("--content-dir <dir>", "Content directory (overrides config)").option("--dry-run", "Show what would be pushed without making changes").option("--yes", "Skip confirmation prompt (required for --remote)").option("--force", "Push even if remote content was truncated (may cause incomplete sync)").option("--json-diff [mode]", "Output JSON diff (summary or full)", "summary").addHelpText("after", `
|
|
7369
|
+
Examples:
|
|
7370
|
+
$ riverbankcms push # Push all content
|
|
7371
|
+
$ riverbankcms push --dry-run # Preview changes
|
|
7372
|
+
$ riverbankcms push --remote --yes # Push to production
|
|
7373
|
+
$ riverbankcms push entries # Push all entries
|
|
7374
|
+
$ riverbankcms push entries blog-post # Push specific content type
|
|
7375
|
+
$ riverbankcms push pages # Push pages with blocks
|
|
7376
|
+
$ riverbankcms push navigation # Push navigation menus
|
|
7377
|
+
$ riverbankcms push --content-dir ./src/content # Custom content directory
|
|
7378
|
+
$ riverbankcms push --dry-run --json-diff=summary # JSON summary for agents
|
|
7379
|
+
$ riverbankcms push --dry-run --json-diff=full # Full JSON before/after payloads
|
|
7380
|
+
|
|
7381
|
+
Sync Behavior:
|
|
7382
|
+
Content sync is controlled by your riverbank.config.ts:
|
|
7383
|
+
- sync.existingEntries: 'skip' (default) or 'update'
|
|
7384
|
+
- sync.contentTarget: 'draft' (default) or 'publish'
|
|
7385
|
+
|
|
7386
|
+
Safety:
|
|
7387
|
+
- Local environment: pushes directly
|
|
7388
|
+
- Remote environment (--remote): defaults to dry-run, requires --yes to execute
|
|
7389
|
+
- Use --dry-run to preview changes before pushing
|
|
7390
|
+
`).action(async (scope, type, options, command) => {
|
|
7391
|
+
const { output, isRemote, globalOpts } = getOutputContext(command);
|
|
7392
|
+
let dryRun = options.dryRun ?? false;
|
|
7393
|
+
const jsonDiffMode = options.jsonDiff ? options.jsonDiff === "summary" || options.jsonDiff === "full" ? options.jsonDiff : null : void 0;
|
|
7394
|
+
if (options.jsonDiff && !jsonDiffMode) {
|
|
7395
|
+
output.error("Invalid value for --json-diff. Use summary or full.");
|
|
7396
|
+
return;
|
|
7397
|
+
}
|
|
7398
|
+
if (isRemote && !options.yes) {
|
|
7399
|
+
dryRun = true;
|
|
7400
|
+
output.info("Remote push defaults to dry-run mode. Use --yes to apply changes.");
|
|
7401
|
+
}
|
|
7402
|
+
try {
|
|
7403
|
+
const cliConfig = await loadCliConfig();
|
|
7404
|
+
const contentDir = path9__namespace.resolve(options.contentDir ?? cliConfig.contentDir);
|
|
7405
|
+
if (!await contentDirExists(contentDir)) {
|
|
7406
|
+
return output.error(`Content directory not found: ${contentDir}`, {
|
|
7407
|
+
suggestion: 'Run "riverbankcms pull" first to download content, or specify a different directory with --content-dir'
|
|
7408
|
+
});
|
|
7409
|
+
}
|
|
7410
|
+
const summary = await getContentSummary(contentDir);
|
|
7411
|
+
if (summary.totalEntries === 0 && summary.pageCount === 0 && !summary.hasNavigation) {
|
|
7412
|
+
output.warn("No content found to push");
|
|
7413
|
+
return;
|
|
7414
|
+
}
|
|
7415
|
+
const env = loadEnvironment(isRemote);
|
|
7416
|
+
const client = createManagementClient({
|
|
7417
|
+
dashboardUrl: env.dashboardUrl,
|
|
7418
|
+
managementApiKey: env.managementApiKey,
|
|
7419
|
+
siteId: env.siteId
|
|
7420
|
+
});
|
|
7421
|
+
output.info("Reading local content...");
|
|
7422
|
+
const localContent = await readAllContent(contentDir);
|
|
7423
|
+
output.info("Fetching remote content for comparison...");
|
|
7424
|
+
const remoteContent = await client.pull.all();
|
|
7425
|
+
if (remoteContent.meta.truncated) {
|
|
7426
|
+
output.warn("Warning: Remote content was truncated due to size limits.");
|
|
7427
|
+
output.warn(
|
|
7428
|
+
remoteContent.meta.truncationMessage ?? "Some content types have more than 500 entries. Results may be incomplete."
|
|
7429
|
+
);
|
|
7430
|
+
if (!options.force) {
|
|
7431
|
+
output.error(
|
|
7432
|
+
"Use --force to push anyway, or use type-specific pulls for accuracy.",
|
|
7433
|
+
{ suggestion: "For large sites, push specific content types: riverbankcms push entries blog-post" }
|
|
7434
|
+
);
|
|
7435
|
+
return;
|
|
7436
|
+
}
|
|
7437
|
+
output.warn("Proceeding with push despite truncation (--force flag used)");
|
|
7438
|
+
}
|
|
7439
|
+
const pushScope = scope?.toLowerCase() ?? "all";
|
|
7440
|
+
const filteredLocal = filterLocalContent(localContent, pushScope, type);
|
|
7441
|
+
output.info("Calculating changes...");
|
|
7442
|
+
const diff = calculateDiff(filteredLocal, remoteContent, {
|
|
7443
|
+
existingEntries: cliConfig.sync.existingEntries
|
|
7444
|
+
});
|
|
7445
|
+
if (!hasPendingChanges(diff)) {
|
|
7446
|
+
output.success("No changes detected - content is already in sync");
|
|
7447
|
+
return;
|
|
7448
|
+
}
|
|
7449
|
+
displayDiff(
|
|
7450
|
+
output,
|
|
7451
|
+
diff,
|
|
7452
|
+
cliConfig,
|
|
7453
|
+
dryRun,
|
|
7454
|
+
isRemote,
|
|
7455
|
+
globalOpts.json ?? false,
|
|
7456
|
+
jsonDiffMode ?? void 0,
|
|
7457
|
+
filteredLocal,
|
|
7458
|
+
remoteContent
|
|
7459
|
+
);
|
|
7460
|
+
if (dryRun) {
|
|
7461
|
+
output.info("\nUse --yes (for remote) or remove --dry-run (for local) to apply changes.");
|
|
7462
|
+
return;
|
|
7463
|
+
}
|
|
7464
|
+
output.info("\nApplying changes...");
|
|
7465
|
+
const result = await executeSyncPlan(
|
|
7466
|
+
client,
|
|
7467
|
+
diff,
|
|
7468
|
+
filteredLocal,
|
|
7469
|
+
{ dryRun: false, contentTarget: cliConfig.sync.contentTarget },
|
|
7470
|
+
output
|
|
7471
|
+
);
|
|
7472
|
+
reportSyncResults(output, result, result.errors.length > 0);
|
|
7473
|
+
} catch (error) {
|
|
7474
|
+
handleCommandError(error, output);
|
|
7475
|
+
}
|
|
7476
|
+
});
|
|
7477
|
+
var formatEntryRow = (entry) => [
|
|
7478
|
+
entry.identifier,
|
|
7479
|
+
entry.status,
|
|
7480
|
+
entry.hasUnpublishedChanges ? "Yes" : "No",
|
|
7481
|
+
entry.slug,
|
|
7482
|
+
formatDateShort(entry.updatedAt)
|
|
7483
|
+
];
|
|
7484
|
+
var upsertCommand = new commander.Command("upsert").description("Create or update an entry").argument("<type>", "Content type").argument("<identifier>", "Entry identifier").option("--data <json>", "Entry data as JSON string").option("--file <path>", "Path to JSON file with entry data").option("--slug <slug>", "Entry slug (defaults to identifier)").option("--title <title>", "Entry title").action(
|
|
7485
|
+
withErrorHandling(async (type, identifier, options, command) => {
|
|
7486
|
+
const { output, client } = createCommandContext(command);
|
|
7487
|
+
const data = await parseJsonData(options);
|
|
7488
|
+
output.info(`Upserting entry: ${type}/${identifier}`);
|
|
7489
|
+
const entry = await client.entries.upsert({
|
|
7490
|
+
contentType: type,
|
|
7491
|
+
identifier,
|
|
7492
|
+
data,
|
|
7493
|
+
slug: options.slug,
|
|
7494
|
+
title: options.title
|
|
7495
|
+
});
|
|
7496
|
+
output.success(`Entry upserted: ${entry.identifier}`, {
|
|
7497
|
+
id: entry.id,
|
|
7498
|
+
identifier: entry.identifier,
|
|
7499
|
+
contentType: entry.contentType,
|
|
7500
|
+
status: entry.status
|
|
7501
|
+
});
|
|
7502
|
+
})
|
|
7503
|
+
);
|
|
7504
|
+
var publishCommand = createPublishCommand({
|
|
7505
|
+
resourceName: "entry",
|
|
7506
|
+
args: ["type", "identifier"],
|
|
7507
|
+
action: (client, type, identifier) => client.entries.publish(type, identifier)
|
|
7508
|
+
});
|
|
7509
|
+
var unpublishCommand = createUnpublishCommand({
|
|
7510
|
+
resourceName: "entry",
|
|
7511
|
+
args: ["type", "identifier"],
|
|
7512
|
+
action: (client, type, identifier) => client.entries.unpublish(type, identifier)
|
|
7513
|
+
});
|
|
7514
|
+
var getCommand = createGetCommand({
|
|
7515
|
+
resourceName: "entry",
|
|
7516
|
+
args: ["type", "identifier"],
|
|
7517
|
+
get: (client, type, identifier) => client.entries.get(type, identifier),
|
|
7518
|
+
formatOutput: (entry) => ({
|
|
7519
|
+
id: entry.id,
|
|
7520
|
+
contentType: entry.contentType,
|
|
7521
|
+
status: entry.status,
|
|
7522
|
+
hasUnpublishedChanges: entry.hasUnpublishedChanges,
|
|
7523
|
+
slug: entry.slug,
|
|
7524
|
+
data: entry.data,
|
|
7525
|
+
createdAt: entry.createdAt,
|
|
7526
|
+
updatedAt: entry.updatedAt,
|
|
7527
|
+
publishedAt: entry.publishedAt
|
|
7528
|
+
})
|
|
7529
|
+
});
|
|
7530
|
+
var listCommand = createListCommand({
|
|
7531
|
+
resourceName: "entry",
|
|
7532
|
+
resourceNamePlural: "entries",
|
|
7533
|
+
args: ["type"],
|
|
7534
|
+
hasPagination: true,
|
|
7535
|
+
list: (client, args, pagination) => client.entries.list(args[0], pagination),
|
|
7536
|
+
tableHeaders: ["Identifier", "Status", "Unpublished Changes", "Slug", "Updated"],
|
|
7537
|
+
formatRow: formatEntryRow
|
|
7538
|
+
});
|
|
7539
|
+
var entryCommand = new commander.Command("entry").description("Manage content entries").addHelpText("after", `
|
|
7540
|
+
Examples:
|
|
7541
|
+
$ riverbankcms entry upsert blog-post my-post --data '{"title": "Hello"}'
|
|
7542
|
+
$ riverbankcms entry upsert blog-post my-post --file ./post-data.json
|
|
7543
|
+
$ riverbankcms entry publish blog-post my-post
|
|
7544
|
+
$ riverbankcms entry unpublish blog-post my-post
|
|
7545
|
+
$ riverbankcms entry get blog-post my-post
|
|
7546
|
+
$ riverbankcms entry list blog-post --limit 10 --page 1
|
|
7547
|
+
`).addCommand(upsertCommand).addCommand(publishCommand).addCommand(unpublishCommand).addCommand(getCommand).addCommand(listCommand);
|
|
7548
|
+
var formatPageRow = (page) => [
|
|
7549
|
+
page.identifier ?? "(no identifier)",
|
|
7550
|
+
page.title,
|
|
7551
|
+
page.path,
|
|
7552
|
+
page.status,
|
|
7553
|
+
page.hasUnpublishedChanges ? "Yes" : "No",
|
|
7554
|
+
formatDateShort(page.updatedAt)
|
|
7555
|
+
];
|
|
7556
|
+
var upsertCommand2 = new commander.Command("upsert").description("Create or update a page").argument("<identifier>", "Page identifier").requiredOption("--title <title>", "Page title").requiredOption("--path <path>", "Page path (e.g., /about)").option("--seo-title <title>", "SEO title").option("--seo-description <description>", "SEO description").action(
|
|
7557
|
+
withErrorHandling(async (identifier, options, command) => {
|
|
7558
|
+
const { output, client } = createCommandContext(command);
|
|
7559
|
+
output.info(`Upserting page: ${identifier}`);
|
|
7560
|
+
const page = await client.pages.upsert({
|
|
7561
|
+
identifier,
|
|
7562
|
+
title: options.title,
|
|
7563
|
+
path: options.path,
|
|
7564
|
+
seoTitle: options.seoTitle,
|
|
7565
|
+
seoDescription: options.seoDescription
|
|
7566
|
+
});
|
|
7567
|
+
output.success(`Page upserted: ${page.identifier}`, {
|
|
7568
|
+
id: page.id,
|
|
7569
|
+
identifier: page.identifier,
|
|
7570
|
+
path: page.path,
|
|
7571
|
+
status: page.status
|
|
7572
|
+
});
|
|
7573
|
+
})
|
|
7574
|
+
);
|
|
7575
|
+
var publishCommand2 = createPublishCommand({
|
|
7576
|
+
resourceName: "page",
|
|
7577
|
+
args: ["identifier"],
|
|
7578
|
+
action: (client, identifier) => client.pages.publish(identifier)
|
|
7579
|
+
});
|
|
7580
|
+
var unpublishCommand2 = createUnpublishCommand({
|
|
7581
|
+
resourceName: "page",
|
|
7582
|
+
args: ["identifier"],
|
|
7583
|
+
action: (client, identifier) => client.pages.unpublish(identifier)
|
|
7584
|
+
});
|
|
7585
|
+
var getCommand2 = createGetCommand({
|
|
7586
|
+
resourceName: "page",
|
|
7587
|
+
args: ["identifier"],
|
|
7588
|
+
get: (client, identifier) => client.pages.get(identifier),
|
|
7589
|
+
formatOutput: (page) => ({
|
|
7590
|
+
id: page.id,
|
|
7591
|
+
title: page.title,
|
|
7592
|
+
path: page.path,
|
|
7593
|
+
status: page.status,
|
|
7594
|
+
hasUnpublishedChanges: page.hasUnpublishedChanges,
|
|
7595
|
+
createdAt: page.createdAt,
|
|
7596
|
+
updatedAt: page.updatedAt,
|
|
7597
|
+
publishedAt: page.publishedAt
|
|
7598
|
+
})
|
|
7599
|
+
});
|
|
7600
|
+
var listCommand2 = createListCommand({
|
|
7601
|
+
resourceName: "page",
|
|
7602
|
+
resourceNamePlural: "pages",
|
|
7603
|
+
args: [],
|
|
7604
|
+
hasPagination: true,
|
|
7605
|
+
list: (client, _args, pagination) => client.pages.list(pagination),
|
|
7606
|
+
tableHeaders: ["Identifier", "Title", "Path", "Status", "Unpublished Changes", "Updated"],
|
|
7607
|
+
formatRow: formatPageRow
|
|
7608
|
+
});
|
|
7609
|
+
var pageCommand = new commander.Command("page").description("Manage pages").addHelpText("after", `
|
|
7610
|
+
Examples:
|
|
7611
|
+
$ riverbankcms page upsert about --title "About Us" --path /about
|
|
7612
|
+
$ riverbankcms page upsert about --title "About Us" --path /about --seo-title "About Our Company"
|
|
7613
|
+
$ riverbankcms page publish about
|
|
7614
|
+
$ riverbankcms page unpublish about
|
|
7615
|
+
$ riverbankcms page get about
|
|
7616
|
+
$ riverbankcms page list --limit 10 --page 1
|
|
7617
|
+
`).addCommand(upsertCommand2).addCommand(publishCommand2).addCommand(unpublishCommand2).addCommand(getCommand2).addCommand(listCommand2);
|
|
7618
|
+
var formatBlockRow = (block) => [
|
|
7619
|
+
block.identifier ?? "(no identifier)",
|
|
7620
|
+
block.kind,
|
|
7621
|
+
String(block.position),
|
|
7622
|
+
formatDateShort(block.updatedAt)
|
|
7623
|
+
];
|
|
7624
|
+
var upsertCommand3 = new commander.Command("upsert").description("Create or update a block on a page").argument("<page-identifier>", "Page identifier").argument("<block-identifier>", "Block identifier").requiredOption("--kind <kind>", "Block kind (e.g., block.hero, block.bodyText)").option("--data <json>", "Block data as JSON string").option("--file <path>", "Path to JSON file with block data").option("--position <n>", "Block position (0-indexed)").action(
|
|
7625
|
+
withErrorHandling(
|
|
7626
|
+
async (pageIdentifier, blockIdentifier, options, command) => {
|
|
7627
|
+
const { output, client } = createCommandContext(command);
|
|
7628
|
+
const data = await parseJsonData(options);
|
|
7629
|
+
output.info(`Upserting block: ${pageIdentifier}/${blockIdentifier}`);
|
|
7630
|
+
const block = await client.blocks.upsert(pageIdentifier, {
|
|
7631
|
+
identifier: blockIdentifier,
|
|
7632
|
+
kind: options.kind,
|
|
7633
|
+
data,
|
|
7634
|
+
position: options.position ? parseInt(options.position, 10) : void 0
|
|
7635
|
+
});
|
|
7636
|
+
output.success(`Block upserted: ${block.identifier}`, {
|
|
7637
|
+
id: block.id,
|
|
7638
|
+
identifier: block.identifier,
|
|
7639
|
+
kind: block.kind,
|
|
7640
|
+
position: block.position
|
|
7641
|
+
});
|
|
7642
|
+
}
|
|
7643
|
+
)
|
|
7644
|
+
);
|
|
7645
|
+
var reorderCommand = new commander.Command("reorder").description("Reorder blocks on a page").argument("<page-identifier>", "Page identifier").argument("<identifiers...>", "Block identifiers in desired order").action(
|
|
7646
|
+
withErrorHandling(
|
|
7647
|
+
async (pageIdentifier, identifiers, _options, command) => {
|
|
7648
|
+
const { output, client } = createCommandContext(command);
|
|
7649
|
+
if (identifiers.length < 2) {
|
|
7650
|
+
return output.error("At least 2 block identifiers are required for reordering");
|
|
7651
|
+
}
|
|
7652
|
+
output.info(`Reordering blocks on page: ${pageIdentifier}`);
|
|
7653
|
+
await client.blocks.reorder(pageIdentifier, identifiers);
|
|
7654
|
+
output.success(`Blocks reordered`, {
|
|
7655
|
+
page: pageIdentifier,
|
|
7656
|
+
order: identifiers
|
|
7657
|
+
});
|
|
7658
|
+
}
|
|
7659
|
+
)
|
|
7660
|
+
);
|
|
7661
|
+
var listCommand3 = createListCommand({
|
|
7662
|
+
resourceName: "block",
|
|
7663
|
+
resourceNamePlural: "blocks",
|
|
7664
|
+
args: ["page-identifier"],
|
|
7665
|
+
hasPagination: false,
|
|
7666
|
+
list: async (client, args) => ({ items: await client.blocks.list(args[0]) }),
|
|
7667
|
+
tableHeaders: ["Identifier", "Kind", "Position", "Updated"],
|
|
7668
|
+
formatRow: formatBlockRow
|
|
7669
|
+
});
|
|
7670
|
+
var getCommand3 = createGetCommand({
|
|
7671
|
+
resourceName: "block",
|
|
7672
|
+
args: ["page-identifier", "block-identifier"],
|
|
7673
|
+
get: (client, pageIdentifier, blockIdentifier) => client.blocks.get(pageIdentifier, blockIdentifier),
|
|
7674
|
+
formatOutput: (block) => ({
|
|
7675
|
+
id: block.id,
|
|
7676
|
+
kind: block.kind,
|
|
7677
|
+
position: block.position,
|
|
7678
|
+
data: block.data,
|
|
7679
|
+
createdAt: block.createdAt,
|
|
7680
|
+
updatedAt: block.updatedAt
|
|
7681
|
+
})
|
|
7682
|
+
});
|
|
7683
|
+
var blockCommand = new commander.Command("block").description("Manage page blocks").addHelpText("after", `
|
|
7684
|
+
Examples:
|
|
7685
|
+
$ riverbankcms block upsert home hero-main --kind block.hero --data '{"heading": "Welcome"}'
|
|
7686
|
+
$ riverbankcms block upsert home hero-main --kind block.hero --file ./hero-data.json
|
|
7687
|
+
$ riverbankcms block reorder home hero-main content-section cta-bottom
|
|
7688
|
+
$ riverbankcms block list home
|
|
7689
|
+
$ riverbankcms block get home hero-main
|
|
7690
|
+
|
|
7691
|
+
To delete a block, use: riverbankcms delete block <page-id> <block-id>
|
|
7692
|
+
`).addCommand(upsertCommand3).addCommand(reorderCommand).addCommand(listCommand3).addCommand(getCommand3);
|
|
7693
|
+
var formatMenuRow = (menu) => {
|
|
7694
|
+
const itemCount = countItems(menu.items);
|
|
7695
|
+
return [
|
|
7696
|
+
menu.name,
|
|
7697
|
+
String(itemCount),
|
|
7698
|
+
formatDateShort(menu.updatedAt)
|
|
7699
|
+
];
|
|
7700
|
+
};
|
|
7701
|
+
function countItems(items) {
|
|
7702
|
+
let count = items.length;
|
|
7703
|
+
for (const item of items) {
|
|
7704
|
+
if (item.children) {
|
|
7705
|
+
count += countItems(item.children);
|
|
7706
|
+
}
|
|
7707
|
+
}
|
|
7708
|
+
return count;
|
|
7709
|
+
}
|
|
7710
|
+
var upsertCommand4 = new commander.Command("upsert").description("Create or update a navigation menu").argument("<menu-name>", "Navigation menu name").option("--file <path>", "Path to JSON file with menu items").option("--data <json>", "Menu items as JSON string").action(
|
|
7711
|
+
withErrorHandling(async (menuName, options, command) => {
|
|
7712
|
+
const { output, client } = createCommandContext(command);
|
|
7713
|
+
const items = await parseJsonArray(options);
|
|
7714
|
+
output.info(`Upserting navigation menu: ${menuName}`);
|
|
7715
|
+
const menu = await client.navigation.upsert({
|
|
7716
|
+
name: menuName,
|
|
7717
|
+
items
|
|
7718
|
+
});
|
|
7719
|
+
output.success(`Navigation menu upserted: ${menu.name}`, {
|
|
7720
|
+
id: menu.id,
|
|
7721
|
+
name: menu.name,
|
|
7722
|
+
itemCount: countItems(menu.items)
|
|
7723
|
+
});
|
|
7724
|
+
})
|
|
7725
|
+
);
|
|
7726
|
+
var getCommand4 = createGetCommand({
|
|
7727
|
+
resourceName: "navigation menu",
|
|
7728
|
+
args: ["menu-name"],
|
|
7729
|
+
get: (client, menuName) => client.navigation.get(menuName),
|
|
7730
|
+
formatOutput: (menu) => ({
|
|
7731
|
+
id: menu.id,
|
|
7732
|
+
name: menu.name,
|
|
7733
|
+
items: menu.items,
|
|
7734
|
+
createdAt: menu.createdAt,
|
|
7735
|
+
updatedAt: menu.updatedAt
|
|
7736
|
+
})
|
|
7737
|
+
});
|
|
7738
|
+
var listCommand4 = createListCommand({
|
|
7739
|
+
resourceName: "navigation menu",
|
|
7740
|
+
resourceNamePlural: "navigation menus",
|
|
7741
|
+
args: [],
|
|
7742
|
+
hasPagination: false,
|
|
7743
|
+
list: async (client) => ({ items: await client.navigation.list() }),
|
|
7744
|
+
tableHeaders: ["Name", "Items", "Updated"],
|
|
7745
|
+
formatRow: formatMenuRow
|
|
7746
|
+
});
|
|
7747
|
+
var navigationCommand = new commander.Command("navigation").description("Manage navigation menus").addHelpText("after", `
|
|
7748
|
+
Examples:
|
|
7749
|
+
$ riverbankcms navigation upsert main --file ./main-nav.json
|
|
7750
|
+
$ riverbankcms navigation upsert footer --data '[{"label": "Home", "url": "/"}]'
|
|
7751
|
+
$ riverbankcms navigation get main
|
|
7752
|
+
$ riverbankcms navigation list
|
|
7753
|
+
|
|
7754
|
+
JSON file format:
|
|
7755
|
+
{
|
|
7756
|
+
"items": [
|
|
7757
|
+
{ "label": "Home", "url": "/" },
|
|
7758
|
+
{ "label": "About", "url": "/about", "children": [
|
|
7759
|
+
{ "label": "Team", "url": "/about/team" }
|
|
7760
|
+
]}
|
|
7761
|
+
]
|
|
7762
|
+
}
|
|
7763
|
+
`).addCommand(upsertCommand4).addCommand(getCommand4).addCommand(listCommand4);
|
|
7764
|
+
var deleteEntryCommand = new commander.Command("entry").description("Delete a content entry").argument("<type>", "Content type").argument("<identifier>", "Entry identifier").option("--yes", "Skip confirmation prompt (required for --remote)").action(
|
|
7765
|
+
withConfirmation(
|
|
7766
|
+
(type, identifier) => `Delete entry "${type}/${identifier}"?`,
|
|
7767
|
+
async (ctx, type, identifier) => {
|
|
7768
|
+
ctx.output.info(`Deleting entry: ${type}/${identifier}`);
|
|
7769
|
+
await ctx.client.entries.delete(type, identifier);
|
|
7770
|
+
ctx.output.success(`Entry deleted: ${identifier}`);
|
|
7771
|
+
}
|
|
7772
|
+
)
|
|
7773
|
+
);
|
|
7774
|
+
var deleteBlockCommand = new commander.Command("block").description("Delete a block from a page").argument("<page-identifier>", "Page identifier").argument("<block-identifier>", "Block identifier").option("--yes", "Skip confirmation prompt (required for --remote)").action(
|
|
7775
|
+
withConfirmation(
|
|
7776
|
+
(pageIdentifier, blockIdentifier) => `Delete block "${blockIdentifier}" from page "${pageIdentifier}"?`,
|
|
7777
|
+
async (ctx, pageIdentifier, blockIdentifier) => {
|
|
7778
|
+
ctx.output.info(`Deleting block: ${pageIdentifier}/${blockIdentifier}`);
|
|
7779
|
+
await ctx.client.blocks.delete(pageIdentifier, blockIdentifier);
|
|
7780
|
+
ctx.output.success(`Block deleted: ${blockIdentifier}`);
|
|
7781
|
+
}
|
|
7782
|
+
)
|
|
7783
|
+
);
|
|
7784
|
+
var deleteCommand = new commander.Command("delete").description("Delete content from CMS").addHelpText("after", `
|
|
7785
|
+
Examples:
|
|
7786
|
+
$ riverbankcms delete entry blog-post my-post
|
|
7787
|
+
$ riverbankcms delete entry blog-post my-post --yes
|
|
7788
|
+
$ riverbankcms delete block home hero-main --yes
|
|
7789
|
+
$ riverbankcms delete entry blog-post my-post --remote --yes
|
|
7790
|
+
|
|
7791
|
+
Note: Remote delete operations require --yes flag for safety.
|
|
7792
|
+
`).addCommand(deleteEntryCommand).addCommand(deleteBlockCommand);
|
|
7793
|
+
|
|
7794
|
+
// src/cli/preview/template.ts
|
|
7795
|
+
function buildPreviewHtml({ html, cssText, title }) {
|
|
7796
|
+
const safeTitle = title ?? "Block Preview";
|
|
7797
|
+
return `<!doctype html>
|
|
7798
|
+
<html lang="en">
|
|
7799
|
+
<head>
|
|
7800
|
+
<meta charset="utf-8" />
|
|
7801
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7802
|
+
<title>${safeTitle}</title>
|
|
7803
|
+
<style>
|
|
7804
|
+
${cssText}
|
|
7805
|
+
</style>
|
|
7806
|
+
</head>
|
|
7807
|
+
<body>
|
|
7808
|
+
${html}
|
|
7809
|
+
</body>
|
|
7810
|
+
</html>`;
|
|
7811
|
+
}
|
|
7812
|
+
|
|
7813
|
+
// src/cli/preview/playwright.config.ts
|
|
7814
|
+
var config = {
|
|
7815
|
+
use: {
|
|
7816
|
+
viewport: { width: 1280, height: 720 },
|
|
7817
|
+
deviceScaleFactor: 2,
|
|
7818
|
+
colorScheme: "light"
|
|
7819
|
+
}
|
|
7820
|
+
};
|
|
7821
|
+
var playwright_config_default = config;
|
|
7822
|
+
|
|
7823
|
+
// src/cli/preview/screenshot.ts
|
|
7824
|
+
async function captureScreenshot(options) {
|
|
7825
|
+
const { html, outputPath, viewport } = options;
|
|
7826
|
+
let playwright;
|
|
7827
|
+
try {
|
|
7828
|
+
playwright = await import('playwright');
|
|
7829
|
+
} catch (error) {
|
|
7830
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7831
|
+
throw new Error(`Playwright is not installed. Install it to use --screenshot. (${message})`);
|
|
7832
|
+
}
|
|
7833
|
+
const browser = await playwright.chromium.launch();
|
|
7834
|
+
const page = await browser.newPage({
|
|
7835
|
+
viewport: viewport ?? playwright_config_default.use.viewport,
|
|
7836
|
+
deviceScaleFactor: playwright_config_default.use.deviceScaleFactor,
|
|
7837
|
+
colorScheme: playwright_config_default.use.colorScheme
|
|
7838
|
+
});
|
|
7839
|
+
await page.setContent(html, { waitUntil: "load" });
|
|
7840
|
+
await page.screenshot({ path: path9__namespace.resolve(outputPath), fullPage: true });
|
|
7841
|
+
await browser.close();
|
|
7842
|
+
}
|
|
7843
|
+
function resolveOutputMode(options) {
|
|
7844
|
+
const modes = [options.terminal, options.open, options.screenshot].filter(Boolean).length;
|
|
7845
|
+
if (modes > 1) {
|
|
7846
|
+
throw new Error("Choose only one output mode: --terminal, --open, or --screenshot");
|
|
7847
|
+
}
|
|
7848
|
+
if (options.open) return "open";
|
|
7849
|
+
if (options.screenshot) return "screenshot";
|
|
7850
|
+
return "terminal";
|
|
7851
|
+
}
|
|
7852
|
+
async function writePreviewHtmlFile(html) {
|
|
7853
|
+
const dir = await fs5__namespace.mkdtemp(path9__namespace.join(os__namespace.tmpdir(), "riverbank-preview-"));
|
|
7854
|
+
const filePath = path9__namespace.join(dir, "index.html");
|
|
7855
|
+
await fs5__namespace.writeFile(filePath, html, "utf-8");
|
|
7856
|
+
return filePath;
|
|
7857
|
+
}
|
|
7858
|
+
async function openPreviewFile(filePath) {
|
|
7859
|
+
const platform = process.platform;
|
|
7860
|
+
if (platform === "darwin") {
|
|
7861
|
+
await runCommand("open", [filePath]);
|
|
7862
|
+
return;
|
|
7863
|
+
}
|
|
7864
|
+
if (platform === "win32") {
|
|
7865
|
+
await runCommand("cmd", ["/c", "start", '""', filePath]);
|
|
7866
|
+
return;
|
|
7867
|
+
}
|
|
7868
|
+
await runCommand("xdg-open", [filePath]);
|
|
7869
|
+
}
|
|
7870
|
+
function runCommand(command, args) {
|
|
7871
|
+
return new Promise((resolve8, reject) => {
|
|
7872
|
+
const child = child_process.spawn(command, args, { stdio: "ignore", detached: true });
|
|
7873
|
+
child.on("error", reject);
|
|
7874
|
+
child.unref();
|
|
7875
|
+
resolve8();
|
|
7876
|
+
});
|
|
7877
|
+
}
|
|
7878
|
+
|
|
7879
|
+
// src/cli/commands/preview.ts
|
|
7880
|
+
async function readCssFiles(paths) {
|
|
7881
|
+
if (!paths || paths.length === 0) return "";
|
|
7882
|
+
const chunks = await Promise.all(paths.map(async (filePath) => {
|
|
7883
|
+
const resolved = path9__namespace.resolve(filePath);
|
|
7884
|
+
const { readFile: readFile4 } = await import('fs/promises');
|
|
7885
|
+
return readFile4(resolved, "utf-8");
|
|
7886
|
+
}));
|
|
7887
|
+
return chunks.join("\n");
|
|
7888
|
+
}
|
|
7889
|
+
function formatValidationIssues(response) {
|
|
7890
|
+
return response.validation.issues.map((issue) => {
|
|
7891
|
+
const pathText = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
7892
|
+
return `${pathText}: ${issue.message}`;
|
|
7893
|
+
});
|
|
7894
|
+
}
|
|
7895
|
+
var previewCommand = new commander.Command("preview").description("Render a block preview").argument("<kind>", "Block kind (e.g., block.hero)").option("--data <json>", "Inline JSON string for block content").option("--file <path>", "Path to JSON file with block content").option("--validation <mode>", "Validation mode (strict or lenient)", "strict").option("--preview-stage <stage>", "Content stage (published or preview)", "preview").option("--theme-id <id>", "Theme ID override").option("--page-id <id>", "Page ID context for loaders").option("--content-entry-id <id>", "Content entry ID context for loaders").option("--css <paths...>", "Append compiled CSS files for site-specific utilities").option("--terminal", "Print HTML output to the terminal (default)").option("--open", "Open the preview in a browser").option("--screenshot", "Capture a screenshot using Playwright").option("--output <path>", "Screenshot output path (default: ./block-preview.png)").action(
|
|
7896
|
+
withErrorHandling(async (kind, options, command) => {
|
|
7897
|
+
const { output, client, isJsonOutput } = createCommandContext(command);
|
|
7898
|
+
const mode = resolveOutputMode(options);
|
|
7899
|
+
const data = await parseJsonData({ data: options.data, file: options.file });
|
|
7900
|
+
const response = await client.preview.block({
|
|
7901
|
+
kind,
|
|
7902
|
+
data,
|
|
7903
|
+
themeId: options.themeId,
|
|
7904
|
+
pageId: options.pageId,
|
|
7905
|
+
contentEntryId: options.contentEntryId,
|
|
7906
|
+
previewStage: options.previewStage,
|
|
7907
|
+
validationMode: options.validation ?? "strict"
|
|
7908
|
+
});
|
|
7909
|
+
const extraCss = await readCssFiles(options.css);
|
|
7910
|
+
const cssText = extraCss ? `${response.cssText}
|
|
7911
|
+
${extraCss}` : response.cssText;
|
|
7912
|
+
const htmlDocument = buildPreviewHtml({ html: response.html, cssText });
|
|
7913
|
+
if (!response.validation.valid) {
|
|
7914
|
+
const issues = formatValidationIssues(response);
|
|
7915
|
+
output.warn("Block validation failed (lenient mode)", { issues });
|
|
7916
|
+
}
|
|
7917
|
+
if (isJsonOutput) {
|
|
7918
|
+
return output.json({ success: true, data: { ...response, cssText } });
|
|
7919
|
+
}
|
|
7920
|
+
if (mode === "open") {
|
|
7921
|
+
const filePath = await writePreviewHtmlFile(htmlDocument);
|
|
7922
|
+
await openPreviewFile(filePath);
|
|
7923
|
+
output.success("Opened preview in browser", { filePath });
|
|
7924
|
+
return;
|
|
7925
|
+
}
|
|
7926
|
+
if (mode === "screenshot") {
|
|
7927
|
+
const outputPath = options.output ?? path9__namespace.resolve(process.cwd(), "block-preview.png");
|
|
7928
|
+
await captureScreenshot({ html: htmlDocument, outputPath });
|
|
7929
|
+
output.success("Screenshot saved", { outputPath });
|
|
7930
|
+
return;
|
|
7931
|
+
}
|
|
7932
|
+
output.info("Block preview HTML:");
|
|
7933
|
+
console.log(htmlDocument);
|
|
7934
|
+
})
|
|
7935
|
+
);
|
|
7936
|
+
|
|
7937
|
+
// src/cli/init-docs/templates.ts
|
|
7938
|
+
var GENERATED_START = "<!-- RIVERBANK-GENERATED-START -->";
|
|
7939
|
+
var GENERATED_END = "<!-- RIVERBANK-GENERATED-END -->";
|
|
7940
|
+
function getGeneratedMarkers() {
|
|
7941
|
+
return { start: GENERATED_START, end: GENERATED_END };
|
|
7942
|
+
}
|
|
7943
|
+
function buildSchemaTemplate(state) {
|
|
7944
|
+
const header = [
|
|
7945
|
+
"# Site Schema",
|
|
7946
|
+
"",
|
|
7947
|
+
"This document captures the content model and SDK configuration for this site.",
|
|
7948
|
+
"The generated section is updated by `riverbankcms init-docs`.",
|
|
7949
|
+
""
|
|
7950
|
+
].join("\n");
|
|
7951
|
+
const generated = buildSchemaGeneratedSection(state);
|
|
7952
|
+
return `${header}${GENERATED_START}
|
|
7953
|
+
${generated}
|
|
7954
|
+
${GENERATED_END}
|
|
7955
|
+
`;
|
|
7956
|
+
}
|
|
7957
|
+
function buildBlockTypesTemplate() {
|
|
7958
|
+
const header = [
|
|
7959
|
+
"# System Block Types",
|
|
7960
|
+
"",
|
|
7961
|
+
"Reference list of all built-in CMS block types.",
|
|
7962
|
+
"The generated section is updated by `riverbankcms init-docs`.",
|
|
7963
|
+
""
|
|
7964
|
+
].join("\n");
|
|
7965
|
+
const generated = buildBlockTypesGeneratedSection();
|
|
7966
|
+
return `${header}${GENERATED_START}
|
|
7967
|
+
${generated}
|
|
7968
|
+
${GENERATED_END}
|
|
7969
|
+
`;
|
|
7970
|
+
}
|
|
7971
|
+
function buildSchemaGeneratedSection(state) {
|
|
7972
|
+
if (!state.config) {
|
|
7973
|
+
return [
|
|
7974
|
+
"## Status",
|
|
7975
|
+
"",
|
|
7976
|
+
state.errorMessage ?? "No SDK config available.",
|
|
7977
|
+
""
|
|
7978
|
+
].join("\n");
|
|
7979
|
+
}
|
|
7980
|
+
const config3 = state.config;
|
|
7981
|
+
const sections = [];
|
|
7982
|
+
sections.push(renderContentTypesSection(config3));
|
|
7983
|
+
sections.push(renderCustomBlocksSection(config3.customBlocks ?? []));
|
|
7984
|
+
sections.push(renderBlockFieldExtensionsSection(config3.blockFieldExtensions));
|
|
7985
|
+
sections.push(renderBlockFieldOptionsSection(config3.blockFieldOptions));
|
|
7986
|
+
return sections.filter(Boolean).join("\n");
|
|
7987
|
+
}
|
|
7988
|
+
function buildBlockTypesGeneratedSection() {
|
|
7989
|
+
const definitions = listBlockDefinitions();
|
|
7990
|
+
const sections = ["## System Blocks", ""];
|
|
7991
|
+
for (const definition of definitions) {
|
|
7992
|
+
const manifest = definition.manifest;
|
|
7993
|
+
sections.push(`### ${manifest.name}`);
|
|
7994
|
+
sections.push("");
|
|
7995
|
+
sections.push(`- Title: ${manifest.title}`);
|
|
7996
|
+
if (manifest.description) {
|
|
7997
|
+
sections.push(`- Description: ${manifest.description}`);
|
|
7998
|
+
}
|
|
7999
|
+
if (manifest.category) {
|
|
8000
|
+
sections.push(`- Category: ${manifest.category}`);
|
|
8001
|
+
}
|
|
8002
|
+
if (manifest.tags && manifest.tags.length > 0) {
|
|
8003
|
+
sections.push(`- Tags: ${manifest.tags.join(", ")}`);
|
|
8004
|
+
}
|
|
8005
|
+
if (manifest.contentTypes && manifest.contentTypes.length > 0) {
|
|
8006
|
+
sections.push(`- Content Types: ${manifest.contentTypes.join(", ")}`);
|
|
8007
|
+
}
|
|
8008
|
+
if (manifest.behaviours?.paletteHidden) {
|
|
8009
|
+
sections.push("- Palette Hidden: yes");
|
|
8010
|
+
}
|
|
8011
|
+
sections.push("");
|
|
8012
|
+
sections.push("Fields:");
|
|
8013
|
+
if (manifest.fields.length === 0) {
|
|
8014
|
+
sections.push("- (none)");
|
|
8015
|
+
} else {
|
|
8016
|
+
sections.push(...renderFieldList(manifest.fields, 0));
|
|
8017
|
+
}
|
|
8018
|
+
sections.push("");
|
|
8019
|
+
}
|
|
8020
|
+
return sections.join("\n");
|
|
8021
|
+
}
|
|
8022
|
+
function renderContentTypesSection(config3) {
|
|
8023
|
+
const contentTypes = config3.content?.contentTypes ?? [];
|
|
8024
|
+
const lines = ["## Content Types", ""];
|
|
8025
|
+
if (contentTypes.length === 0) {
|
|
8026
|
+
lines.push("No content types defined.");
|
|
8027
|
+
lines.push("");
|
|
8028
|
+
return lines.join("\n");
|
|
8029
|
+
}
|
|
8030
|
+
for (const type of contentTypes) {
|
|
8031
|
+
lines.push(`### ${type.name} (${type.key})`);
|
|
8032
|
+
lines.push("");
|
|
8033
|
+
if (type.description) {
|
|
8034
|
+
lines.push(type.description);
|
|
8035
|
+
lines.push("");
|
|
8036
|
+
}
|
|
8037
|
+
lines.push(`- Has Pages: ${type.hasPages ? "yes" : "no"}`);
|
|
8038
|
+
if (type.routePattern) lines.push(`- Route Pattern: ${type.routePattern}`);
|
|
8039
|
+
if (type.titleField) lines.push(`- Title Field: ${type.titleField}`);
|
|
8040
|
+
if (type.isSingleton) lines.push("- Singleton: yes");
|
|
8041
|
+
lines.push("");
|
|
8042
|
+
lines.push("Fields:");
|
|
8043
|
+
if (type.fields.length === 0) {
|
|
8044
|
+
lines.push("- (none)");
|
|
8045
|
+
} else {
|
|
8046
|
+
lines.push(...renderFieldList(type.fields, 0));
|
|
8047
|
+
}
|
|
8048
|
+
lines.push("");
|
|
8049
|
+
}
|
|
8050
|
+
return lines.join("\n");
|
|
8051
|
+
}
|
|
8052
|
+
function renderCustomBlocksSection(customBlocks) {
|
|
8053
|
+
const lines = ["## Custom Blocks", ""];
|
|
8054
|
+
if (customBlocks.length === 0) {
|
|
8055
|
+
lines.push("No custom blocks defined.");
|
|
8056
|
+
lines.push("");
|
|
8057
|
+
return lines.join("\n");
|
|
8058
|
+
}
|
|
8059
|
+
for (const block of customBlocks) {
|
|
8060
|
+
lines.push(`### ${block.id}`);
|
|
8061
|
+
lines.push("");
|
|
8062
|
+
lines.push(`- Title: ${block.title}`);
|
|
8063
|
+
if (block.description) lines.push(`- Description: ${block.description}`);
|
|
8064
|
+
if (block.category) lines.push(`- Category: ${block.category}`);
|
|
8065
|
+
if (block.tags && block.tags.length > 0) lines.push(`- Tags: ${block.tags.join(", ")}`);
|
|
8066
|
+
lines.push("");
|
|
8067
|
+
lines.push("Fields:");
|
|
8068
|
+
if (block.fields.length === 0) {
|
|
8069
|
+
lines.push("- (none)");
|
|
8070
|
+
} else {
|
|
8071
|
+
lines.push(...renderFieldList(block.fields, 0));
|
|
8072
|
+
}
|
|
8073
|
+
lines.push("");
|
|
8074
|
+
}
|
|
8075
|
+
return lines.join("\n");
|
|
8076
|
+
}
|
|
8077
|
+
function renderBlockFieldExtensionsSection(extensions) {
|
|
8078
|
+
const lines = ["## Block Field Extensions", ""];
|
|
8079
|
+
if (!extensions || Object.keys(extensions).length === 0) {
|
|
8080
|
+
lines.push("No block field extensions defined.");
|
|
8081
|
+
lines.push("");
|
|
8082
|
+
return lines.join("\n");
|
|
8083
|
+
}
|
|
8084
|
+
for (const [blockId, extension] of Object.entries(extensions)) {
|
|
8085
|
+
lines.push(`### ${blockId}`);
|
|
8086
|
+
lines.push("");
|
|
8087
|
+
if (extension.fields.length === 0) {
|
|
8088
|
+
lines.push("- (none)");
|
|
8089
|
+
} else {
|
|
8090
|
+
lines.push(...renderFieldList(extension.fields, 0));
|
|
8091
|
+
}
|
|
8092
|
+
lines.push("");
|
|
8093
|
+
}
|
|
8094
|
+
return lines.join("\n");
|
|
8095
|
+
}
|
|
8096
|
+
function renderBlockFieldOptionsSection(options) {
|
|
8097
|
+
const lines = ["## Block Field Options", ""];
|
|
8098
|
+
if (!options || Object.keys(options).length === 0) {
|
|
8099
|
+
lines.push("No block field options defined.");
|
|
8100
|
+
lines.push("");
|
|
8101
|
+
return lines.join("\n");
|
|
8102
|
+
}
|
|
8103
|
+
for (const [blockId, fields4] of Object.entries(options)) {
|
|
8104
|
+
lines.push(`### ${blockId}`);
|
|
8105
|
+
lines.push("");
|
|
8106
|
+
for (const [fieldId, config3] of Object.entries(fields4)) {
|
|
8107
|
+
lines.push(`- ${fieldId}`);
|
|
8108
|
+
if (config3.options && config3.options.length > 0) {
|
|
8109
|
+
lines.push(...renderOptionsList(config3.options, 1));
|
|
8110
|
+
}
|
|
8111
|
+
}
|
|
8112
|
+
lines.push("");
|
|
8113
|
+
}
|
|
8114
|
+
return lines.join("\n");
|
|
8115
|
+
}
|
|
8116
|
+
function renderFieldList(fields4, depth) {
|
|
8117
|
+
const lines = [];
|
|
8118
|
+
const indent = " ".repeat(depth);
|
|
8119
|
+
for (const field of fields4) {
|
|
8120
|
+
const required = field.required ? " required" : "";
|
|
8121
|
+
const label = field.label ? ` - ${field.label}` : "";
|
|
8122
|
+
lines.push(`${indent}- ${field.id} (${field.type})${required}${label}`);
|
|
8123
|
+
if ("options" in field && Array.isArray(field.options) && field.options.length > 0) {
|
|
8124
|
+
lines.push(...renderOptionsList(field.options, depth + 1));
|
|
8125
|
+
}
|
|
8126
|
+
if ("fields" in field && Array.isArray(field.fields)) {
|
|
8127
|
+
lines.push(...renderFieldList(field.fields, depth + 1));
|
|
8128
|
+
}
|
|
8129
|
+
if ("schema" in field && field.schema && "fields" in field.schema) {
|
|
8130
|
+
lines.push(...renderFieldList(field.schema.fields, depth + 1));
|
|
8131
|
+
}
|
|
8132
|
+
if ("tabs" in field && Array.isArray(field.tabs)) {
|
|
8133
|
+
for (const tab of field.tabs) {
|
|
8134
|
+
lines.push(`${indent} - tab ${tab.id} (${tab.label})`);
|
|
8135
|
+
lines.push(...renderFieldList(tab.fields, depth + 2));
|
|
8136
|
+
}
|
|
8137
|
+
}
|
|
8138
|
+
}
|
|
8139
|
+
return lines;
|
|
8140
|
+
}
|
|
8141
|
+
function renderOptionsList(options, depth) {
|
|
8142
|
+
const indent = " ".repeat(depth);
|
|
8143
|
+
return options.map((option) => `${indent}- option: ${option.value} (${option.label})`);
|
|
8144
|
+
}
|
|
8145
|
+
|
|
8146
|
+
// src/cli/init-docs/index.ts
|
|
8147
|
+
var AGENTS_START = "<!-- RIVERBANK-CONTEXT-START -->";
|
|
8148
|
+
var AGENTS_END = "<!-- RIVERBANK-CONTEXT-END -->";
|
|
8149
|
+
async function initDocs(options) {
|
|
8150
|
+
const { rootDir, configPath, agentsPath, output } = options;
|
|
8151
|
+
const docsDir = path9__namespace.join(rootDir, "docs");
|
|
8152
|
+
const contextDir = path9__namespace.join(rootDir, "context");
|
|
8153
|
+
const workflowsDir = path9__namespace.join(docsDir, "workflows");
|
|
8154
|
+
const knowledgeDir = path9__namespace.join(contextDir, "knowledge");
|
|
8155
|
+
const brandDir = path9__namespace.join(contextDir, "brand");
|
|
8156
|
+
await ensureDir2(workflowsDir);
|
|
8157
|
+
await ensureDir2(knowledgeDir);
|
|
8158
|
+
await ensureDir2(brandDir);
|
|
8159
|
+
await writeFileIfMissing(path9__namespace.join(docsDir, "cli-reference.md"), cliReferenceTemplate());
|
|
8160
|
+
await writeFileIfMissing(path9__namespace.join(docsDir, "content-management.md"), contentManagementTemplate());
|
|
8161
|
+
await writeFileIfMissing(path9__namespace.join(workflowsDir, "create-page.md"), workflowCreatePageTemplate());
|
|
8162
|
+
await writeFileIfMissing(path9__namespace.join(workflowsDir, "add-block.md"), workflowAddBlockTemplate());
|
|
8163
|
+
await writeFileIfMissing(path9__namespace.join(workflowsDir, "publish-workflow.md"), workflowPublishTemplate());
|
|
8164
|
+
await writeFileIfMissing(path9__namespace.join(contextDir, "brief.md"), briefTemplate());
|
|
8165
|
+
await writeFileIfMissing(path9__namespace.join(knowledgeDir, "README.md"), knowledgeTemplate());
|
|
8166
|
+
await writeFileIfMissing(path9__namespace.join(brandDir, "README.md"), brandTemplate());
|
|
8167
|
+
const { config: config3, errorMessage } = await loadConfig(configPath, output);
|
|
8168
|
+
await upsertGeneratedDoc(
|
|
8169
|
+
path9__namespace.join(docsDir, "schema.md"),
|
|
8170
|
+
buildSchemaTemplate({ config: config3, errorMessage }),
|
|
8171
|
+
buildSchemaGeneratedSection({ config: config3, errorMessage })
|
|
8172
|
+
);
|
|
8173
|
+
await upsertGeneratedDoc(
|
|
8174
|
+
path9__namespace.join(docsDir, "block-types.md"),
|
|
8175
|
+
buildBlockTypesTemplate(),
|
|
8176
|
+
buildBlockTypesGeneratedSection()
|
|
8177
|
+
);
|
|
8178
|
+
await upsertAgentsSection(agentsPath);
|
|
8179
|
+
}
|
|
8180
|
+
async function loadConfig(configPath, output) {
|
|
8181
|
+
if (!configPath) {
|
|
8182
|
+
return { errorMessage: "No config path provided." };
|
|
8183
|
+
}
|
|
8184
|
+
try {
|
|
8185
|
+
const { loadConfigFile: loadConfigFile2 } = await Promise.resolve().then(() => (init_load_config(), load_config_exports));
|
|
8186
|
+
const rawConfig = await loadConfigFile2(configPath);
|
|
8187
|
+
const parsed = riverbankSiteConfigSchema.safeParse(rawConfig);
|
|
8188
|
+
if (!parsed.success) {
|
|
8189
|
+
return { errorMessage: `Config validation failed: ${parsed.error.message}` };
|
|
8190
|
+
}
|
|
8191
|
+
return { config: parsed.data };
|
|
8192
|
+
} catch (error) {
|
|
8193
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8194
|
+
output?.warn("Failed to load config; schema will be partial", { message });
|
|
8195
|
+
return { errorMessage: message };
|
|
8196
|
+
}
|
|
8197
|
+
}
|
|
8198
|
+
async function ensureDir2(dirPath) {
|
|
8199
|
+
await fs5__namespace.mkdir(dirPath, { recursive: true });
|
|
8200
|
+
}
|
|
8201
|
+
async function writeFileIfMissing(filePath, contents) {
|
|
8202
|
+
try {
|
|
8203
|
+
await fs5__namespace.access(filePath);
|
|
8204
|
+
} catch {
|
|
8205
|
+
await fs5__namespace.writeFile(filePath, contents, "utf-8");
|
|
8206
|
+
}
|
|
8207
|
+
}
|
|
8208
|
+
async function upsertGeneratedDoc(filePath, template, generatedSection) {
|
|
8209
|
+
const markers = getGeneratedMarkers();
|
|
8210
|
+
const contents = await readFileOrTemplate(filePath, template);
|
|
8211
|
+
const updated = replaceMarkedSection(contents, markers.start, markers.end, generatedSection);
|
|
8212
|
+
await fs5__namespace.writeFile(filePath, updated, "utf-8");
|
|
8213
|
+
}
|
|
8214
|
+
async function readFileOrTemplate(filePath, template) {
|
|
8215
|
+
try {
|
|
8216
|
+
return await fs5__namespace.readFile(filePath, "utf-8");
|
|
8217
|
+
} catch {
|
|
8218
|
+
return template;
|
|
8219
|
+
}
|
|
8220
|
+
}
|
|
8221
|
+
function replaceMarkedSection(content, startMarker, endMarker, section2) {
|
|
8222
|
+
const startIndex = content.indexOf(startMarker);
|
|
8223
|
+
const endIndex = content.indexOf(endMarker);
|
|
8224
|
+
if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
|
|
8225
|
+
const trimmed = content.trimEnd();
|
|
8226
|
+
return `${trimmed}
|
|
8227
|
+
|
|
8228
|
+
${startMarker}
|
|
8229
|
+
${section2}
|
|
8230
|
+
${endMarker}
|
|
8231
|
+
`;
|
|
8232
|
+
}
|
|
8233
|
+
const before = content.slice(0, startIndex + startMarker.length);
|
|
8234
|
+
const after = content.slice(endIndex);
|
|
8235
|
+
return `${before}
|
|
8236
|
+
${section2}
|
|
8237
|
+
${after}`;
|
|
8238
|
+
}
|
|
8239
|
+
async function upsertAgentsSection(filePath) {
|
|
8240
|
+
const content = await readFileOrTemplate(filePath, "# AGENTS.md\n");
|
|
8241
|
+
const section2 = agentsSectionTemplate();
|
|
8242
|
+
const updated = replaceMarkedSection(content, AGENTS_START, AGENTS_END, section2);
|
|
8243
|
+
await fs5__namespace.writeFile(filePath, updated, "utf-8");
|
|
8244
|
+
}
|
|
8245
|
+
function cliReferenceTemplate() {
|
|
8246
|
+
return [
|
|
8247
|
+
"# CLI Reference",
|
|
8248
|
+
"",
|
|
8249
|
+
"Describe the SDK CLI commands available to agents.",
|
|
8250
|
+
"",
|
|
8251
|
+
"Include usage patterns, required flags, and common examples.",
|
|
8252
|
+
""
|
|
8253
|
+
].join("\n");
|
|
8254
|
+
}
|
|
8255
|
+
function contentManagementTemplate() {
|
|
8256
|
+
return [
|
|
8257
|
+
"# Content Management",
|
|
8258
|
+
"",
|
|
8259
|
+
"Describe how content is structured, edited, and published for this site.",
|
|
8260
|
+
""
|
|
8261
|
+
].join("\n");
|
|
8262
|
+
}
|
|
8263
|
+
function workflowCreatePageTemplate() {
|
|
8264
|
+
return [
|
|
8265
|
+
"# Workflow: Create a Page",
|
|
8266
|
+
"",
|
|
8267
|
+
"Describe the steps to create a page for this site.",
|
|
8268
|
+
"",
|
|
8269
|
+
"Include any required content types or blocks.",
|
|
8270
|
+
""
|
|
8271
|
+
].join("\n");
|
|
8272
|
+
}
|
|
8273
|
+
function workflowAddBlockTemplate() {
|
|
8274
|
+
return [
|
|
8275
|
+
"# Workflow: Add a Block",
|
|
8276
|
+
"",
|
|
8277
|
+
"Describe how to add blocks to an existing page.",
|
|
8278
|
+
""
|
|
8279
|
+
].join("\n");
|
|
8280
|
+
}
|
|
8281
|
+
function workflowPublishTemplate() {
|
|
8282
|
+
return [
|
|
8283
|
+
"# Workflow: Publish Content",
|
|
8284
|
+
"",
|
|
8285
|
+
"Describe how to publish content safely for this site.",
|
|
8286
|
+
""
|
|
8287
|
+
].join("\n");
|
|
8288
|
+
}
|
|
8289
|
+
function briefTemplate() {
|
|
8290
|
+
return [
|
|
8291
|
+
"# Site Brief",
|
|
8292
|
+
"",
|
|
8293
|
+
"Summarize the brand, tone, and goals for this site.",
|
|
8294
|
+
""
|
|
8295
|
+
].join("\n");
|
|
8296
|
+
}
|
|
8297
|
+
function knowledgeTemplate() {
|
|
8298
|
+
return [
|
|
8299
|
+
"# Knowledge Base",
|
|
8300
|
+
"",
|
|
8301
|
+
"Add domain knowledge, FAQs, and reference links for the site.",
|
|
8302
|
+
""
|
|
8303
|
+
].join("\n");
|
|
8304
|
+
}
|
|
8305
|
+
function brandTemplate() {
|
|
8306
|
+
return [
|
|
8307
|
+
"# Brand Assets",
|
|
8308
|
+
"",
|
|
8309
|
+
"List or link the brand assets available for this site.",
|
|
8310
|
+
""
|
|
8311
|
+
].join("\n");
|
|
8312
|
+
}
|
|
8313
|
+
function agentsSectionTemplate() {
|
|
8314
|
+
return [
|
|
8315
|
+
"## Riverbank SDK Context",
|
|
8316
|
+
"",
|
|
8317
|
+
"- This repo contains both the CMS and the SDK used by this site.",
|
|
8318
|
+
"- Agents can suggest or implement CMS changes when needed, not just local workarounds.",
|
|
8319
|
+
"- Use riverbank.config.ts as the source of truth for site schema and custom blocks.",
|
|
8320
|
+
"",
|
|
8321
|
+
"## Code Quality Guidelines (SDK + CMS)",
|
|
8322
|
+
"",
|
|
8323
|
+
"- Prefer functional, composable helpers over large classes.",
|
|
8324
|
+
"- Keep changes small and DRY; remove old code instead of shimming.",
|
|
8325
|
+
"- Keep route handlers thin and use helpers/services.",
|
|
8326
|
+
"- Use typed API clients; avoid hardcoded URLs."
|
|
8327
|
+
].join("\n");
|
|
8328
|
+
}
|
|
8329
|
+
|
|
8330
|
+
// src/cli/commands/init-docs.ts
|
|
8331
|
+
async function runInitDocs(options, command) {
|
|
8332
|
+
const { output, isJsonOutput } = createCommandContext(command);
|
|
8333
|
+
const rootDir = path9__namespace.resolve(options.path ?? ".riverbank");
|
|
8334
|
+
const configPath = path9__namespace.resolve(options.config ?? path9__namespace.join(process.cwd(), "riverbank.config.ts"));
|
|
8335
|
+
const agentsPath = path9__namespace.resolve(process.cwd(), "AGENTS.md");
|
|
8336
|
+
await initDocs({
|
|
8337
|
+
rootDir,
|
|
8338
|
+
configPath,
|
|
8339
|
+
agentsPath,
|
|
8340
|
+
output
|
|
8341
|
+
});
|
|
8342
|
+
if (isJsonOutput) {
|
|
8343
|
+
output.json({ success: true, data: { rootDir } });
|
|
8344
|
+
return;
|
|
8345
|
+
}
|
|
8346
|
+
output.success("Agent docs initialized", { rootDir });
|
|
8347
|
+
}
|
|
8348
|
+
var initDocsCommand = new commander.Command("init-docs").description("Scaffold agent documentation and site context").option("--path <path>", "Destination directory (default: ./.riverbank)").option("--config <path>", "Path to riverbank.config.ts (default: ./riverbank.config.ts)").action(
|
|
8349
|
+
withErrorHandling(async (options, command) => {
|
|
8350
|
+
await runInitDocs(options, command);
|
|
8351
|
+
})
|
|
8352
|
+
);
|
|
8353
|
+
|
|
8354
|
+
// src/cli/index.ts
|
|
8355
|
+
dotenv.config({ path: ".env.local" });
|
|
8356
|
+
dotenv.config({ path: ".env" });
|
|
8357
|
+
var program = new commander.Command();
|
|
8358
|
+
program.name("riverbankcms").description("Riverbank CMS SDK CLI - manage content and configuration").version("0.1.0").option("--json", "Output in JSON format for machine parsing").option("--quiet", "Minimal output (suppress non-essential messages)").option("--remote", "Use remote/production environment instead of local").addHelpText("after", `
|
|
8359
|
+
Environment Variables:
|
|
8360
|
+
Local environment (default):
|
|
8361
|
+
RIVERBANK_LOCAL_SITE_ID Site ID for local development
|
|
8362
|
+
RIVERBANK_LOCAL_DASHBOARD_URL Dashboard URL (e.g., http://localhost:4000)
|
|
8363
|
+
RIVERBANK_LOCAL_MGMT_API_KEY Management API key (bld_mgmt_sk_...)
|
|
8364
|
+
|
|
8365
|
+
Remote environment (--remote):
|
|
8366
|
+
RIVERBANK_REMOTE_SITE_ID Site ID for production
|
|
8367
|
+
RIVERBANK_REMOTE_DASHBOARD_URL Dashboard URL (e.g., https://dashboard.riverbankcms.com)
|
|
8368
|
+
RIVERBANK_REMOTE_MGMT_API_KEY Management API key (bld_mgmt_sk_...)
|
|
8369
|
+
|
|
8370
|
+
Examples:
|
|
8371
|
+
$ riverbankcms push-config --api-key $RIVERBANK_API_KEY
|
|
8372
|
+
$ riverbankcms pull # Pull all content from local
|
|
8373
|
+
$ riverbankcms pull --remote # Pull all content from production
|
|
8374
|
+
$ riverbankcms pull entries blog-post # Pull specific content type
|
|
8375
|
+
$ riverbankcms push # Push content to local
|
|
8376
|
+
$ riverbankcms push --remote --yes # Push content to production
|
|
5402
8377
|
|
|
5403
8378
|
Run 'riverbankcms <command> --help' for detailed options.
|
|
5404
8379
|
`);
|
|
5405
8380
|
program.addCommand(pushConfigCommand);
|
|
8381
|
+
program.addCommand(pullCommand);
|
|
8382
|
+
program.addCommand(pushCommand);
|
|
8383
|
+
program.addCommand(entryCommand);
|
|
8384
|
+
program.addCommand(pageCommand);
|
|
8385
|
+
program.addCommand(blockCommand);
|
|
8386
|
+
program.addCommand(navigationCommand);
|
|
8387
|
+
program.addCommand(deleteCommand);
|
|
8388
|
+
program.addCommand(previewCommand);
|
|
8389
|
+
program.addCommand(initDocsCommand);
|
|
5406
8390
|
program.parse();
|
|
5407
8391
|
//# sourceMappingURL=index.js.map
|
|
5408
8392
|
//# sourceMappingURL=index.js.map
|