@riverbankcms/sdk 0.7.5 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -1
- package/dist/cli/index.js +4299 -209
- package/dist/cli/index.js.map +1 -1
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +230 -31
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +230 -31
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/hooks.d.mts +2 -2
- package/dist/client/hooks.d.ts +2 -2
- package/dist/client/hooks.js +34 -20
- package/dist/client/hooks.js.map +1 -1
- package/dist/client/hooks.mjs +34 -20
- package/dist/client/hooks.mjs.map +1 -1
- package/dist/client/rendering/client.js +182 -123
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +174 -109
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/usePage-CdnO2CP5.d.mts +6875 -0
- package/dist/client/usePage-Dsi39Exp.d.ts +6915 -0
- package/dist/client/usePage-Im82JRRe.d.mts +6915 -0
- package/dist/client/usePage-_ksKXlUF.d.ts +6875 -0
- package/dist/server/{Layout-Yluyb6sK.d.ts → Layout-CZ-kxKfl.d.ts} +1 -1
- package/dist/server/{Layout-qWLdVm5-.d.mts → Layout-ESG8zvrk.d.mts} +1 -1
- package/dist/server/{chunk-4YQJUL5W.mjs → chunk-5GCSRTIU.mjs} +8 -4
- package/dist/server/chunk-5GCSRTIU.mjs.map +1 -0
- package/dist/server/{chunk-YYO3RIFO.js → chunk-6ERSDFTY.js} +35 -21
- package/dist/server/chunk-6ERSDFTY.js.map +1 -0
- package/dist/server/{chunk-YXA4GAAQ.mjs → chunk-6VTKALLN.mjs} +2 -6
- package/dist/server/{chunk-YXA4GAAQ.mjs.map → chunk-6VTKALLN.mjs.map} +1 -1
- package/dist/server/{chunk-BYBJA6SP.mjs → chunk-A3UZ2LDH.mjs} +35 -21
- package/dist/server/chunk-A3UZ2LDH.mjs.map +1 -0
- package/dist/server/{chunk-OSF34JTQ.mjs → chunk-ADD3O2QO.mjs} +4 -4
- package/dist/server/{chunk-C6FIJC7T.mjs → chunk-BNHK7YOC.mjs} +2 -2
- package/dist/server/{chunk-TT5JWA4X.js → chunk-DAXWU3S3.js} +9 -9
- package/dist/server/{chunk-TT5JWA4X.js.map → chunk-DAXWU3S3.js.map} +1 -1
- package/dist/server/{chunk-7UPVCT3K.js → chunk-F2NDLDDA.js} +239 -154
- package/dist/server/chunk-F2NDLDDA.js.map +1 -0
- package/dist/server/{chunk-LNOUXALA.mjs → chunk-FUFPKTSI.mjs} +96 -11
- package/dist/server/chunk-FUFPKTSI.mjs.map +1 -0
- package/dist/server/{chunk-2NBNOY3C.mjs → chunk-GRFFJUCO.mjs} +107 -4
- package/dist/server/chunk-GRFFJUCO.mjs.map +1 -0
- package/dist/server/{chunk-AEFWG657.mjs → chunk-HDHY4236.mjs} +2 -2
- package/dist/server/{chunk-2KCF2DNK.js → chunk-HE3RTUDX.js} +8 -8
- package/dist/server/{chunk-2KCF2DNK.js.map → chunk-HE3RTUDX.js.map} +1 -1
- package/dist/server/{chunk-RVDS7VSP.js → chunk-IJTJH4J3.js} +4 -4
- package/dist/server/{chunk-RVDS7VSP.js.map → chunk-IJTJH4J3.js.map} +1 -1
- package/dist/server/{chunk-P3NNN73G.js → chunk-K44OPKLA.js} +3 -3
- package/dist/server/{chunk-P3NNN73G.js.map → chunk-K44OPKLA.js.map} +1 -1
- package/dist/server/{chunk-EIJ27EZQ.js → chunk-KDCVCDW6.js} +10 -6
- package/dist/server/chunk-KDCVCDW6.js.map +1 -0
- package/dist/server/{chunk-7BVRA5MY.js → chunk-KGORQCHF.js} +9 -9
- package/dist/server/{chunk-7BVRA5MY.js.map → chunk-KGORQCHF.js.map} +1 -1
- package/dist/server/{chunk-KH3EXBJM.js → chunk-MFNWLB5G.js} +111 -8
- package/dist/server/chunk-MFNWLB5G.js.map +1 -0
- package/dist/server/{chunk-EIVISR62.js → chunk-P4O3WSAR.js} +2 -6
- package/dist/server/chunk-P4O3WSAR.js.map +1 -0
- package/dist/server/{chunk-RBJFXNDM.mjs → chunk-PGZJUNCY.mjs} +4 -4
- package/dist/server/{chunk-ARNCLSQT.mjs → chunk-T5PAA22U.mjs} +2 -2
- package/dist/server/{chunk-T26N3P26.js → chunk-TLZHVGTL.js} +4 -4
- package/dist/server/{chunk-T26N3P26.js.map → chunk-TLZHVGTL.js.map} +1 -1
- package/dist/server/{chunk-P4K63SBZ.mjs → chunk-TR7MSLWL.mjs} +3 -3
- package/dist/server/{chunk-NFEGQTCC.mjs → chunk-WMJKH4XE.mjs} +8 -1
- package/dist/server/{chunk-4CV4JOE5.js → chunk-Z6ZWNWWR.js} +9 -2
- package/dist/server/chunk-Z6ZWNWWR.js.map +1 -0
- package/dist/server/{components-Di5ME6He.d.ts → components-CE48wJM1.d.ts} +4 -4
- package/dist/server/{components-DNHfSCML.d.mts → components-iEDvl2Yw.d.mts} +4 -4
- package/dist/server/components.d.mts +6 -6
- package/dist/server/components.d.ts +6 -6
- package/dist/server/components.js +7 -7
- package/dist/server/components.mjs +6 -6
- package/dist/server/config-validation.d.mts +3 -3
- package/dist/server/config-validation.d.ts +3 -3
- package/dist/server/config-validation.js +6 -6
- package/dist/server/config-validation.mjs +5 -5
- package/dist/server/config.d.mts +5 -5
- package/dist/server/config.d.ts +5 -5
- package/dist/server/config.js +6 -6
- package/dist/server/config.mjs +5 -5
- package/dist/server/data.d.mts +3 -3
- package/dist/server/data.d.ts +3 -3
- package/dist/server/data.js +4 -4
- package/dist/server/data.mjs +3 -3
- package/dist/server/env.js +1 -1
- package/dist/server/env.mjs +1 -1
- package/dist/server/{index-DLvNddi-.d.ts → index-BHLK2mgQ.d.ts} +2 -2
- package/dist/server/{index--Oyunk_B.d.mts → index-BrH_NIRO.d.mts} +2 -2
- package/dist/server/{index-Clm3skz_.d.mts → index-Cgvb5fVQ.d.mts} +2 -2
- package/dist/server/{index-C9Ra8dza.d.ts → index-DTBg8eXj.d.ts} +2 -2
- package/dist/server/index.d.mts +14 -6
- package/dist/server/index.d.ts +14 -6
- package/dist/server/index.js +11 -11
- package/dist/server/index.mjs +2 -2
- package/dist/server/{loadContent-D7LQwI0o.d.ts → loadContent-BUK6IVJf.d.ts} +26 -4
- package/dist/server/{loadContent-DVfuBLiZ.d.mts → loadContent-au9Weoy0.d.mts} +26 -4
- package/dist/server/loadPage-AWYZ2QA2.mjs +11 -0
- package/dist/server/loadPage-CMHYAW2J.js +11 -0
- package/dist/server/{loadPage-AXNAERDS.js.map → loadPage-CMHYAW2J.js.map} +1 -1
- package/dist/server/{loadPage-BucnLHmE.d.mts → loadPage-DiHEl8BA.d.mts} +3 -3
- package/dist/server/{loadPage-BmYJCe_V.d.ts → loadPage-JOIbF7ih.d.ts} +3 -3
- package/dist/server/metadata.d.mts +5 -5
- package/dist/server/metadata.d.ts +5 -5
- package/dist/server/metadata.js +1 -1
- package/dist/server/metadata.mjs +1 -1
- package/dist/server/navigation.d.mts +4 -8
- package/dist/server/navigation.d.ts +4 -8
- package/dist/server/navigation.js +3 -7
- package/dist/server/navigation.js.map +1 -1
- package/dist/server/navigation.mjs +2 -6
- package/dist/server/next/revalidate.js +1 -1
- package/dist/server/next/revalidate.mjs +1 -1
- package/dist/server/next/tags.js +1 -1
- package/dist/server/next/tags.mjs +1 -1
- package/dist/server/next.d.mts +39 -8
- package/dist/server/next.d.ts +39 -8
- package/dist/server/next.js +47 -25
- package/dist/server/next.js.map +1 -1
- package/dist/server/next.mjs +40 -18
- package/dist/server/next.mjs.map +1 -1
- package/dist/server/rendering/server.d.mts +5 -5
- package/dist/server/rendering/server.d.ts +5 -5
- package/dist/server/rendering/server.js +9 -9
- package/dist/server/rendering/server.mjs +8 -8
- package/dist/server/rendering.d.mts +8 -8
- package/dist/server/rendering.d.ts +8 -8
- package/dist/server/rendering.js +11 -11
- package/dist/server/rendering.mjs +10 -10
- package/dist/server/routing.d.mts +5 -5
- package/dist/server/routing.d.ts +5 -5
- package/dist/server/routing.js +2 -2
- package/dist/server/routing.mjs +2 -2
- package/dist/server/{schema-Z6-afHJG.d.mts → schema-DYtW0zEu.d.mts} +40 -0
- package/dist/server/{schema-Z6-afHJG.d.ts → schema-DYtW0zEu.d.ts} +40 -0
- package/dist/server/server.d.mts +6 -6
- package/dist/server/server.d.ts +6 -6
- package/dist/server/server.js +7 -7
- package/dist/server/server.mjs +6 -6
- package/dist/server/theme-bridge.js +9 -9
- package/dist/server/theme-bridge.mjs +3 -3
- package/dist/server/theme.js +1 -1
- package/dist/server/theme.mjs +1 -1
- package/dist/server/{types-BSV6Vc-P.d.mts → types-BAM1kcGA.d.mts} +9 -1
- package/dist/server/{types-C-LShyIg.d.mts → types-CmBB0Osp.d.ts} +44 -2
- package/dist/server/{types-BRQyLrQU.d.ts → types-DDNKxQXw.d.mts} +44 -2
- package/dist/server/{types-DLBhEPSt.d.ts → types-DVesWaB7.d.ts} +38 -1
- package/dist/server/{types-BjgZt8xJ.d.mts → types-M0CviVW2.d.mts} +38 -1
- package/dist/server/{types-Dt98DeYa.d.ts → types-_SNCu2ZZ.d.ts} +9 -1
- package/dist/server/{validation-BGuRo8P1.d.mts → validation-BA1TKthZ.d.mts} +2 -2
- package/dist/server/{validation-DU2YE7u5.d.ts → validation-js7BCPN8.d.ts} +2 -2
- package/dist/server/webhooks.js +1 -1
- package/dist/server/webhooks.mjs +1 -1
- package/package.json +2 -1
- package/dist/server/chunk-2NBNOY3C.mjs.map +0 -1
- package/dist/server/chunk-4CV4JOE5.js.map +0 -1
- package/dist/server/chunk-4YQJUL5W.mjs.map +0 -1
- package/dist/server/chunk-7UPVCT3K.js.map +0 -1
- package/dist/server/chunk-BYBJA6SP.mjs.map +0 -1
- package/dist/server/chunk-EIJ27EZQ.js.map +0 -1
- package/dist/server/chunk-EIVISR62.js.map +0 -1
- package/dist/server/chunk-KH3EXBJM.js.map +0 -1
- package/dist/server/chunk-LNOUXALA.mjs.map +0 -1
- package/dist/server/chunk-YYO3RIFO.js.map +0 -1
- package/dist/server/loadPage-AXNAERDS.js +0 -11
- package/dist/server/loadPage-XR7ORQ2E.mjs +0 -11
- /package/dist/server/{chunk-OSF34JTQ.mjs.map → chunk-ADD3O2QO.mjs.map} +0 -0
- /package/dist/server/{chunk-C6FIJC7T.mjs.map → chunk-BNHK7YOC.mjs.map} +0 -0
- /package/dist/server/{chunk-AEFWG657.mjs.map → chunk-HDHY4236.mjs.map} +0 -0
- /package/dist/server/{chunk-RBJFXNDM.mjs.map → chunk-PGZJUNCY.mjs.map} +0 -0
- /package/dist/server/{chunk-ARNCLSQT.mjs.map → chunk-T5PAA22U.mjs.map} +0 -0
- /package/dist/server/{chunk-P4K63SBZ.mjs.map → chunk-TR7MSLWL.mjs.map} +0 -0
- /package/dist/server/{chunk-NFEGQTCC.mjs.map → chunk-WMJKH4XE.mjs.map} +0 -0
- /package/dist/server/{loadPage-XR7ORQ2E.mjs.map → loadPage-AWYZ2QA2.mjs.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -3,16 +3,20 @@
|
|
|
3
3
|
|
|
4
4
|
var jiti = require('jiti');
|
|
5
5
|
var path9 = require('path');
|
|
6
|
-
var
|
|
6
|
+
var fs6 = require('fs');
|
|
7
7
|
var dotenv = require('dotenv');
|
|
8
8
|
var commander = require('commander');
|
|
9
9
|
var zod = require('zod');
|
|
10
|
-
var prompts = require('prompts');
|
|
11
10
|
var fs3 = require('fs/promises');
|
|
12
11
|
var readline = require('readline');
|
|
12
|
+
var prompts = require('prompts');
|
|
13
13
|
var equal = require('fast-deep-equal');
|
|
14
14
|
var os = require('os');
|
|
15
15
|
var child_process = require('child_process');
|
|
16
|
+
var simpleGit = require('simple-git');
|
|
17
|
+
require('react');
|
|
18
|
+
require('react/jsx-runtime');
|
|
19
|
+
var crypto2 = require('crypto');
|
|
16
20
|
|
|
17
21
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
18
22
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -36,14 +40,19 @@ function _interopNamespace(e) {
|
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
var path9__namespace = /*#__PURE__*/_interopNamespace(path9);
|
|
39
|
-
var
|
|
43
|
+
var fs6__namespace = /*#__PURE__*/_interopNamespace(fs6);
|
|
40
44
|
var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
|
|
41
45
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
46
|
+
var prompts__default = /*#__PURE__*/_interopDefault(prompts);
|
|
42
47
|
var equal__default = /*#__PURE__*/_interopDefault(equal);
|
|
43
48
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
49
|
+
var simpleGit__default = /*#__PURE__*/_interopDefault(simpleGit);
|
|
50
|
+
var crypto2__namespace = /*#__PURE__*/_interopNamespace(crypto2);
|
|
44
51
|
|
|
45
52
|
var __defProp = Object.defineProperty;
|
|
53
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
46
54
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
55
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
47
56
|
var __esm = (fn, res) => function __init() {
|
|
48
57
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
49
58
|
};
|
|
@@ -51,6 +60,15 @@ var __export = (target, all) => {
|
|
|
51
60
|
for (var name in all)
|
|
52
61
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
53
62
|
};
|
|
63
|
+
var __copyProps = (to, from, except, desc) => {
|
|
64
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
65
|
+
for (let key of __getOwnPropNames(from))
|
|
66
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
67
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
68
|
+
}
|
|
69
|
+
return to;
|
|
70
|
+
};
|
|
71
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
54
72
|
|
|
55
73
|
// src/cli/load-config.ts
|
|
56
74
|
var load_config_exports = {};
|
|
@@ -59,7 +77,7 @@ __export(load_config_exports, {
|
|
|
59
77
|
});
|
|
60
78
|
async function loadConfigFile(configPath) {
|
|
61
79
|
const resolvedPath = resolveConfigPath(configPath);
|
|
62
|
-
if (!
|
|
80
|
+
if (!fs6.existsSync(resolvedPath)) {
|
|
63
81
|
throw new Error(
|
|
64
82
|
`Config file not found: ${resolvedPath}
|
|
65
83
|
Create a riverbank.config.ts file or specify a path with --config`
|
|
@@ -90,7 +108,7 @@ function resolveConfigPath(configPath) {
|
|
|
90
108
|
return path9.resolve(process.cwd(), DEFAULT_CONFIG_FILENAME);
|
|
91
109
|
}
|
|
92
110
|
const resolved = path9.resolve(configPath);
|
|
93
|
-
if (
|
|
111
|
+
if (fs6.existsSync(resolved) && !resolved.endsWith(".ts") && !resolved.endsWith(".js")) {
|
|
94
112
|
return path9.resolve(resolved, DEFAULT_CONFIG_FILENAME);
|
|
95
113
|
}
|
|
96
114
|
return resolved;
|
|
@@ -102,6 +120,240 @@ var init_load_config = __esm({
|
|
|
102
120
|
}
|
|
103
121
|
});
|
|
104
122
|
|
|
123
|
+
// src/constants.ts
|
|
124
|
+
var PREBUILD_PAGE_SIZE, DEFAULT_MAX_PREBUILD_AGE_SEC, DEFAULT_PREBUILD_DIR;
|
|
125
|
+
var init_constants = __esm({
|
|
126
|
+
"src/constants.ts"() {
|
|
127
|
+
PREBUILD_PAGE_SIZE = 50;
|
|
128
|
+
DEFAULT_MAX_PREBUILD_AGE_SEC = 86400;
|
|
129
|
+
DEFAULT_PREBUILD_DIR = ".riverbank-cache";
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// src/prebuild/loader.ts
|
|
134
|
+
var loader_exports = {};
|
|
135
|
+
__export(loader_exports, {
|
|
136
|
+
PrebuildLoader: () => PrebuildLoader,
|
|
137
|
+
canUsePrebuild: () => canUsePrebuild,
|
|
138
|
+
createPrebuildLoader: () => createPrebuildLoader
|
|
139
|
+
});
|
|
140
|
+
function loadManifest(prebuildDir) {
|
|
141
|
+
if (cachedManifest?.dir === prebuildDir) {
|
|
142
|
+
return cachedManifest.manifest;
|
|
143
|
+
}
|
|
144
|
+
const manifestPath = path9__namespace.join(prebuildDir, "manifest.json");
|
|
145
|
+
if (!fs6__namespace.existsSync(manifestPath)) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const content = fs6__namespace.readFileSync(manifestPath, "utf-8");
|
|
150
|
+
const manifest = JSON.parse(content);
|
|
151
|
+
cachedManifest = { manifest, dir: prebuildDir };
|
|
152
|
+
return manifest;
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function loadJsonFile(prebuildDir, relativePath) {
|
|
158
|
+
const filePath = path9__namespace.join(prebuildDir, relativePath);
|
|
159
|
+
if (!fs6__namespace.existsSync(filePath)) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const content = fs6__namespace.readFileSync(filePath, "utf-8");
|
|
164
|
+
return JSON.parse(content);
|
|
165
|
+
} catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function isPrebuildExpired(manifest, maxAgeSec) {
|
|
170
|
+
const ageMs = Date.now() - new Date(manifest.generatedAt).getTime();
|
|
171
|
+
return ageMs > maxAgeSec * 1e3;
|
|
172
|
+
}
|
|
173
|
+
function getPrebuildAgeSec(manifest) {
|
|
174
|
+
const ageMs = Date.now() - new Date(manifest.generatedAt).getTime();
|
|
175
|
+
return Math.floor(ageMs / 1e3);
|
|
176
|
+
}
|
|
177
|
+
function canUsePrebuild() {
|
|
178
|
+
if (typeof process === "undefined" || !process.versions?.node) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
return typeof fs6__namespace.existsSync === "function";
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function createPrebuildLoader(config3) {
|
|
188
|
+
return new PrebuildLoader(config3);
|
|
189
|
+
}
|
|
190
|
+
var cachedManifest, PrebuildLoader;
|
|
191
|
+
var init_loader = __esm({
|
|
192
|
+
"src/prebuild/loader.ts"() {
|
|
193
|
+
init_constants();
|
|
194
|
+
cachedManifest = null;
|
|
195
|
+
PrebuildLoader = class {
|
|
196
|
+
constructor(config3) {
|
|
197
|
+
this.prebuildDir = config3.prebuildDir ?? DEFAULT_PREBUILD_DIR;
|
|
198
|
+
this.maxPrebuildAgeSec = config3.maxPrebuildAgeSec ?? DEFAULT_MAX_PREBUILD_AGE_SEC;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if prebuild is available and not expired.
|
|
202
|
+
*/
|
|
203
|
+
isAvailable() {
|
|
204
|
+
if (!canUsePrebuild()) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
const manifest = loadManifest(this.prebuildDir);
|
|
208
|
+
if (!manifest) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
return !isPrebuildExpired(manifest, this.maxPrebuildAgeSec);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Load site data from prebuild cache.
|
|
215
|
+
*/
|
|
216
|
+
loadSite(siteId) {
|
|
217
|
+
const manifest = loadManifest(this.prebuildDir);
|
|
218
|
+
if (!manifest || isPrebuildExpired(manifest, this.maxPrebuildAgeSec)) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
const cacheFile = loadJsonFile(this.prebuildDir, "site.json");
|
|
222
|
+
if (!cacheFile || cacheFile.data.site.id !== siteId) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
data: cacheFile.data,
|
|
227
|
+
prebuildAgeSec: getPrebuildAgeSec(manifest)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Load page data from prebuild cache.
|
|
232
|
+
*/
|
|
233
|
+
loadPage(siteId, pagePath) {
|
|
234
|
+
const manifest = loadManifest(this.prebuildDir);
|
|
235
|
+
if (!manifest || isPrebuildExpired(manifest, this.maxPrebuildAgeSec)) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
const cacheKey = `page:${siteId}:${pagePath}:false`;
|
|
239
|
+
const relativePath = manifest.keyToFile[cacheKey];
|
|
240
|
+
if (!relativePath) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
const cacheFile = loadJsonFile(this.prebuildDir, relativePath);
|
|
244
|
+
if (!cacheFile) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
data: cacheFile.data,
|
|
249
|
+
prebuildAgeSec: getPrebuildAgeSec(manifest)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Load entries from prebuild cache with runtime filtering.
|
|
254
|
+
*
|
|
255
|
+
* The prebuild stores ALL entries for each content type.
|
|
256
|
+
* Filtering, ordering, and pagination are applied at runtime.
|
|
257
|
+
*/
|
|
258
|
+
loadEntries(siteId, params) {
|
|
259
|
+
if (params.preview) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
if (params.mode === "manual" && params.entryIds?.length) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
const manifest = loadManifest(this.prebuildDir);
|
|
266
|
+
if (!manifest || isPrebuildExpired(manifest, this.maxPrebuildAgeSec)) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
const cacheKey = `entries-all:${siteId}:${params.contentType}`;
|
|
270
|
+
const relativePath = manifest.keyToFile[cacheKey];
|
|
271
|
+
if (!relativePath) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const cacheFile = loadJsonFile(this.prebuildDir, relativePath);
|
|
275
|
+
if (!cacheFile) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
let entries = [...cacheFile.entries];
|
|
279
|
+
if (params.order === "newest") {
|
|
280
|
+
entries.sort((a, b) => {
|
|
281
|
+
const aTime = a.publishedAt ? new Date(a.publishedAt).getTime() : 0;
|
|
282
|
+
const bTime = b.publishedAt ? new Date(b.publishedAt).getTime() : 0;
|
|
283
|
+
return bTime - aTime;
|
|
284
|
+
});
|
|
285
|
+
} else if (params.order === "oldest") {
|
|
286
|
+
entries.sort((a, b) => {
|
|
287
|
+
const aTime = a.publishedAt ? new Date(a.publishedAt).getTime() : 0;
|
|
288
|
+
const bTime = b.publishedAt ? new Date(b.publishedAt).getTime() : 0;
|
|
289
|
+
return aTime - bTime;
|
|
290
|
+
});
|
|
291
|
+
} else if (params.order === "title") {
|
|
292
|
+
entries.sort((a, b) => (a.title || "").localeCompare(b.title || ""));
|
|
293
|
+
}
|
|
294
|
+
const total = entries.length;
|
|
295
|
+
const offset = params.offset ?? 0;
|
|
296
|
+
const limit = params.limit ?? 10;
|
|
297
|
+
if (offset > 0) {
|
|
298
|
+
entries = entries.slice(offset);
|
|
299
|
+
}
|
|
300
|
+
entries = entries.slice(0, limit);
|
|
301
|
+
const prebuildAgeSec = getPrebuildAgeSec(manifest);
|
|
302
|
+
if (params.includeMeta) {
|
|
303
|
+
return {
|
|
304
|
+
data: {
|
|
305
|
+
entries,
|
|
306
|
+
total,
|
|
307
|
+
hasMore: offset + entries.length < total,
|
|
308
|
+
limit,
|
|
309
|
+
offset,
|
|
310
|
+
totalPages: Math.ceil(total / limit),
|
|
311
|
+
currentPage: Math.floor(offset / limit) + 1
|
|
312
|
+
},
|
|
313
|
+
prebuildAgeSec
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
data: { entries },
|
|
318
|
+
prebuildAgeSec
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Load navigation data from prebuild cache.
|
|
323
|
+
*/
|
|
324
|
+
loadNavigation() {
|
|
325
|
+
const manifest = loadManifest(this.prebuildDir);
|
|
326
|
+
if (!manifest || isPrebuildExpired(manifest, this.maxPrebuildAgeSec)) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
const cacheFile = loadJsonFile(
|
|
330
|
+
this.prebuildDir,
|
|
331
|
+
"navigation/menus.json"
|
|
332
|
+
);
|
|
333
|
+
if (!cacheFile) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
data: cacheFile.menus,
|
|
338
|
+
prebuildAgeSec: getPrebuildAgeSec(manifest)
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get the manifest for inspection.
|
|
343
|
+
*/
|
|
344
|
+
getManifest() {
|
|
345
|
+
return loadManifest(this.prebuildDir);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Clear the cached manifest (for testing).
|
|
349
|
+
*/
|
|
350
|
+
clearCache() {
|
|
351
|
+
cachedManifest = null;
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
105
357
|
// ../blocks/src/system/manifest/augmentManifest.ts
|
|
106
358
|
function augmentManifest(manifest) {
|
|
107
359
|
let augmentedFields = manifest.fields ?? [];
|
|
@@ -254,7 +506,11 @@ var uiSchema = zod.z.object({
|
|
|
254
506
|
layout: zod.z.enum(["stack", "grid"]).optional(),
|
|
255
507
|
columns: zod.z.number().int().min(2).max(4).optional(),
|
|
256
508
|
// Entry picker configuration
|
|
257
|
-
contentTypeField: zod.z.string().optional()
|
|
509
|
+
contentTypeField: zod.z.string().optional(),
|
|
510
|
+
// Extras pattern: fields marked as extras are hidden behind a modal toggle
|
|
511
|
+
extras: zod.z.boolean().optional(),
|
|
512
|
+
// Render in block header instead of form body (used for section styles)
|
|
513
|
+
renderInHeader: zod.z.boolean().optional()
|
|
258
514
|
}).partial();
|
|
259
515
|
var baseFieldSchema = zod.z.object({
|
|
260
516
|
id: zod.z.string().min(1, "Field id is required"),
|
|
@@ -574,11 +830,11 @@ function bind(from, options) {
|
|
|
574
830
|
}
|
|
575
831
|
});
|
|
576
832
|
}
|
|
577
|
-
function when(
|
|
833
|
+
function when(path13, options) {
|
|
578
834
|
return (node) => ({
|
|
579
835
|
...node,
|
|
580
836
|
$when: {
|
|
581
|
-
when: { from:
|
|
837
|
+
when: { from: path13 },
|
|
582
838
|
...options?.equals !== void 0 ? { equals: options.equals } : {},
|
|
583
839
|
...options?.not ? { not: true } : {}
|
|
584
840
|
}
|
|
@@ -651,25 +907,25 @@ function devValidate(node) {
|
|
|
651
907
|
}
|
|
652
908
|
|
|
653
909
|
// ../blocks/src/system/node/fragments/backgroundLayer.ts
|
|
654
|
-
function backgroundLayer(
|
|
910
|
+
function backgroundLayer(path13, options = {}) {
|
|
655
911
|
const {
|
|
656
912
|
styleClassName = "absolute inset-0 -z-10 h-full w-full pointer-events-none",
|
|
657
913
|
imageClassName
|
|
658
914
|
} = options;
|
|
659
915
|
const styleLayer = el("div", {
|
|
660
|
-
className: { $bind: { from:
|
|
661
|
-
style: { $bind: { from:
|
|
916
|
+
className: { $bind: { from: path13, transforms: [{ id: "background.resolveClass", options: { baseClass: styleClassName } }] } },
|
|
917
|
+
style: { $bind: { from: path13, transforms: [{ id: "background.resolveStyle" }] } }
|
|
662
918
|
});
|
|
663
|
-
const imageLayer = createBackgroundImageNode(
|
|
919
|
+
const imageLayer = createBackgroundImageNode(path13, imageClassName);
|
|
664
920
|
return [styleLayer, imageLayer];
|
|
665
921
|
}
|
|
666
|
-
function createBackgroundImageNode(
|
|
667
|
-
const imagePath = `${
|
|
922
|
+
function createBackgroundImageNode(path13, baseClassName = "absolute -z-10") {
|
|
923
|
+
const imagePath = `${path13}.image`;
|
|
668
924
|
return media(
|
|
669
925
|
{
|
|
670
926
|
className: {
|
|
671
927
|
$bind: {
|
|
672
|
-
from:
|
|
928
|
+
from: path13,
|
|
673
929
|
transforms: [{
|
|
674
930
|
id: "background.resolveImageClassName",
|
|
675
931
|
options: { baseClass: `background-image ${baseClassName}` }
|
|
@@ -678,7 +934,7 @@ function createBackgroundImageNode(path11, baseClassName = "absolute -z-10") {
|
|
|
678
934
|
},
|
|
679
935
|
style: {
|
|
680
936
|
$bind: {
|
|
681
|
-
from:
|
|
937
|
+
from: path13,
|
|
682
938
|
transforms: [{ id: "background.resolveImageStyle" }]
|
|
683
939
|
}
|
|
684
940
|
}
|
|
@@ -1106,27 +1362,27 @@ function scopePropValue(value, scope) {
|
|
|
1106
1362
|
}
|
|
1107
1363
|
return value;
|
|
1108
1364
|
}
|
|
1109
|
-
function scopeContentPath(
|
|
1365
|
+
function scopeContentPath(path13, scope) {
|
|
1110
1366
|
if (!scope || scope.length === 0) {
|
|
1111
|
-
return
|
|
1367
|
+
return path13;
|
|
1112
1368
|
}
|
|
1113
|
-
if (
|
|
1369
|
+
if (path13 === "content") {
|
|
1114
1370
|
return `content.${scope}`;
|
|
1115
1371
|
}
|
|
1116
|
-
if (
|
|
1117
|
-
const remainder =
|
|
1372
|
+
if (path13.startsWith("content.")) {
|
|
1373
|
+
const remainder = path13.slice("content.".length);
|
|
1118
1374
|
return remainder.length > 0 ? `content.${scope}.${remainder}` : `content.${scope}`;
|
|
1119
1375
|
}
|
|
1120
|
-
if (
|
|
1121
|
-
return
|
|
1376
|
+
if (path13.startsWith("content[")) {
|
|
1377
|
+
return path13.replace(/^content/, `content.${scope}`);
|
|
1122
1378
|
}
|
|
1123
|
-
if (
|
|
1124
|
-
return
|
|
1379
|
+
if (path13.startsWith("$root.")) {
|
|
1380
|
+
return path13;
|
|
1125
1381
|
}
|
|
1126
|
-
if (
|
|
1127
|
-
return
|
|
1382
|
+
if (path13.includes(".")) {
|
|
1383
|
+
return path13;
|
|
1128
1384
|
}
|
|
1129
|
-
return `content.${scope}.${
|
|
1385
|
+
return `content.${scope}.${path13}`;
|
|
1130
1386
|
}
|
|
1131
1387
|
|
|
1132
1388
|
// ../blocks/src/system/fragments/builder.ts
|
|
@@ -1424,25 +1680,30 @@ function createButtonGroup(options = {}) {
|
|
|
1424
1680
|
ui: { colSpan: 2 }
|
|
1425
1681
|
}
|
|
1426
1682
|
];
|
|
1427
|
-
const
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1683
|
+
const iconFields = [
|
|
1684
|
+
{
|
|
1685
|
+
id: "iconLeft",
|
|
1686
|
+
type: "media",
|
|
1687
|
+
label: "Left icon",
|
|
1688
|
+
required: false,
|
|
1689
|
+
mediaKinds: ["image"],
|
|
1690
|
+
ui: { extras: true }
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
id: "iconRight",
|
|
1694
|
+
type: "media",
|
|
1695
|
+
label: "Right icon",
|
|
1696
|
+
required: false,
|
|
1697
|
+
mediaKinds: ["image"],
|
|
1698
|
+
ui: { extras: true }
|
|
1438
1699
|
}
|
|
1439
|
-
|
|
1700
|
+
];
|
|
1440
1701
|
return {
|
|
1441
1702
|
id: groupId,
|
|
1442
1703
|
type: "group",
|
|
1443
1704
|
label: groupLabel,
|
|
1444
1705
|
ui: { layout: "grid", columns: 2, flattenInRepeater, hideLabel: !showGroupLabel },
|
|
1445
|
-
schema: { fields: [...mainFields,
|
|
1706
|
+
schema: { fields: [...mainFields, ...iconFields] },
|
|
1446
1707
|
required: false
|
|
1447
1708
|
};
|
|
1448
1709
|
}
|
|
@@ -2738,6 +2999,10 @@ var BACKGROUND_POSITION_PRESETS = [
|
|
|
2738
2999
|
BACKGROUND_POSITION_PRESETS.map((p) => p.value);
|
|
2739
3000
|
|
|
2740
3001
|
// ../blocks/src/system/fields/background.ts
|
|
3002
|
+
var BACKGROUND_WIDGETS = {
|
|
3003
|
+
COLOR: "backgroundColor",
|
|
3004
|
+
GRADIENT: "backgroundGradient"
|
|
3005
|
+
};
|
|
2741
3006
|
function createBackgroundField(options = {}) {
|
|
2742
3007
|
const {
|
|
2743
3008
|
id = "background",
|
|
@@ -2763,8 +3028,7 @@ function createBackgroundField(options = {}) {
|
|
|
2763
3028
|
required: false,
|
|
2764
3029
|
multiline: false,
|
|
2765
3030
|
ui: {
|
|
2766
|
-
|
|
2767
|
-
widget: "backgroundColor"
|
|
3031
|
+
widget: BACKGROUND_WIDGETS.COLOR
|
|
2768
3032
|
}
|
|
2769
3033
|
}
|
|
2770
3034
|
]
|
|
@@ -2781,11 +3045,11 @@ function createBackgroundField(options = {}) {
|
|
|
2781
3045
|
id: "gradient",
|
|
2782
3046
|
type: "text",
|
|
2783
3047
|
label: "Gradient",
|
|
2784
|
-
description: "
|
|
3048
|
+
description: "Select a gradient from theme presets.",
|
|
2785
3049
|
required: false,
|
|
2786
|
-
multiline:
|
|
3050
|
+
multiline: false,
|
|
2787
3051
|
ui: {
|
|
2788
|
-
|
|
3052
|
+
widget: BACKGROUND_WIDGETS.GRADIENT
|
|
2789
3053
|
}
|
|
2790
3054
|
}
|
|
2791
3055
|
]
|
|
@@ -2844,7 +3108,7 @@ function createBackgroundField(options = {}) {
|
|
|
2844
3108
|
id: "position",
|
|
2845
3109
|
type: "presetOrCustom",
|
|
2846
3110
|
label: "Position",
|
|
2847
|
-
description: 'Anchor point for
|
|
3111
|
+
description: 'Anchor point for scaled images. For "Fill" mode, the image focus point (if set) takes precedence.',
|
|
2848
3112
|
required: false,
|
|
2849
3113
|
presets: [...BACKGROUND_POSITION_PRESETS],
|
|
2850
3114
|
customInput: {
|
|
@@ -2935,6 +3199,8 @@ function sectionStylesField(options = {}) {
|
|
|
2935
3199
|
required: false,
|
|
2936
3200
|
schema: { fields: fields4 },
|
|
2937
3201
|
ui: {
|
|
3202
|
+
// Render in block header instead of form body
|
|
3203
|
+
renderInHeader: true,
|
|
2938
3204
|
modalConfig: {
|
|
2939
3205
|
buttonLabel: label,
|
|
2940
3206
|
description: "Configure background and spacing for this section.",
|
|
@@ -5133,7 +5399,7 @@ var contentConfigSchema = contentConfigBaseSchema.superRefine((data, ctx) => {
|
|
|
5133
5399
|
});
|
|
5134
5400
|
}
|
|
5135
5401
|
const paths = data.pages.map((p) => p.path);
|
|
5136
|
-
const duplicatePaths = paths.filter((
|
|
5402
|
+
const duplicatePaths = paths.filter((path13, i) => paths.indexOf(path13) !== i);
|
|
5137
5403
|
if (duplicatePaths.length > 0) {
|
|
5138
5404
|
ctx.addIssue({
|
|
5139
5405
|
code: zod.z.ZodIssueCode.custom,
|
|
@@ -5391,111 +5657,6 @@ var riverbankSiteConfigSchema = zod.z.object({
|
|
|
5391
5657
|
|
|
5392
5658
|
// src/cli/push-config.ts
|
|
5393
5659
|
init_load_config();
|
|
5394
|
-
async function pushToDashboard(dashboardUrl, siteId, apiKey, config3) {
|
|
5395
|
-
const pushUrl = `${dashboardUrl}/api/sites/${siteId}/sdk-config`;
|
|
5396
|
-
console.log(`Pushing config to ${pushUrl}...`);
|
|
5397
|
-
let response;
|
|
5398
|
-
try {
|
|
5399
|
-
response = await fetch(pushUrl, {
|
|
5400
|
-
method: "POST",
|
|
5401
|
-
headers: {
|
|
5402
|
-
"Content-Type": "application/json",
|
|
5403
|
-
"Authorization": `Bearer ${apiKey}`
|
|
5404
|
-
},
|
|
5405
|
-
body: JSON.stringify({ config: config3 }),
|
|
5406
|
-
signal: AbortSignal.timeout(3e4)
|
|
5407
|
-
});
|
|
5408
|
-
} catch (error) {
|
|
5409
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5410
|
-
throw new Error(`Failed to connect to dashboard: ${message}`);
|
|
5411
|
-
}
|
|
5412
|
-
if (!response.ok) {
|
|
5413
|
-
let errorMessage = `Dashboard returned ${response.status}`;
|
|
5414
|
-
try {
|
|
5415
|
-
const errorBody = await response.json();
|
|
5416
|
-
if (errorBody.error) {
|
|
5417
|
-
errorMessage = errorBody.error;
|
|
5418
|
-
if (errorBody.details) {
|
|
5419
|
-
errorMessage += ":\n" + errorBody.details.map((d) => ` - ${d.path}: ${d.message}`).join("\n");
|
|
5420
|
-
}
|
|
5421
|
-
}
|
|
5422
|
-
} catch {
|
|
5423
|
-
}
|
|
5424
|
-
throw new Error(errorMessage);
|
|
5425
|
-
}
|
|
5426
|
-
}
|
|
5427
|
-
async function pushConfigAction(options) {
|
|
5428
|
-
try {
|
|
5429
|
-
const rawConfig = await loadConfigFile(options.config);
|
|
5430
|
-
console.log("Validating config...");
|
|
5431
|
-
const parseResult = riverbankSiteConfigSchema.safeParse(rawConfig);
|
|
5432
|
-
if (!parseResult.success) {
|
|
5433
|
-
console.error("Invalid config:");
|
|
5434
|
-
for (const issue of parseResult.error.issues) {
|
|
5435
|
-
console.error(` - ${issue.path.join(".")}: ${issue.message}`);
|
|
5436
|
-
}
|
|
5437
|
-
process.exit(1);
|
|
5438
|
-
}
|
|
5439
|
-
const conflicts = validateFieldIdConflicts(parseResult.data.blockFieldExtensions);
|
|
5440
|
-
if (conflicts.length > 0) {
|
|
5441
|
-
console.error("Field ID conflicts detected in blockFieldExtensions:");
|
|
5442
|
-
for (const conflict of conflicts) {
|
|
5443
|
-
console.error(` - ${conflict.message}`);
|
|
5444
|
-
}
|
|
5445
|
-
process.exit(1);
|
|
5446
|
-
}
|
|
5447
|
-
const { siteId } = parseResult.data;
|
|
5448
|
-
const apiKey = resolveManagementApiKey(options.apiKey, options.isRemote);
|
|
5449
|
-
await pushToDashboard(options.dashboard, siteId, apiKey, parseResult.data);
|
|
5450
|
-
console.log("Config pushed successfully!");
|
|
5451
|
-
} catch (error) {
|
|
5452
|
-
console.error("Error:", error instanceof Error ? error.message : error);
|
|
5453
|
-
process.exit(1);
|
|
5454
|
-
}
|
|
5455
|
-
}
|
|
5456
|
-
function resolveDashboardUrl(cliOption, isRemote) {
|
|
5457
|
-
const envVar = isRemote ? "RIVERBANK_REMOTE_DASHBOARD_URL" : "RIVERBANK_LOCAL_DASHBOARD_URL";
|
|
5458
|
-
const url = cliOption || process.env[envVar];
|
|
5459
|
-
if (!url) {
|
|
5460
|
-
console.error("Error: Dashboard URL is required.");
|
|
5461
|
-
console.error(`Provide --dashboard <url> or set ${envVar} environment variable.`);
|
|
5462
|
-
process.exit(1);
|
|
5463
|
-
}
|
|
5464
|
-
return url;
|
|
5465
|
-
}
|
|
5466
|
-
function resolveManagementApiKey(cliOption, isRemote) {
|
|
5467
|
-
const envVar = isRemote ? "RIVERBANK_REMOTE_MGMT_API_KEY" : "RIVERBANK_LOCAL_MGMT_API_KEY";
|
|
5468
|
-
const apiKey = cliOption || process.env[envVar];
|
|
5469
|
-
if (!apiKey) {
|
|
5470
|
-
console.error("Error: Management API key is required.");
|
|
5471
|
-
console.error(`Provide --api-key <key> or set ${envVar} environment variable.`);
|
|
5472
|
-
process.exit(1);
|
|
5473
|
-
}
|
|
5474
|
-
if (!apiKey.startsWith("bld_mgmt_sk_")) {
|
|
5475
|
-
console.error(`Error: Invalid management API key format for ${envVar}.`);
|
|
5476
|
-
console.error("Expected key starting with bld_mgmt_sk_.");
|
|
5477
|
-
process.exit(1);
|
|
5478
|
-
}
|
|
5479
|
-
return apiKey;
|
|
5480
|
-
}
|
|
5481
|
-
var pushConfigCommand = new commander.Command("push-config").description("Push SDK config to dashboard").option("--api-key <key>", "Management API key (or set RIVERBANK_*_MGMT_API_KEY)").option("--dashboard <url>", "Dashboard URL (or set RIVERBANK_*_DASHBOARD_URL env var)").option("--config <path>", "Path to config file (default: ./riverbank.config.ts)").addHelpText("after", `
|
|
5482
|
-
Description:
|
|
5483
|
-
Syncs your local riverbank.config.ts to the CMS dashboard, including:
|
|
5484
|
-
- Custom blocks
|
|
5485
|
-
- Block field extensions
|
|
5486
|
-
- Block field options
|
|
5487
|
-
- Content types, pages, entries, and navigation
|
|
5488
|
-
|
|
5489
|
-
Examples:
|
|
5490
|
-
$ npx riverbankcms push-config
|
|
5491
|
-
$ npx riverbankcms push-config --api-key bld_mgmt_sk_... --dashboard https://www.riverbankcms.com
|
|
5492
|
-
$ npx riverbankcms push-config --config ./src/riverbank.config.ts
|
|
5493
|
-
`).action((options, command) => {
|
|
5494
|
-
const globalOpts = command.optsWithGlobals();
|
|
5495
|
-
const isRemote = globalOpts.remote ?? false;
|
|
5496
|
-
const dashboard = resolveDashboardUrl(options.dashboard, isRemote);
|
|
5497
|
-
return pushConfigAction({ ...options, dashboard, isRemote });
|
|
5498
|
-
});
|
|
5499
5660
|
|
|
5500
5661
|
// src/client/management/http.ts
|
|
5501
5662
|
var ManagementApiError = class extends Error {
|
|
@@ -5507,11 +5668,14 @@ var ManagementApiError = class extends Error {
|
|
|
5507
5668
|
this.statusCode = statusCode;
|
|
5508
5669
|
}
|
|
5509
5670
|
};
|
|
5671
|
+
function is404Error(error) {
|
|
5672
|
+
return error instanceof ManagementApiError && error.statusCode === 404;
|
|
5673
|
+
}
|
|
5510
5674
|
function createHttpClient(config3) {
|
|
5511
5675
|
const baseUrl = `${config3.dashboardUrl}/api/sdk/${config3.siteId}`;
|
|
5512
5676
|
const timeout = config3.timeout ?? 3e4;
|
|
5513
|
-
async function request(method,
|
|
5514
|
-
let url = `${baseUrl}${
|
|
5677
|
+
async function request(method, path13, options) {
|
|
5678
|
+
let url = `${baseUrl}${path13}`;
|
|
5515
5679
|
if (options?.params && Object.keys(options.params).length > 0) {
|
|
5516
5680
|
const searchParams = new URLSearchParams(options.params);
|
|
5517
5681
|
url = `${url}?${searchParams.toString()}`;
|
|
@@ -5579,17 +5743,17 @@ function createHttpClient(config3) {
|
|
|
5579
5743
|
return json.data;
|
|
5580
5744
|
}
|
|
5581
5745
|
return {
|
|
5582
|
-
async get(
|
|
5583
|
-
return request("GET",
|
|
5746
|
+
async get(path13, params) {
|
|
5747
|
+
return request("GET", path13, { params });
|
|
5584
5748
|
},
|
|
5585
|
-
async post(
|
|
5586
|
-
return request("POST",
|
|
5749
|
+
async post(path13, body) {
|
|
5750
|
+
return request("POST", path13, { body });
|
|
5587
5751
|
},
|
|
5588
|
-
async patch(
|
|
5589
|
-
return request("PATCH",
|
|
5752
|
+
async patch(path13, body) {
|
|
5753
|
+
return request("PATCH", path13, { body });
|
|
5590
5754
|
},
|
|
5591
|
-
async delete(
|
|
5592
|
-
await request("DELETE",
|
|
5755
|
+
async delete(path13, body) {
|
|
5756
|
+
await request("DELETE", path13, { body });
|
|
5593
5757
|
}
|
|
5594
5758
|
};
|
|
5595
5759
|
}
|
|
@@ -5612,7 +5776,7 @@ function createEntryOperations(http) {
|
|
|
5612
5776
|
`/entries/${encodeURIComponent(contentType)}/${encodeURIComponent(identifier)}`
|
|
5613
5777
|
);
|
|
5614
5778
|
} catch (error) {
|
|
5615
|
-
if (error
|
|
5779
|
+
if (is404Error(error)) {
|
|
5616
5780
|
return null;
|
|
5617
5781
|
}
|
|
5618
5782
|
throw error;
|
|
@@ -5654,7 +5818,7 @@ function createPageOperations(http) {
|
|
|
5654
5818
|
`/pages/${encodeURIComponent(identifier)}`
|
|
5655
5819
|
);
|
|
5656
5820
|
} catch (error) {
|
|
5657
|
-
if (error
|
|
5821
|
+
if (is404Error(error)) {
|
|
5658
5822
|
return null;
|
|
5659
5823
|
}
|
|
5660
5824
|
throw error;
|
|
@@ -5687,7 +5851,7 @@ function createBlockOperations(http) {
|
|
|
5687
5851
|
`/pages/${encodeURIComponent(pageIdentifier)}/blocks/${encodeURIComponent(blockIdentifier)}`
|
|
5688
5852
|
);
|
|
5689
5853
|
} catch (error) {
|
|
5690
|
-
if (error
|
|
5854
|
+
if (is404Error(error)) {
|
|
5691
5855
|
return null;
|
|
5692
5856
|
}
|
|
5693
5857
|
throw error;
|
|
@@ -5726,7 +5890,7 @@ function createNavigationOperations(http) {
|
|
|
5726
5890
|
`/navigation/${encodeURIComponent(name)}`
|
|
5727
5891
|
);
|
|
5728
5892
|
} catch (error) {
|
|
5729
|
-
if (error
|
|
5893
|
+
if (is404Error(error)) {
|
|
5730
5894
|
return null;
|
|
5731
5895
|
}
|
|
5732
5896
|
throw error;
|
|
@@ -5795,6 +5959,9 @@ function createPullOperations(http) {
|
|
|
5795
5959
|
truncationMessage: entriesResult?.meta?.truncationMessage
|
|
5796
5960
|
}
|
|
5797
5961
|
};
|
|
5962
|
+
},
|
|
5963
|
+
async siteInfo() {
|
|
5964
|
+
return http.get("/pull/site-info");
|
|
5798
5965
|
}
|
|
5799
5966
|
};
|
|
5800
5967
|
}
|
|
@@ -5817,43 +5984,164 @@ function createIdentifiersOperations(http) {
|
|
|
5817
5984
|
};
|
|
5818
5985
|
}
|
|
5819
5986
|
|
|
5820
|
-
// src/client/management/
|
|
5821
|
-
function
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5987
|
+
// src/client/management/media.ts
|
|
5988
|
+
function createMediaOperations(config3) {
|
|
5989
|
+
const baseUrl = `${config3.dashboardUrl}/api/sdk/${config3.siteId}`;
|
|
5990
|
+
const timeout = config3.timeout ?? 6e4;
|
|
5991
|
+
async function formDataRequest(path13, formData) {
|
|
5992
|
+
const url = `${baseUrl}${path13}`;
|
|
5993
|
+
const headers = {
|
|
5994
|
+
"Authorization": `Bearer ${config3.managementApiKey}`
|
|
5995
|
+
// Note: Don't set Content-Type - let fetch set it with boundary
|
|
5996
|
+
};
|
|
5997
|
+
let response;
|
|
5998
|
+
try {
|
|
5999
|
+
response = await fetch(url, {
|
|
6000
|
+
method: "POST",
|
|
6001
|
+
headers,
|
|
6002
|
+
body: formData,
|
|
6003
|
+
signal: AbortSignal.timeout(timeout)
|
|
6004
|
+
});
|
|
6005
|
+
} catch (error) {
|
|
6006
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
6007
|
+
throw new ManagementApiError(
|
|
6008
|
+
`Request timed out after ${timeout}ms`,
|
|
6009
|
+
"sdk:timeout",
|
|
6010
|
+
void 0,
|
|
6011
|
+
void 0
|
|
6012
|
+
);
|
|
6013
|
+
}
|
|
6014
|
+
throw new ManagementApiError(
|
|
6015
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
6016
|
+
"sdk:network-error",
|
|
6017
|
+
void 0,
|
|
6018
|
+
void 0
|
|
6019
|
+
);
|
|
6020
|
+
}
|
|
6021
|
+
const json = await response.json();
|
|
6022
|
+
if (!response.ok || !json.success) {
|
|
6023
|
+
if (json.error) {
|
|
6024
|
+
throw new ManagementApiError(
|
|
6025
|
+
json.error.message,
|
|
6026
|
+
json.error.code,
|
|
6027
|
+
json.error.details,
|
|
6028
|
+
response.status
|
|
6029
|
+
);
|
|
6030
|
+
}
|
|
6031
|
+
throw new ManagementApiError(
|
|
6032
|
+
`Request failed with status ${response.status}`,
|
|
6033
|
+
"sdk:http-error",
|
|
6034
|
+
void 0,
|
|
6035
|
+
response.status
|
|
6036
|
+
);
|
|
6037
|
+
}
|
|
6038
|
+
return json.data;
|
|
5841
6039
|
}
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
6040
|
+
async function jsonRequest(path13, body) {
|
|
6041
|
+
const url = `${baseUrl}${path13}`;
|
|
6042
|
+
let response;
|
|
6043
|
+
try {
|
|
6044
|
+
response = await fetch(url, {
|
|
6045
|
+
method: "POST",
|
|
6046
|
+
headers: {
|
|
6047
|
+
"Authorization": `Bearer ${config3.managementApiKey}`,
|
|
6048
|
+
"Content-Type": "application/json"
|
|
6049
|
+
},
|
|
6050
|
+
body: JSON.stringify(body),
|
|
6051
|
+
signal: AbortSignal.timeout(3e4)
|
|
6052
|
+
});
|
|
6053
|
+
} catch (error) {
|
|
6054
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
6055
|
+
throw new ManagementApiError(
|
|
6056
|
+
"Request timed out after 30000ms",
|
|
6057
|
+
"sdk:timeout",
|
|
6058
|
+
void 0,
|
|
6059
|
+
void 0
|
|
6060
|
+
);
|
|
6061
|
+
}
|
|
6062
|
+
throw new ManagementApiError(
|
|
6063
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
6064
|
+
"sdk:network-error",
|
|
6065
|
+
void 0,
|
|
6066
|
+
void 0
|
|
6067
|
+
);
|
|
6068
|
+
}
|
|
6069
|
+
const json = await response.json();
|
|
6070
|
+
if (!response.ok || !json.success) {
|
|
6071
|
+
if (json.error) {
|
|
6072
|
+
throw new ManagementApiError(
|
|
6073
|
+
json.error.message,
|
|
6074
|
+
json.error.code,
|
|
6075
|
+
json.error.details,
|
|
6076
|
+
response.status
|
|
6077
|
+
);
|
|
6078
|
+
}
|
|
6079
|
+
throw new ManagementApiError(
|
|
6080
|
+
`Request failed with status ${response.status}`,
|
|
6081
|
+
"sdk:http-error",
|
|
6082
|
+
void 0,
|
|
6083
|
+
response.status
|
|
6084
|
+
);
|
|
6085
|
+
}
|
|
6086
|
+
return json.data;
|
|
6087
|
+
}
|
|
6088
|
+
return {
|
|
6089
|
+
async upload(file) {
|
|
6090
|
+
const formData = new FormData();
|
|
6091
|
+
const blob = new Blob([new Uint8Array(file.data)], { type: file.contentType });
|
|
6092
|
+
formData.append("file", blob, file.filename);
|
|
6093
|
+
formData.append("storagePath", file.storagePath);
|
|
6094
|
+
return formDataRequest("/media/upload", formData);
|
|
6095
|
+
},
|
|
6096
|
+
async exists(storagePath) {
|
|
6097
|
+
const result = await jsonRequest("/media/exists", { storagePath });
|
|
6098
|
+
return result.exists;
|
|
6099
|
+
}
|
|
6100
|
+
};
|
|
6101
|
+
}
|
|
6102
|
+
|
|
6103
|
+
// src/client/management/index.ts
|
|
6104
|
+
function createManagementClient(config3) {
|
|
6105
|
+
if (!config3.dashboardUrl) {
|
|
6106
|
+
throw new Error(
|
|
6107
|
+
"dashboardUrl is required when creating a management client. Example: http://localhost:4000 or https://dashboard.riverbankcms.com"
|
|
6108
|
+
);
|
|
6109
|
+
}
|
|
6110
|
+
if (!config3.managementApiKey) {
|
|
6111
|
+
throw new Error(
|
|
6112
|
+
"managementApiKey is required when creating a management client. A management API key starts with bld_mgmt_sk_"
|
|
6113
|
+
);
|
|
6114
|
+
}
|
|
6115
|
+
if (!config3.managementApiKey.startsWith("bld_mgmt_sk_")) {
|
|
6116
|
+
throw new Error(
|
|
6117
|
+
"Invalid management API key format. A management API key must start with bld_mgmt_sk_"
|
|
6118
|
+
);
|
|
6119
|
+
}
|
|
6120
|
+
if (!config3.siteId) {
|
|
6121
|
+
throw new Error(
|
|
6122
|
+
"siteId is required when creating a management client."
|
|
6123
|
+
);
|
|
6124
|
+
}
|
|
6125
|
+
const http = createHttpClient(config3);
|
|
6126
|
+
return {
|
|
6127
|
+
entries: createEntryOperations(http),
|
|
6128
|
+
pages: createPageOperations(http),
|
|
6129
|
+
blocks: createBlockOperations(http),
|
|
6130
|
+
navigation: createNavigationOperations(http),
|
|
6131
|
+
settings: createSettingsOperations(http),
|
|
6132
|
+
pull: createPullOperations(http),
|
|
6133
|
+
preview: createPreviewOperations(http),
|
|
6134
|
+
identifiers: createIdentifiersOperations(http),
|
|
6135
|
+
media: createMediaOperations(config3)
|
|
6136
|
+
};
|
|
6137
|
+
}
|
|
6138
|
+
|
|
6139
|
+
// src/cli/env.ts
|
|
6140
|
+
function getEnvVarName(key, remote) {
|
|
6141
|
+
const target = remote ? "REMOTE" : "LOCAL";
|
|
6142
|
+
return `RIVERBANK_${target}_${key}`;
|
|
6143
|
+
}
|
|
6144
|
+
function getEnvPrefix(target) {
|
|
5857
6145
|
return target === "remote" ? "RIVERBANK_REMOTE" : "RIVERBANK_LOCAL";
|
|
5858
6146
|
}
|
|
5859
6147
|
function requireEnv(name) {
|
|
@@ -5863,12 +6151,16 @@ function requireEnv(name) {
|
|
|
5863
6151
|
}
|
|
5864
6152
|
return value;
|
|
5865
6153
|
}
|
|
6154
|
+
function getEnv(name, fallback) {
|
|
6155
|
+
return process.env[name] ?? fallback;
|
|
6156
|
+
}
|
|
5866
6157
|
function loadEnvironment(remote) {
|
|
5867
6158
|
const target = remote ? "remote" : "local";
|
|
5868
6159
|
const prefix = getEnvPrefix(target);
|
|
5869
6160
|
const siteId = requireEnv(`${prefix}_SITE_ID`);
|
|
5870
6161
|
const dashboardUrl = requireEnv(`${prefix}_DASHBOARD_URL`);
|
|
5871
6162
|
const managementApiKey = requireEnv(`${prefix}_MGMT_API_KEY`);
|
|
6163
|
+
const supabaseUrl = getEnv(`${prefix}_SUPABASE_URL`);
|
|
5872
6164
|
if (!managementApiKey.startsWith("bld_mgmt_sk_")) {
|
|
5873
6165
|
throw new Error(
|
|
5874
6166
|
`Invalid management API key format for ${prefix}_MGMT_API_KEY. Expected key starting with bld_mgmt_sk_`
|
|
@@ -5881,10 +6173,20 @@ function loadEnvironment(remote) {
|
|
|
5881
6173
|
`Invalid dashboard URL in ${prefix}_DASHBOARD_URL: ${dashboardUrl}. Expected format: http://localhost:4000 or https://dashboard.example.com`
|
|
5882
6174
|
);
|
|
5883
6175
|
}
|
|
6176
|
+
if (supabaseUrl) {
|
|
6177
|
+
try {
|
|
6178
|
+
new URL(supabaseUrl);
|
|
6179
|
+
} catch {
|
|
6180
|
+
throw new Error(
|
|
6181
|
+
`Invalid Supabase URL in ${prefix}_SUPABASE_URL: ${supabaseUrl}. Expected format: http://127.0.0.1:54321 or https://xxx.supabase.co`
|
|
6182
|
+
);
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
5884
6185
|
return {
|
|
5885
6186
|
siteId,
|
|
5886
6187
|
dashboardUrl,
|
|
5887
|
-
managementApiKey
|
|
6188
|
+
managementApiKey,
|
|
6189
|
+
supabaseUrl
|
|
5888
6190
|
};
|
|
5889
6191
|
}
|
|
5890
6192
|
|
|
@@ -6261,6 +6563,112 @@ function createListCommand(config3) {
|
|
|
6261
6563
|
);
|
|
6262
6564
|
}
|
|
6263
6565
|
|
|
6566
|
+
// src/cli/push-config.ts
|
|
6567
|
+
async function pushToDashboard(output, dashboardUrl, siteId, apiKey, config3) {
|
|
6568
|
+
const pushUrl = `${dashboardUrl}/api/sites/${siteId}/sdk-config`;
|
|
6569
|
+
output.info(`Pushing config to ${pushUrl}...`);
|
|
6570
|
+
let response;
|
|
6571
|
+
try {
|
|
6572
|
+
response = await fetch(pushUrl, {
|
|
6573
|
+
method: "POST",
|
|
6574
|
+
headers: {
|
|
6575
|
+
"Content-Type": "application/json",
|
|
6576
|
+
"Authorization": `Bearer ${apiKey}`
|
|
6577
|
+
},
|
|
6578
|
+
body: JSON.stringify({ config: config3 }),
|
|
6579
|
+
signal: AbortSignal.timeout(3e4)
|
|
6580
|
+
});
|
|
6581
|
+
} catch (error) {
|
|
6582
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
6583
|
+
throw new Error(`Failed to connect to dashboard: ${message}`);
|
|
6584
|
+
}
|
|
6585
|
+
if (!response.ok) {
|
|
6586
|
+
let errorMessage = `Dashboard returned ${response.status}`;
|
|
6587
|
+
try {
|
|
6588
|
+
const errorBody = await response.json();
|
|
6589
|
+
if (errorBody.error) {
|
|
6590
|
+
errorMessage = errorBody.error;
|
|
6591
|
+
if (errorBody.details) {
|
|
6592
|
+
errorMessage += ":\n" + errorBody.details.map((d) => ` - ${d.path}: ${d.message}`).join("\n");
|
|
6593
|
+
}
|
|
6594
|
+
}
|
|
6595
|
+
} catch {
|
|
6596
|
+
}
|
|
6597
|
+
throw new Error(errorMessage);
|
|
6598
|
+
}
|
|
6599
|
+
}
|
|
6600
|
+
async function pushConfigAction(output, options) {
|
|
6601
|
+
try {
|
|
6602
|
+
const rawConfig = await loadConfigFile(options.config);
|
|
6603
|
+
output.info("Validating config...");
|
|
6604
|
+
const parseResult = riverbankSiteConfigSchema.safeParse(rawConfig);
|
|
6605
|
+
if (!parseResult.success) {
|
|
6606
|
+
output.error("Invalid config", {
|
|
6607
|
+
issues: parseResult.error.issues.map((issue) => ({
|
|
6608
|
+
path: issue.path.join("."),
|
|
6609
|
+
message: issue.message
|
|
6610
|
+
}))
|
|
6611
|
+
});
|
|
6612
|
+
}
|
|
6613
|
+
const conflicts = validateFieldIdConflicts(parseResult.data.blockFieldExtensions);
|
|
6614
|
+
if (conflicts.length > 0) {
|
|
6615
|
+
output.error("Field ID conflicts detected in blockFieldExtensions", {
|
|
6616
|
+
conflicts: conflicts.map((c) => c.message)
|
|
6617
|
+
});
|
|
6618
|
+
}
|
|
6619
|
+
const { siteId } = parseResult.data;
|
|
6620
|
+
const apiKey = resolveManagementApiKey(output, options.apiKey, options.isRemote);
|
|
6621
|
+
await pushToDashboard(output, options.dashboard, siteId, apiKey, parseResult.data);
|
|
6622
|
+
output.success("Config pushed successfully!");
|
|
6623
|
+
} catch (error) {
|
|
6624
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6625
|
+
output.error(message);
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
function resolveDashboardUrl(output, cliOption, isRemote) {
|
|
6629
|
+
const envVar = getEnvVarName("DASHBOARD_URL", isRemote);
|
|
6630
|
+
const url = cliOption || process.env[envVar];
|
|
6631
|
+
if (!url) {
|
|
6632
|
+
output.error("Dashboard URL is required", {
|
|
6633
|
+
suggestion: `Provide --dashboard <url> or set ${envVar} environment variable.`
|
|
6634
|
+
});
|
|
6635
|
+
}
|
|
6636
|
+
return url;
|
|
6637
|
+
}
|
|
6638
|
+
function resolveManagementApiKey(output, cliOption, isRemote) {
|
|
6639
|
+
const envVar = getEnvVarName("MGMT_API_KEY", isRemote);
|
|
6640
|
+
const apiKey = cliOption || process.env[envVar];
|
|
6641
|
+
if (!apiKey) {
|
|
6642
|
+
output.error("Management API key is required", {
|
|
6643
|
+
suggestion: `Provide --api-key <key> or set ${envVar} environment variable.`
|
|
6644
|
+
});
|
|
6645
|
+
}
|
|
6646
|
+
if (!apiKey.startsWith("bld_mgmt_sk_")) {
|
|
6647
|
+
output.error("Invalid management API key format", {
|
|
6648
|
+
expected: "Key starting with bld_mgmt_sk_",
|
|
6649
|
+
suggestion: `Check your ${envVar} environment variable.`
|
|
6650
|
+
});
|
|
6651
|
+
}
|
|
6652
|
+
return apiKey;
|
|
6653
|
+
}
|
|
6654
|
+
var pushConfigCommand = new commander.Command("push-config").description("Push SDK config to dashboard").option("--api-key <key>", "Management API key (or set RIVERBANK_*_MGMT_API_KEY)").option("--dashboard <url>", "Dashboard URL (or set RIVERBANK_*_DASHBOARD_URL env var)").option("--config <path>", "Path to config file (default: ./riverbank.config.ts)").addHelpText("after", `
|
|
6655
|
+
Description:
|
|
6656
|
+
Syncs your local riverbank.config.ts to the CMS dashboard, including:
|
|
6657
|
+
- Custom blocks
|
|
6658
|
+
- Block field extensions
|
|
6659
|
+
- Block field options
|
|
6660
|
+
- Content types, pages, entries, and navigation
|
|
6661
|
+
|
|
6662
|
+
Examples:
|
|
6663
|
+
$ npx riverbankcms push-config
|
|
6664
|
+
$ npx riverbankcms push-config --api-key bld_mgmt_sk_... --dashboard https://www.riverbankcms.com
|
|
6665
|
+
$ npx riverbankcms push-config --config ./src/riverbank.config.ts
|
|
6666
|
+
`).action((options, command) => {
|
|
6667
|
+
const { output, isRemote } = getOutputContext(command);
|
|
6668
|
+
const dashboard = resolveDashboardUrl(output, options.dashboard, isRemote);
|
|
6669
|
+
return pushConfigAction(output, { ...options, dashboard, isRemote });
|
|
6670
|
+
});
|
|
6671
|
+
|
|
6264
6672
|
// src/cli/sync/mapper.ts
|
|
6265
6673
|
function stripNavigationItemIds(items) {
|
|
6266
6674
|
return items.map((item) => {
|
|
@@ -6364,6 +6772,53 @@ async function writeSettings(contentDir, pulledSettings) {
|
|
|
6364
6772
|
await writeJsonFile(filePath, pulledSettings.settings);
|
|
6365
6773
|
return filePath;
|
|
6366
6774
|
}
|
|
6775
|
+
async function updateMetadataAfterPush(contentDir, remoteContent) {
|
|
6776
|
+
const metaDir = path9__namespace.join(contentDir, ".meta");
|
|
6777
|
+
await ensureDir(metaDir);
|
|
6778
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6779
|
+
for (const [contentType, entries] of Object.entries(remoteContent.entries)) {
|
|
6780
|
+
const metaPath = path9__namespace.join(metaDir, `${contentType}.json`);
|
|
6781
|
+
const entriesMeta = {};
|
|
6782
|
+
for (const entry of entries) {
|
|
6783
|
+
entriesMeta[entry.identifier] = {
|
|
6784
|
+
createdAt: entry.createdAt,
|
|
6785
|
+
updatedAt: entry.updatedAt
|
|
6786
|
+
};
|
|
6787
|
+
}
|
|
6788
|
+
await writeJsonFile(metaPath, {
|
|
6789
|
+
pulledAt: now,
|
|
6790
|
+
entries: entriesMeta
|
|
6791
|
+
});
|
|
6792
|
+
}
|
|
6793
|
+
if (remoteContent.pages.length > 0) {
|
|
6794
|
+
const metaPath = path9__namespace.join(metaDir, "pages.json");
|
|
6795
|
+
const pagesMeta = {};
|
|
6796
|
+
for (const page of remoteContent.pages) {
|
|
6797
|
+
pagesMeta[page.identifier] = {
|
|
6798
|
+
createdAt: page.createdAt,
|
|
6799
|
+
updatedAt: page.updatedAt
|
|
6800
|
+
};
|
|
6801
|
+
}
|
|
6802
|
+
await writeJsonFile(metaPath, {
|
|
6803
|
+
pulledAt: now,
|
|
6804
|
+
pages: pagesMeta
|
|
6805
|
+
});
|
|
6806
|
+
}
|
|
6807
|
+
if (remoteContent.navigation.length > 0) {
|
|
6808
|
+
const metaPath = path9__namespace.join(metaDir, "navigation.json");
|
|
6809
|
+
const menusMeta = {};
|
|
6810
|
+
for (const menu of remoteContent.navigation) {
|
|
6811
|
+
menusMeta[menu.name] = {
|
|
6812
|
+
createdAt: menu.createdAt,
|
|
6813
|
+
updatedAt: menu.updatedAt
|
|
6814
|
+
};
|
|
6815
|
+
}
|
|
6816
|
+
await writeJsonFile(metaPath, {
|
|
6817
|
+
pulledAt: now,
|
|
6818
|
+
menus: menusMeta
|
|
6819
|
+
});
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6367
6822
|
async function fileExists(filePath) {
|
|
6368
6823
|
try {
|
|
6369
6824
|
await fs3__namespace.access(filePath);
|
|
@@ -6512,6 +6967,107 @@ async function readNavigationMeta(contentDir) {
|
|
|
6512
6967
|
}
|
|
6513
6968
|
}
|
|
6514
6969
|
|
|
6970
|
+
// src/cli/sync/media.ts
|
|
6971
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
6972
|
+
maxRetries: 3,
|
|
6973
|
+
baseDelayMs: 1e3,
|
|
6974
|
+
// 1 second
|
|
6975
|
+
maxDelayMs: 1e4
|
|
6976
|
+
// 10 seconds
|
|
6977
|
+
};
|
|
6978
|
+
function sleep(ms) {
|
|
6979
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
6980
|
+
}
|
|
6981
|
+
function getBackoffDelay(attempt, baseDelayMs, maxDelayMs) {
|
|
6982
|
+
const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
|
|
6983
|
+
const jitter = exponentialDelay * (0.75 + Math.random() * 0.5);
|
|
6984
|
+
return Math.min(jitter, maxDelayMs);
|
|
6985
|
+
}
|
|
6986
|
+
async function withRetry(fn, options = {}) {
|
|
6987
|
+
const { maxRetries, baseDelayMs, maxDelayMs } = { ...DEFAULT_RETRY_CONFIG, ...options };
|
|
6988
|
+
let lastError;
|
|
6989
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
6990
|
+
try {
|
|
6991
|
+
return await fn();
|
|
6992
|
+
} catch (error) {
|
|
6993
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
6994
|
+
if (attempt < maxRetries) {
|
|
6995
|
+
const delay = getBackoffDelay(attempt, baseDelayMs, maxDelayMs);
|
|
6996
|
+
options.onRetry?.(attempt + 1, lastError);
|
|
6997
|
+
await sleep(delay);
|
|
6998
|
+
}
|
|
6999
|
+
}
|
|
7000
|
+
}
|
|
7001
|
+
throw lastError;
|
|
7002
|
+
}
|
|
7003
|
+
function extractMediaPaths(data) {
|
|
7004
|
+
const paths = /* @__PURE__ */ new Set();
|
|
7005
|
+
function walk(value) {
|
|
7006
|
+
if (value === null || value === void 0) {
|
|
7007
|
+
return;
|
|
7008
|
+
}
|
|
7009
|
+
if (Array.isArray(value)) {
|
|
7010
|
+
for (const item of value) {
|
|
7011
|
+
walk(item);
|
|
7012
|
+
}
|
|
7013
|
+
return;
|
|
7014
|
+
}
|
|
7015
|
+
if (typeof value === "object") {
|
|
7016
|
+
const obj = value;
|
|
7017
|
+
if (typeof obj.storagePath === "string" && obj.storagePath) {
|
|
7018
|
+
paths.add(obj.storagePath);
|
|
7019
|
+
}
|
|
7020
|
+
for (const key of Object.keys(obj)) {
|
|
7021
|
+
walk(obj[key]);
|
|
7022
|
+
}
|
|
7023
|
+
}
|
|
7024
|
+
}
|
|
7025
|
+
walk(data);
|
|
7026
|
+
return paths;
|
|
7027
|
+
}
|
|
7028
|
+
function buildStorageUrl(supabaseUrl, relativePath, siteId, bucket = "media") {
|
|
7029
|
+
const baseUrl = supabaseUrl.replace(/\/$/, "");
|
|
7030
|
+
const fullPath = `sites/${siteId}/${relativePath}`;
|
|
7031
|
+
return `${baseUrl}/storage/v1/object/public/${bucket}/${fullPath}`;
|
|
7032
|
+
}
|
|
7033
|
+
async function downloadMedia(url, options) {
|
|
7034
|
+
try {
|
|
7035
|
+
return await withRetry(
|
|
7036
|
+
async () => {
|
|
7037
|
+
const response = await fetch(url);
|
|
7038
|
+
if (!response.ok) {
|
|
7039
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
7040
|
+
throw new DownloadError(`HTTP ${response.status}`, response.status, false);
|
|
7041
|
+
}
|
|
7042
|
+
throw new DownloadError(`HTTP ${response.status}`, response.status, true);
|
|
7043
|
+
}
|
|
7044
|
+
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
7045
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
7046
|
+
const data = Buffer.from(arrayBuffer);
|
|
7047
|
+
return { data, contentType };
|
|
7048
|
+
},
|
|
7049
|
+
{
|
|
7050
|
+
maxRetries: options?.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
|
|
7051
|
+
onRetry: (attempt, error) => {
|
|
7052
|
+
console.warn(`[media-sync] Retry ${attempt} for ${url}: ${error.message}`);
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
);
|
|
7056
|
+
} catch (error) {
|
|
7057
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7058
|
+
console.error(`[media-sync] Download failed after retries: ${url} - ${message}`);
|
|
7059
|
+
return null;
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
var DownloadError = class extends Error {
|
|
7063
|
+
constructor(message, status, retryable) {
|
|
7064
|
+
super(message);
|
|
7065
|
+
this.status = status;
|
|
7066
|
+
this.retryable = retryable;
|
|
7067
|
+
this.name = "DownloadError";
|
|
7068
|
+
}
|
|
7069
|
+
};
|
|
7070
|
+
|
|
6515
7071
|
// src/cli/commands/pull.ts
|
|
6516
7072
|
var DEFAULT_PAGE_LIMIT = 500;
|
|
6517
7073
|
async function pullEntriesWithPagination(client, contentType, output) {
|
|
@@ -6596,21 +7152,105 @@ async function fetchAllContentPaginated(client, contentTypes, output) {
|
|
|
6596
7152
|
meta: { pulledAt: (/* @__PURE__ */ new Date()).toISOString(), entries: allMeta }
|
|
6597
7153
|
};
|
|
6598
7154
|
}
|
|
6599
|
-
|
|
7155
|
+
async function syncMediaFiles(content, sourceSupabaseUrl, sourceSiteId, targetSiteId, targetClient, output) {
|
|
7156
|
+
const mediaPaths = extractMediaPaths(content);
|
|
7157
|
+
if (mediaPaths.size === 0) {
|
|
7158
|
+
output.info("No media files found in content");
|
|
7159
|
+
return;
|
|
7160
|
+
}
|
|
7161
|
+
output.info(`Found ${mediaPaths.size} media files to sync`);
|
|
7162
|
+
let synced = 0;
|
|
7163
|
+
let skipped = 0;
|
|
7164
|
+
let failed = 0;
|
|
7165
|
+
for (const relativePath of mediaPaths) {
|
|
7166
|
+
const filename = relativePath.split("/").pop() ?? "file";
|
|
7167
|
+
const targetPath = `sites/${targetSiteId}/${relativePath}`;
|
|
7168
|
+
try {
|
|
7169
|
+
const exists = await targetClient.media.exists(targetPath);
|
|
7170
|
+
if (exists) {
|
|
7171
|
+
skipped++;
|
|
7172
|
+
continue;
|
|
7173
|
+
}
|
|
7174
|
+
} catch (error) {
|
|
7175
|
+
if (process.env.DEBUG) {
|
|
7176
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
7177
|
+
console.warn(`[media-sync] Could not check if ${filename} exists: ${message}`);
|
|
7178
|
+
}
|
|
7179
|
+
}
|
|
7180
|
+
const url = buildStorageUrl(sourceSupabaseUrl, relativePath, sourceSiteId);
|
|
7181
|
+
const downloaded = await downloadMedia(url);
|
|
7182
|
+
if (!downloaded) {
|
|
7183
|
+
output.warn(` Failed to download: ${filename}`);
|
|
7184
|
+
failed++;
|
|
7185
|
+
continue;
|
|
7186
|
+
}
|
|
7187
|
+
try {
|
|
7188
|
+
await targetClient.media.upload({
|
|
7189
|
+
data: downloaded.data,
|
|
7190
|
+
filename,
|
|
7191
|
+
contentType: downloaded.contentType,
|
|
7192
|
+
storagePath: targetPath
|
|
7193
|
+
});
|
|
7194
|
+
synced++;
|
|
7195
|
+
} catch (error) {
|
|
7196
|
+
output.warn(` Failed to upload: ${filename}`);
|
|
7197
|
+
failed++;
|
|
7198
|
+
}
|
|
7199
|
+
}
|
|
7200
|
+
output.info(`Media sync: ${synced} synced, ${skipped} skipped (already exist), ${failed} failed`);
|
|
7201
|
+
}
|
|
7202
|
+
var pullCommand = new commander.Command("pull").description("Pull content from CMS").argument("[scope]", "What to pull: entries, pages, navigation, settings, or all (default)").argument("[type]", 'Content type (when scope is "entries")').option("--output <dir>", "Output directory", "./content").option("--force", "Overwrite existing files without prompting").option("--yes", "Skip confirmation prompt (same as --force)").option("--sync-media", "Sync media files from source to target environment").addHelpText("after", `
|
|
6600
7203
|
Examples:
|
|
6601
7204
|
$ riverbankcms pull # Pull all content
|
|
6602
7205
|
$ riverbankcms pull --remote # Pull from production
|
|
7206
|
+
$ riverbankcms pull --remote --sync-media # Pull from production and sync media to local
|
|
6603
7207
|
$ riverbankcms pull entries # Pull all entries
|
|
6604
7208
|
$ riverbankcms pull entries blog-post # Pull specific content type
|
|
6605
7209
|
$ riverbankcms pull pages # Pull pages with blocks
|
|
6606
7210
|
$ riverbankcms pull navigation # Pull navigation menus
|
|
6607
7211
|
$ riverbankcms pull settings # Pull site settings
|
|
6608
7212
|
$ riverbankcms pull --output ./src/content # Custom output directory
|
|
7213
|
+
|
|
7214
|
+
Media Sync:
|
|
7215
|
+
When using --sync-media, media files are:
|
|
7216
|
+
1. Downloaded from the source environment's Supabase storage
|
|
7217
|
+
2. Uploaded to the target environment via the management API
|
|
7218
|
+
|
|
7219
|
+
The storage URL is automatically fetched from the source CMS API.
|
|
7220
|
+
No additional environment variables are required beyond the standard
|
|
7221
|
+
RIVERBANK_*_DASHBOARD_URL, RIVERBANK_*_SITE_ID, and RIVERBANK_*_MGMT_API_KEY.
|
|
6609
7222
|
`).action(
|
|
6610
7223
|
withErrorHandling(
|
|
6611
7224
|
async (scope, type, options, command) => {
|
|
6612
|
-
const { output, client } = createCommandContext(command);
|
|
7225
|
+
const { output, client, isRemote } = createCommandContext(command);
|
|
6613
7226
|
const contentDir = path9__namespace.resolve(options.output ?? "./content");
|
|
7227
|
+
let targetClient = null;
|
|
7228
|
+
let sourceSupabaseUrl = null;
|
|
7229
|
+
let sourceSiteId = null;
|
|
7230
|
+
let targetSiteId = null;
|
|
7231
|
+
if (options.syncMedia) {
|
|
7232
|
+
const sourceEnv = loadEnvironment(isRemote);
|
|
7233
|
+
const targetEnv = loadEnvironment(!isRemote);
|
|
7234
|
+
output.info("Fetching storage configuration from source...");
|
|
7235
|
+
try {
|
|
7236
|
+
const siteInfo = await client.pull.siteInfo();
|
|
7237
|
+
sourceSupabaseUrl = siteInfo.supabaseUrl;
|
|
7238
|
+
} catch (error) {
|
|
7239
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
7240
|
+
output.error(`Failed to get storage configuration from source: ${message}`, {
|
|
7241
|
+
suggestion: "Ensure the source CMS is running and accessible"
|
|
7242
|
+
});
|
|
7243
|
+
return;
|
|
7244
|
+
}
|
|
7245
|
+
sourceSiteId = sourceEnv.siteId;
|
|
7246
|
+
targetSiteId = targetEnv.siteId;
|
|
7247
|
+
targetClient = createManagementClient({
|
|
7248
|
+
dashboardUrl: targetEnv.dashboardUrl,
|
|
7249
|
+
managementApiKey: targetEnv.managementApiKey,
|
|
7250
|
+
siteId: targetEnv.siteId
|
|
7251
|
+
});
|
|
7252
|
+
output.info(`Media sync enabled: ${isRemote ? "remote" : "local"} -> ${isRemote ? "local" : "remote"}`);
|
|
7253
|
+
}
|
|
6614
7254
|
if (await contentDirExists(contentDir) && !options.force && !options.yes) {
|
|
6615
7255
|
if (!process.stdin.isTTY) {
|
|
6616
7256
|
output.error("Content directory already exists and --yes not specified", {
|
|
@@ -6693,6 +7333,17 @@ Examples:
|
|
|
6693
7333
|
const contentTypes = Object.keys(result.entries);
|
|
6694
7334
|
result = await fetchAllContentPaginated(client, contentTypes, output);
|
|
6695
7335
|
}
|
|
7336
|
+
if (targetClient && sourceSupabaseUrl && sourceSiteId && targetSiteId) {
|
|
7337
|
+
output.info("Syncing media files...");
|
|
7338
|
+
await syncMediaFiles(
|
|
7339
|
+
result,
|
|
7340
|
+
sourceSupabaseUrl,
|
|
7341
|
+
sourceSiteId,
|
|
7342
|
+
targetSiteId,
|
|
7343
|
+
targetClient,
|
|
7344
|
+
output
|
|
7345
|
+
);
|
|
7346
|
+
}
|
|
6696
7347
|
const { totalCount: totalEntries } = await writeAllEntries(
|
|
6697
7348
|
contentDir,
|
|
6698
7349
|
result.entries,
|
|
@@ -6748,11 +7399,11 @@ function findChangedFields(local, remote, prefix = "") {
|
|
|
6748
7399
|
const changes = [];
|
|
6749
7400
|
const allKeys = /* @__PURE__ */ new Set([...Object.keys(local), ...Object.keys(remote)]);
|
|
6750
7401
|
for (const key of allKeys) {
|
|
6751
|
-
const
|
|
7402
|
+
const path13 = prefix ? `${prefix}.${key}` : key;
|
|
6752
7403
|
const localVal = local[key];
|
|
6753
7404
|
const remoteVal = remote[key];
|
|
6754
7405
|
if (!equal__default.default(localVal, remoteVal)) {
|
|
6755
|
-
changes.push(
|
|
7406
|
+
changes.push(path13);
|
|
6756
7407
|
}
|
|
6757
7408
|
}
|
|
6758
7409
|
return changes;
|
|
@@ -7691,6 +8342,43 @@ Safety:
|
|
|
7691
8342
|
output
|
|
7692
8343
|
);
|
|
7693
8344
|
reportSyncResults(output, result, result.errors.length > 0);
|
|
8345
|
+
if (result.errors.length === 0) {
|
|
8346
|
+
try {
|
|
8347
|
+
output.info("Updating local metadata...");
|
|
8348
|
+
const freshRemote = await client.pull.all();
|
|
8349
|
+
const entriesForMeta = {};
|
|
8350
|
+
if (freshRemote.meta.entries) {
|
|
8351
|
+
for (const [key, meta] of Object.entries(freshRemote.meta.entries)) {
|
|
8352
|
+
const [contentType, identifier] = key.split(":");
|
|
8353
|
+
if (contentType && identifier) {
|
|
8354
|
+
if (!entriesForMeta[contentType]) {
|
|
8355
|
+
entriesForMeta[contentType] = [];
|
|
8356
|
+
}
|
|
8357
|
+
entriesForMeta[contentType].push({
|
|
8358
|
+
identifier,
|
|
8359
|
+
updatedAt: meta.updatedAt,
|
|
8360
|
+
createdAt: meta.createdAt
|
|
8361
|
+
});
|
|
8362
|
+
}
|
|
8363
|
+
}
|
|
8364
|
+
}
|
|
8365
|
+
await updateMetadataAfterPush(contentDir, {
|
|
8366
|
+
entries: entriesForMeta,
|
|
8367
|
+
pages: freshRemote.pages.map((p) => ({
|
|
8368
|
+
identifier: p.identifier,
|
|
8369
|
+
updatedAt: p.updatedAt,
|
|
8370
|
+
createdAt: p.createdAt
|
|
8371
|
+
})),
|
|
8372
|
+
navigation: freshRemote.navigation.map((n) => ({
|
|
8373
|
+
name: n.name,
|
|
8374
|
+
updatedAt: n.updatedAt,
|
|
8375
|
+
createdAt: n.createdAt
|
|
8376
|
+
}))
|
|
8377
|
+
});
|
|
8378
|
+
} catch (metaError) {
|
|
8379
|
+
output.warn('Push succeeded but metadata update failed. Run "pull" to sync metadata.');
|
|
8380
|
+
}
|
|
8381
|
+
}
|
|
7694
8382
|
} catch (error) {
|
|
7695
8383
|
handleCommandError(error, output);
|
|
7696
8384
|
}
|
|
@@ -8505,7 +9193,7 @@ function getContentDir() {
|
|
|
8505
9193
|
function loadTemplate(name) {
|
|
8506
9194
|
const contentDir = getContentDir();
|
|
8507
9195
|
const filePath = path9__namespace.join(contentDir, `${name}.md`);
|
|
8508
|
-
return
|
|
9196
|
+
return fs6.readFileSync(filePath, "utf-8");
|
|
8509
9197
|
}
|
|
8510
9198
|
async function initDocs(options) {
|
|
8511
9199
|
const { rootDir, configPath, agentsPath } = options;
|
|
@@ -8692,6 +9380,3407 @@ Examples:
|
|
|
8692
9380
|
$ riverbankcms identifiers backfill
|
|
8693
9381
|
`).addCommand(backfillCommand);
|
|
8694
9382
|
|
|
9383
|
+
// src/cli/commands/deploy.ts
|
|
9384
|
+
init_load_config();
|
|
9385
|
+
|
|
9386
|
+
// ../api/src/endpoints.ts
|
|
9387
|
+
var ENDPOINT_DEFINITIONS = {
|
|
9388
|
+
// AI endpoints - no cache due to dynamic nature
|
|
9389
|
+
aiContentUpdateChat: {
|
|
9390
|
+
path: "/ai/content-update-chat",
|
|
9391
|
+
method: "POST",
|
|
9392
|
+
auth: "user",
|
|
9393
|
+
responseKind: "stream"
|
|
9394
|
+
},
|
|
9395
|
+
aiChat: {
|
|
9396
|
+
path: "/ai/chat",
|
|
9397
|
+
method: "POST",
|
|
9398
|
+
auth: "user",
|
|
9399
|
+
responseKind: "stream"
|
|
9400
|
+
},
|
|
9401
|
+
applySeoChanges: {
|
|
9402
|
+
path: "/seo/apply",
|
|
9403
|
+
method: "POST",
|
|
9404
|
+
auth: "user",
|
|
9405
|
+
responseKind: "json"
|
|
9406
|
+
},
|
|
9407
|
+
aiCreateBriefChat: {
|
|
9408
|
+
path: "/ai/chat/create-brief",
|
|
9409
|
+
method: "POST",
|
|
9410
|
+
auth: "user",
|
|
9411
|
+
responseKind: "json"
|
|
9412
|
+
},
|
|
9413
|
+
aiPrototypeChat: {
|
|
9414
|
+
path: "/ai/chat/create-prototype",
|
|
9415
|
+
method: "POST",
|
|
9416
|
+
revalidate: 30,
|
|
9417
|
+
// Short cache for AI responses to avoid duplicate calls
|
|
9418
|
+
tags: ["ai-prototype"],
|
|
9419
|
+
auth: "user",
|
|
9420
|
+
responseKind: "json"
|
|
9421
|
+
},
|
|
9422
|
+
aiPatchDryRun: {
|
|
9423
|
+
path: "/ai/patch/dry-run",
|
|
9424
|
+
method: "POST",
|
|
9425
|
+
auth: "user",
|
|
9426
|
+
responseKind: "json"
|
|
9427
|
+
},
|
|
9428
|
+
aiPatchApply: {
|
|
9429
|
+
path: "/ai/patch/apply",
|
|
9430
|
+
method: "POST",
|
|
9431
|
+
auth: "user",
|
|
9432
|
+
responseKind: "json"
|
|
9433
|
+
},
|
|
9434
|
+
aiPlaygroundPropose: {
|
|
9435
|
+
path: "/sites/{siteId}/ai/playground/propose",
|
|
9436
|
+
method: "POST",
|
|
9437
|
+
auth: "user",
|
|
9438
|
+
responseKind: "json"
|
|
9439
|
+
},
|
|
9440
|
+
// Admin SEO
|
|
9441
|
+
listGscPropertiesAdmin: {
|
|
9442
|
+
path: "/admin/seo/gsc/properties",
|
|
9443
|
+
method: "GET",
|
|
9444
|
+
auth: "admin",
|
|
9445
|
+
responseKind: "json"
|
|
9446
|
+
},
|
|
9447
|
+
adminSetGscPersist: {
|
|
9448
|
+
path: "/admin/seo/gsc/meta/persist",
|
|
9449
|
+
method: "POST",
|
|
9450
|
+
auth: "admin",
|
|
9451
|
+
responseKind: "json"
|
|
9452
|
+
},
|
|
9453
|
+
adminStartGscVerification: {
|
|
9454
|
+
path: "/admin/seo/gsc/properties/verify/start",
|
|
9455
|
+
method: "POST",
|
|
9456
|
+
auth: "admin",
|
|
9457
|
+
responseKind: "json"
|
|
9458
|
+
},
|
|
9459
|
+
adminConfirmGscVerification: {
|
|
9460
|
+
path: "/admin/seo/gsc/properties/verify/confirm",
|
|
9461
|
+
method: "POST",
|
|
9462
|
+
auth: "admin",
|
|
9463
|
+
responseKind: "json"
|
|
9464
|
+
},
|
|
9465
|
+
adminRunGscIngest: {
|
|
9466
|
+
path: "/admin/seo/ingest/run",
|
|
9467
|
+
method: "POST",
|
|
9468
|
+
auth: "admin",
|
|
9469
|
+
responseKind: "json"
|
|
9470
|
+
},
|
|
9471
|
+
getSeoVerificationMeta: {
|
|
9472
|
+
path: "/public/seo/verification/meta",
|
|
9473
|
+
method: "GET",
|
|
9474
|
+
auth: "public",
|
|
9475
|
+
responseKind: "json"
|
|
9476
|
+
},
|
|
9477
|
+
checkRedirect: {
|
|
9478
|
+
path: "/api/public/content/redirect",
|
|
9479
|
+
method: "GET",
|
|
9480
|
+
revalidate: 86400,
|
|
9481
|
+
// 24 hours - redirects rarely change
|
|
9482
|
+
tags: ["redirect"],
|
|
9483
|
+
auth: "public",
|
|
9484
|
+
responseKind: "json"
|
|
9485
|
+
},
|
|
9486
|
+
listRedirectRules: {
|
|
9487
|
+
path: "/sites/{siteId}/redirects",
|
|
9488
|
+
method: "GET",
|
|
9489
|
+
auth: "user",
|
|
9490
|
+
responseKind: "json"
|
|
9491
|
+
},
|
|
9492
|
+
createRedirectRule: {
|
|
9493
|
+
path: "/sites/{siteId}/redirects",
|
|
9494
|
+
method: "POST",
|
|
9495
|
+
auth: "user",
|
|
9496
|
+
responseKind: "json"
|
|
9497
|
+
},
|
|
9498
|
+
deleteRedirectRule: {
|
|
9499
|
+
path: "/sites/{siteId}/redirects/{ruleId}",
|
|
9500
|
+
method: "DELETE",
|
|
9501
|
+
auth: "user",
|
|
9502
|
+
responseKind: "json"
|
|
9503
|
+
},
|
|
9504
|
+
// API Keys (Account-level - DEPRECATED, use site-scoped endpoints)
|
|
9505
|
+
listApiKeys: {
|
|
9506
|
+
path: "/account/api-keys",
|
|
9507
|
+
method: "GET",
|
|
9508
|
+
auth: "user",
|
|
9509
|
+
responseKind: "json"
|
|
9510
|
+
},
|
|
9511
|
+
createApiKey: {
|
|
9512
|
+
path: "/account/api-keys",
|
|
9513
|
+
method: "POST",
|
|
9514
|
+
auth: "user",
|
|
9515
|
+
responseKind: "json"
|
|
9516
|
+
},
|
|
9517
|
+
revokeApiKey: {
|
|
9518
|
+
path: "/account/api-keys/{keyId}",
|
|
9519
|
+
method: "DELETE",
|
|
9520
|
+
auth: "user",
|
|
9521
|
+
responseKind: "json"
|
|
9522
|
+
},
|
|
9523
|
+
// API Keys (Site-scoped - preferred)
|
|
9524
|
+
listSiteApiKeys: {
|
|
9525
|
+
path: "/sites/{siteId}/api-keys",
|
|
9526
|
+
method: "GET",
|
|
9527
|
+
auth: "user",
|
|
9528
|
+
tags: ["site-{siteId}", "api-keys"],
|
|
9529
|
+
responseKind: "json"
|
|
9530
|
+
},
|
|
9531
|
+
createSiteApiKey: {
|
|
9532
|
+
path: "/sites/{siteId}/api-keys",
|
|
9533
|
+
method: "POST",
|
|
9534
|
+
auth: "user",
|
|
9535
|
+
tags: ["site-{siteId}", "api-keys"],
|
|
9536
|
+
responseKind: "json"
|
|
9537
|
+
},
|
|
9538
|
+
revokeSiteApiKey: {
|
|
9539
|
+
path: "/sites/{siteId}/api-keys/{keyId}",
|
|
9540
|
+
method: "DELETE",
|
|
9541
|
+
auth: "user",
|
|
9542
|
+
tags: ["site-{siteId}", "api-keys"],
|
|
9543
|
+
responseKind: "json"
|
|
9544
|
+
},
|
|
9545
|
+
getSitePreviewKey: {
|
|
9546
|
+
path: "/sites/{siteId}/api-keys/preview",
|
|
9547
|
+
method: "GET",
|
|
9548
|
+
auth: "user",
|
|
9549
|
+
tags: ["site-{siteId}", "api-keys", "preview-key"],
|
|
9550
|
+
responseKind: "json"
|
|
9551
|
+
},
|
|
9552
|
+
regenerateSitePreviewKey: {
|
|
9553
|
+
path: "/sites/{siteId}/api-keys/preview/regenerate",
|
|
9554
|
+
method: "POST",
|
|
9555
|
+
auth: "user",
|
|
9556
|
+
tags: ["site-{siteId}", "api-keys", "preview-key"],
|
|
9557
|
+
responseKind: "json"
|
|
9558
|
+
},
|
|
9559
|
+
getSiteApiKeyAccessLogs: {
|
|
9560
|
+
path: "/sites/{siteId}/api-keys/access-logs",
|
|
9561
|
+
method: "GET",
|
|
9562
|
+
auth: "user",
|
|
9563
|
+
tags: ["site-{siteId}", "api-keys", "access-logs"],
|
|
9564
|
+
responseKind: "json"
|
|
9565
|
+
},
|
|
9566
|
+
// Management API Keys (SDK write operations)
|
|
9567
|
+
listManagementKeys: {
|
|
9568
|
+
path: "/sites/{siteId}/api-keys/management",
|
|
9569
|
+
method: "GET",
|
|
9570
|
+
auth: "user",
|
|
9571
|
+
tags: ["site-{siteId}", "api-keys", "management-keys"],
|
|
9572
|
+
responseKind: "json"
|
|
9573
|
+
},
|
|
9574
|
+
createManagementKey: {
|
|
9575
|
+
path: "/sites/{siteId}/api-keys/management",
|
|
9576
|
+
method: "POST",
|
|
9577
|
+
auth: "user",
|
|
9578
|
+
tags: ["site-{siteId}", "api-keys", "management-keys"],
|
|
9579
|
+
responseKind: "json"
|
|
9580
|
+
},
|
|
9581
|
+
revokeManagementKey: {
|
|
9582
|
+
path: "/sites/{siteId}/api-keys/management",
|
|
9583
|
+
method: "DELETE",
|
|
9584
|
+
auth: "user",
|
|
9585
|
+
tags: ["site-{siteId}", "api-keys", "management-keys"],
|
|
9586
|
+
responseKind: "json"
|
|
9587
|
+
},
|
|
9588
|
+
getBookingSettings: {
|
|
9589
|
+
path: "/sites/{siteId}/bookings/settings",
|
|
9590
|
+
method: "GET",
|
|
9591
|
+
auth: "user",
|
|
9592
|
+
responseKind: "json"
|
|
9593
|
+
},
|
|
9594
|
+
updateBookingSettings: {
|
|
9595
|
+
path: "/sites/{siteId}/bookings/settings",
|
|
9596
|
+
method: "PUT",
|
|
9597
|
+
auth: "user",
|
|
9598
|
+
responseKind: "json"
|
|
9599
|
+
},
|
|
9600
|
+
listAppointmentResources: {
|
|
9601
|
+
path: "/sites/{siteId}/bookings/resources",
|
|
9602
|
+
method: "GET",
|
|
9603
|
+
auth: "user",
|
|
9604
|
+
responseKind: "json"
|
|
9605
|
+
},
|
|
9606
|
+
createAppointmentResource: {
|
|
9607
|
+
path: "/sites/{siteId}/bookings/resources",
|
|
9608
|
+
method: "POST",
|
|
9609
|
+
auth: "user",
|
|
9610
|
+
responseKind: "json"
|
|
9611
|
+
},
|
|
9612
|
+
getAppointmentResource: {
|
|
9613
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}",
|
|
9614
|
+
method: "GET",
|
|
9615
|
+
auth: "user",
|
|
9616
|
+
responseKind: "json"
|
|
9617
|
+
},
|
|
9618
|
+
updateAppointmentResource: {
|
|
9619
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}",
|
|
9620
|
+
method: "PUT",
|
|
9621
|
+
auth: "user",
|
|
9622
|
+
responseKind: "json"
|
|
9623
|
+
},
|
|
9624
|
+
deleteAppointmentResource: {
|
|
9625
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}",
|
|
9626
|
+
method: "DELETE",
|
|
9627
|
+
auth: "user",
|
|
9628
|
+
responseKind: "json"
|
|
9629
|
+
},
|
|
9630
|
+
listAppointmentServices: {
|
|
9631
|
+
path: "/sites/{siteId}/bookings/services",
|
|
9632
|
+
method: "GET",
|
|
9633
|
+
auth: "user",
|
|
9634
|
+
responseKind: "json"
|
|
9635
|
+
},
|
|
9636
|
+
createAppointmentService: {
|
|
9637
|
+
path: "/sites/{siteId}/bookings/services",
|
|
9638
|
+
method: "POST",
|
|
9639
|
+
auth: "user",
|
|
9640
|
+
responseKind: "json"
|
|
9641
|
+
},
|
|
9642
|
+
getAppointmentService: {
|
|
9643
|
+
path: "/sites/{siteId}/bookings/services/{serviceId}",
|
|
9644
|
+
method: "GET",
|
|
9645
|
+
auth: "user",
|
|
9646
|
+
responseKind: "json"
|
|
9647
|
+
},
|
|
9648
|
+
updateAppointmentService: {
|
|
9649
|
+
path: "/sites/{siteId}/bookings/services/{serviceId}",
|
|
9650
|
+
method: "PUT",
|
|
9651
|
+
auth: "user",
|
|
9652
|
+
responseKind: "json"
|
|
9653
|
+
},
|
|
9654
|
+
deleteAppointmentService: {
|
|
9655
|
+
path: "/sites/{siteId}/bookings/services/{serviceId}",
|
|
9656
|
+
method: "DELETE",
|
|
9657
|
+
auth: "user",
|
|
9658
|
+
responseKind: "json"
|
|
9659
|
+
},
|
|
9660
|
+
getAppointmentServicesReference: {
|
|
9661
|
+
path: "/sites/{siteId}/bookings/services/reference",
|
|
9662
|
+
method: "GET",
|
|
9663
|
+
auth: "user",
|
|
9664
|
+
responseKind: "json"
|
|
9665
|
+
},
|
|
9666
|
+
getAppointmentResourcesReference: {
|
|
9667
|
+
path: "/sites/{siteId}/bookings/resources/reference",
|
|
9668
|
+
method: "GET",
|
|
9669
|
+
auth: "user",
|
|
9670
|
+
responseKind: "json"
|
|
9671
|
+
},
|
|
9672
|
+
// Service-Resource linking
|
|
9673
|
+
getResourceServices: {
|
|
9674
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}/services",
|
|
9675
|
+
method: "GET",
|
|
9676
|
+
auth: "user",
|
|
9677
|
+
responseKind: "json"
|
|
9678
|
+
},
|
|
9679
|
+
updateResourceServices: {
|
|
9680
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}/services",
|
|
9681
|
+
method: "PUT",
|
|
9682
|
+
auth: "user",
|
|
9683
|
+
responseKind: "json"
|
|
9684
|
+
},
|
|
9685
|
+
getServiceResources: {
|
|
9686
|
+
path: "/sites/{siteId}/bookings/services/{serviceId}/resources",
|
|
9687
|
+
method: "GET",
|
|
9688
|
+
auth: "user",
|
|
9689
|
+
responseKind: "json"
|
|
9690
|
+
},
|
|
9691
|
+
updateServiceResources: {
|
|
9692
|
+
path: "/sites/{siteId}/bookings/services/{serviceId}/resources",
|
|
9693
|
+
method: "PUT",
|
|
9694
|
+
auth: "user",
|
|
9695
|
+
responseKind: "json"
|
|
9696
|
+
},
|
|
9697
|
+
// Availability management
|
|
9698
|
+
listAvailabilityRules: {
|
|
9699
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}/availability",
|
|
9700
|
+
method: "GET",
|
|
9701
|
+
auth: "user",
|
|
9702
|
+
responseKind: "json"
|
|
9703
|
+
},
|
|
9704
|
+
upsertAvailabilityRule: {
|
|
9705
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}/availability",
|
|
9706
|
+
method: "POST",
|
|
9707
|
+
auth: "user",
|
|
9708
|
+
responseKind: "json"
|
|
9709
|
+
},
|
|
9710
|
+
deleteAvailabilityRule: {
|
|
9711
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}/availability/{ruleId}",
|
|
9712
|
+
method: "DELETE",
|
|
9713
|
+
auth: "user",
|
|
9714
|
+
responseKind: "json"
|
|
9715
|
+
},
|
|
9716
|
+
listBlackouts: {
|
|
9717
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}/blackouts",
|
|
9718
|
+
method: "GET",
|
|
9719
|
+
auth: "user",
|
|
9720
|
+
responseKind: "json"
|
|
9721
|
+
},
|
|
9722
|
+
createBlackout: {
|
|
9723
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}/blackouts",
|
|
9724
|
+
method: "POST",
|
|
9725
|
+
auth: "user",
|
|
9726
|
+
responseKind: "json"
|
|
9727
|
+
},
|
|
9728
|
+
deleteBlackout: {
|
|
9729
|
+
path: "/sites/{siteId}/bookings/resources/{resourceId}/blackouts/{blackoutId}",
|
|
9730
|
+
method: "DELETE",
|
|
9731
|
+
auth: "user",
|
|
9732
|
+
responseKind: "json"
|
|
9733
|
+
},
|
|
9734
|
+
getAvailableSlots: {
|
|
9735
|
+
path: "/sites/{siteId}/bookings/availability/slots",
|
|
9736
|
+
method: "GET",
|
|
9737
|
+
auth: "user",
|
|
9738
|
+
responseKind: "json"
|
|
9739
|
+
},
|
|
9740
|
+
createAppointment: {
|
|
9741
|
+
path: "/sites/{siteId}/bookings/appointments",
|
|
9742
|
+
method: "POST",
|
|
9743
|
+
auth: "user",
|
|
9744
|
+
responseKind: "json"
|
|
9745
|
+
},
|
|
9746
|
+
// Data retrieval endpoints - good candidates for caching
|
|
9747
|
+
getBrief: {
|
|
9748
|
+
path: "/briefs",
|
|
9749
|
+
method: "GET",
|
|
9750
|
+
revalidate: 120,
|
|
9751
|
+
// 2 minutes
|
|
9752
|
+
tags: ["brief"],
|
|
9753
|
+
auth: "user",
|
|
9754
|
+
responseKind: "json"
|
|
9755
|
+
},
|
|
9756
|
+
// Unified site data endpoint - use this for all site lookups
|
|
9757
|
+
getSite: {
|
|
9758
|
+
path: "/sites",
|
|
9759
|
+
method: "GET",
|
|
9760
|
+
revalidate: 900,
|
|
9761
|
+
// 15 minutes - site data changes less frequently
|
|
9762
|
+
tags: ["site"],
|
|
9763
|
+
auth: "user",
|
|
9764
|
+
responseKind: "json"
|
|
9765
|
+
},
|
|
9766
|
+
// DEPRECATED: Use getSite with ?slug={slug} instead
|
|
9767
|
+
getSiteBySlug: {
|
|
9768
|
+
path: "/sites/by-slug/{slug}",
|
|
9769
|
+
method: "GET",
|
|
9770
|
+
revalidate: 900,
|
|
9771
|
+
// 15 minutes - site data changes less frequently
|
|
9772
|
+
tags: ["site", "site-{slug}"],
|
|
9773
|
+
auth: "user",
|
|
9774
|
+
responseKind: "json"
|
|
9775
|
+
},
|
|
9776
|
+
// DEPRECATED: Use getSite with ?domain={domain} instead
|
|
9777
|
+
getSiteByDomain: {
|
|
9778
|
+
path: "/sites/by-domain/{domain}",
|
|
9779
|
+
method: "GET",
|
|
9780
|
+
revalidate: 900,
|
|
9781
|
+
// 15 minutes - site data changes less frequently
|
|
9782
|
+
tags: ["site", "site-domain-{domain}"],
|
|
9783
|
+
auth: "user",
|
|
9784
|
+
responseKind: "json"
|
|
9785
|
+
},
|
|
9786
|
+
createBriefTurn: {
|
|
9787
|
+
path: "/brief-turns",
|
|
9788
|
+
method: "POST",
|
|
9789
|
+
tags: ["brief"],
|
|
9790
|
+
// Tags for invalidation after mutation
|
|
9791
|
+
auth: "user",
|
|
9792
|
+
responseKind: "json"
|
|
9793
|
+
},
|
|
9794
|
+
upsertBrief: {
|
|
9795
|
+
path: "/briefs",
|
|
9796
|
+
method: "PUT",
|
|
9797
|
+
tags: ["brief"],
|
|
9798
|
+
auth: "user",
|
|
9799
|
+
responseKind: "json"
|
|
9800
|
+
},
|
|
9801
|
+
aiBriefToSpec: {
|
|
9802
|
+
path: "/ai/actions/brief-to-spec",
|
|
9803
|
+
method: "POST",
|
|
9804
|
+
tags: ["brief", "spec"],
|
|
9805
|
+
auth: "user",
|
|
9806
|
+
responseKind: "json"
|
|
9807
|
+
},
|
|
9808
|
+
createSite: {
|
|
9809
|
+
path: "/sites",
|
|
9810
|
+
method: "POST",
|
|
9811
|
+
tags: ["site"],
|
|
9812
|
+
auth: "user",
|
|
9813
|
+
responseKind: "json"
|
|
9814
|
+
},
|
|
9815
|
+
createSiteManual: {
|
|
9816
|
+
path: "/sites/manual",
|
|
9817
|
+
method: "POST",
|
|
9818
|
+
tags: ["site"],
|
|
9819
|
+
auth: "user",
|
|
9820
|
+
responseKind: "json"
|
|
9821
|
+
},
|
|
9822
|
+
updateSite: {
|
|
9823
|
+
path: "/sites",
|
|
9824
|
+
method: "PUT",
|
|
9825
|
+
tags: ["site"],
|
|
9826
|
+
auth: "user",
|
|
9827
|
+
responseKind: "json"
|
|
9828
|
+
},
|
|
9829
|
+
deleteSite: {
|
|
9830
|
+
path: "/sites/{siteId}",
|
|
9831
|
+
method: "DELETE",
|
|
9832
|
+
tags: ["site", "site-{siteId}"],
|
|
9833
|
+
auth: "user",
|
|
9834
|
+
responseKind: "json"
|
|
9835
|
+
},
|
|
9836
|
+
listSiteMembers: {
|
|
9837
|
+
path: "/sites/{siteId}/members",
|
|
9838
|
+
method: "GET",
|
|
9839
|
+
tags: ["site-{siteId}", "site-members-{siteId}"],
|
|
9840
|
+
auth: "user",
|
|
9841
|
+
responseKind: "json"
|
|
9842
|
+
},
|
|
9843
|
+
inviteSiteMember: {
|
|
9844
|
+
path: "/sites/{siteId}/members",
|
|
9845
|
+
method: "POST",
|
|
9846
|
+
tags: ["site-{siteId}", "site-members-{siteId}"],
|
|
9847
|
+
auth: "user",
|
|
9848
|
+
responseKind: "json"
|
|
9849
|
+
},
|
|
9850
|
+
updateSiteMemberRole: {
|
|
9851
|
+
path: "/sites/{siteId}/members/{memberId}",
|
|
9852
|
+
method: "PATCH",
|
|
9853
|
+
tags: ["site-{siteId}", "site-members-{siteId}", "site-member-{memberId}"],
|
|
9854
|
+
auth: "user",
|
|
9855
|
+
responseKind: "json"
|
|
9856
|
+
},
|
|
9857
|
+
authLogin: {
|
|
9858
|
+
path: "/auth/login/submit",
|
|
9859
|
+
method: "POST",
|
|
9860
|
+
tags: ["auth"],
|
|
9861
|
+
auth: "public",
|
|
9862
|
+
responseKind: "json"
|
|
9863
|
+
},
|
|
9864
|
+
authForgotPassword: {
|
|
9865
|
+
path: "/auth/forgot/submit",
|
|
9866
|
+
method: "POST",
|
|
9867
|
+
tags: ["auth"],
|
|
9868
|
+
auth: "public",
|
|
9869
|
+
responseKind: "json"
|
|
9870
|
+
},
|
|
9871
|
+
authRegister: {
|
|
9872
|
+
path: "/auth/register/submit",
|
|
9873
|
+
method: "POST",
|
|
9874
|
+
tags: ["auth"],
|
|
9875
|
+
auth: "public",
|
|
9876
|
+
responseKind: "json"
|
|
9877
|
+
},
|
|
9878
|
+
authReauthenticate: {
|
|
9879
|
+
path: "/auth/reauth/submit",
|
|
9880
|
+
method: "POST",
|
|
9881
|
+
tags: ["auth"],
|
|
9882
|
+
auth: "public",
|
|
9883
|
+
responseKind: "json"
|
|
9884
|
+
},
|
|
9885
|
+
lookupSiteDomains: {
|
|
9886
|
+
path: "/sites/{siteId}/domains/lookup",
|
|
9887
|
+
method: "POST",
|
|
9888
|
+
tags: ["site-{siteId}", "site-domains"],
|
|
9889
|
+
auth: "user",
|
|
9890
|
+
responseKind: "json"
|
|
9891
|
+
},
|
|
9892
|
+
registerSiteDomain: {
|
|
9893
|
+
path: "/sites/{siteId}/domains/register",
|
|
9894
|
+
method: "POST",
|
|
9895
|
+
tags: ["site-{siteId}", "site-domains"],
|
|
9896
|
+
auth: "user",
|
|
9897
|
+
responseKind: "json"
|
|
9898
|
+
},
|
|
9899
|
+
addCustomDomain: {
|
|
9900
|
+
path: "/sites/{siteId}/domains/custom",
|
|
9901
|
+
method: "POST",
|
|
9902
|
+
tags: ["site-{siteId}", "site-domains"],
|
|
9903
|
+
auth: "user",
|
|
9904
|
+
responseKind: "json"
|
|
9905
|
+
},
|
|
9906
|
+
removeCustomDomain: {
|
|
9907
|
+
path: "/sites/{siteId}/domains/custom",
|
|
9908
|
+
method: "DELETE",
|
|
9909
|
+
tags: ["site-{siteId}", "site-domains"],
|
|
9910
|
+
auth: "user",
|
|
9911
|
+
responseKind: "json"
|
|
9912
|
+
},
|
|
9913
|
+
syncCustomDomainToEdgeConfig: {
|
|
9914
|
+
path: "/sites/{siteId}/domains/custom/sync",
|
|
9915
|
+
method: "POST",
|
|
9916
|
+
tags: ["site-{siteId}", "site-domains"],
|
|
9917
|
+
auth: "user",
|
|
9918
|
+
responseKind: "json"
|
|
9919
|
+
},
|
|
9920
|
+
removeSiteMember: {
|
|
9921
|
+
path: "/sites/{siteId}/members/{memberId}",
|
|
9922
|
+
method: "DELETE",
|
|
9923
|
+
tags: ["site-{siteId}", "site-members-{siteId}", "site-member-{memberId}"],
|
|
9924
|
+
auth: "user",
|
|
9925
|
+
responseKind: "json"
|
|
9926
|
+
},
|
|
9927
|
+
revokeSiteInvitation: {
|
|
9928
|
+
path: "/sites/{siteId}/members/invitations/{invitationId}",
|
|
9929
|
+
method: "DELETE",
|
|
9930
|
+
tags: ["site-{siteId}", "site-members-{siteId}", "site-invite-{invitationId}"],
|
|
9931
|
+
auth: "user",
|
|
9932
|
+
responseKind: "json"
|
|
9933
|
+
},
|
|
9934
|
+
transferSiteOwnership: {
|
|
9935
|
+
path: "/sites/{siteId}/members/transfer-ownership",
|
|
9936
|
+
method: "POST",
|
|
9937
|
+
tags: ["site-{siteId}", "site-members-{siteId}"],
|
|
9938
|
+
auth: "user",
|
|
9939
|
+
responseKind: "json"
|
|
9940
|
+
},
|
|
9941
|
+
listContentEntries: {
|
|
9942
|
+
path: "/sites/{siteId}/content/{type}",
|
|
9943
|
+
method: "GET",
|
|
9944
|
+
revalidate: 60,
|
|
9945
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}"],
|
|
9946
|
+
auth: "user",
|
|
9947
|
+
responseKind: "json"
|
|
9948
|
+
},
|
|
9949
|
+
getContentTemplate: {
|
|
9950
|
+
path: "/sites/{siteId}/content/types/{type}/template",
|
|
9951
|
+
method: "GET",
|
|
9952
|
+
revalidate: 60,
|
|
9953
|
+
tags: ["site-{siteId}", "content-template-{siteId}-{type}"],
|
|
9954
|
+
auth: "user",
|
|
9955
|
+
responseKind: "json"
|
|
9956
|
+
},
|
|
9957
|
+
updateContentTemplateBlock: {
|
|
9958
|
+
path: "/sites/{siteId}/content/types/{type}/template/blocks/{blockId}",
|
|
9959
|
+
method: "PATCH",
|
|
9960
|
+
tags: ["site-{siteId}", "content-template-{siteId}-{type}", "content-template-block-{blockId}"],
|
|
9961
|
+
auth: "user",
|
|
9962
|
+
responseKind: "json"
|
|
9963
|
+
},
|
|
9964
|
+
applyContentTemplateAddon: {
|
|
9965
|
+
path: "/sites/{siteId}/content/types/{type}/template/addons",
|
|
9966
|
+
method: "POST",
|
|
9967
|
+
tags: ["site-{siteId}", "content-template-{siteId}-{type}"],
|
|
9968
|
+
auth: "user",
|
|
9969
|
+
responseKind: "json"
|
|
9970
|
+
},
|
|
9971
|
+
updateContentTemplateBlockBindings: {
|
|
9972
|
+
path: "/sites/{siteId}/content/types/{type}/template/blocks/{blockId}/bindings",
|
|
9973
|
+
method: "PATCH",
|
|
9974
|
+
tags: ["site-{siteId}", "content-template-{siteId}-{type}", "content-template-block-{blockId}"],
|
|
9975
|
+
auth: "user",
|
|
9976
|
+
responseKind: "json"
|
|
9977
|
+
},
|
|
9978
|
+
createTemplateBlock: {
|
|
9979
|
+
path: "/sites/{siteId}/content/types/{type}/template/blocks",
|
|
9980
|
+
method: "POST",
|
|
9981
|
+
tags: ["site-{siteId}", "content-template-{siteId}-{type}"],
|
|
9982
|
+
auth: "user",
|
|
9983
|
+
responseKind: "json"
|
|
9984
|
+
},
|
|
9985
|
+
deleteTemplateBlock: {
|
|
9986
|
+
path: "/sites/{siteId}/content/types/{type}/template/blocks/{blockId}",
|
|
9987
|
+
method: "DELETE",
|
|
9988
|
+
tags: ["site-{siteId}", "content-template-{siteId}-{type}", "content-template-block-{blockId}"],
|
|
9989
|
+
auth: "user",
|
|
9990
|
+
responseKind: "json"
|
|
9991
|
+
},
|
|
9992
|
+
reorderTemplateBlocks: {
|
|
9993
|
+
path: "/sites/{siteId}/content/types/{type}/template/blocks/reorder",
|
|
9994
|
+
method: "POST",
|
|
9995
|
+
tags: ["site-{siteId}", "content-template-{siteId}-{type}"],
|
|
9996
|
+
auth: "user",
|
|
9997
|
+
responseKind: "json"
|
|
9998
|
+
},
|
|
9999
|
+
getTransforms: {
|
|
10000
|
+
path: "/transforms",
|
|
10001
|
+
method: "GET",
|
|
10002
|
+
revalidate: 3600,
|
|
10003
|
+
tags: ["transforms"],
|
|
10004
|
+
auth: "public",
|
|
10005
|
+
responseKind: "json"
|
|
10006
|
+
},
|
|
10007
|
+
createContentEntry: {
|
|
10008
|
+
path: "/sites/{siteId}/content/{type}",
|
|
10009
|
+
method: "POST",
|
|
10010
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}"],
|
|
10011
|
+
auth: "user",
|
|
10012
|
+
responseKind: "json"
|
|
10013
|
+
},
|
|
10014
|
+
getContentEntry: {
|
|
10015
|
+
path: "/sites/{siteId}/content/{type}/{entryId}",
|
|
10016
|
+
method: "GET",
|
|
10017
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}", "content-entry-{entryId}"],
|
|
10018
|
+
auth: "user",
|
|
10019
|
+
responseKind: "json"
|
|
10020
|
+
},
|
|
10021
|
+
updateContentEntry: {
|
|
10022
|
+
path: "/sites/{siteId}/content/{type}/{entryId}",
|
|
10023
|
+
method: "PUT",
|
|
10024
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}", "content-entry-{entryId}"],
|
|
10025
|
+
auth: "user",
|
|
10026
|
+
responseKind: "json"
|
|
10027
|
+
},
|
|
10028
|
+
updateContentEntryContent: {
|
|
10029
|
+
path: "/sites/{siteId}/content/{type}/{entryId}/content",
|
|
10030
|
+
method: "PUT",
|
|
10031
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}", "content-entry-{entryId}"],
|
|
10032
|
+
auth: "user",
|
|
10033
|
+
responseKind: "json"
|
|
10034
|
+
},
|
|
10035
|
+
updateRouteMetadata: {
|
|
10036
|
+
path: "/sites/{siteId}/routes/{routeId}/metadata",
|
|
10037
|
+
method: "PATCH",
|
|
10038
|
+
tags: ["site-{siteId}", "route-{routeId}"],
|
|
10039
|
+
auth: "user",
|
|
10040
|
+
responseKind: "json"
|
|
10041
|
+
},
|
|
10042
|
+
publishContentEntry: {
|
|
10043
|
+
path: "/sites/{siteId}/content/{type}/{entryId}/publish",
|
|
10044
|
+
method: "POST",
|
|
10045
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}", "content-entry-{entryId}"],
|
|
10046
|
+
auth: "user",
|
|
10047
|
+
responseKind: "json"
|
|
10048
|
+
},
|
|
10049
|
+
discardContentEntry: {
|
|
10050
|
+
path: "/sites/{siteId}/content/{type}/{entryId}/discard",
|
|
10051
|
+
method: "POST",
|
|
10052
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}", "content-entry-{entryId}"],
|
|
10053
|
+
auth: "user",
|
|
10054
|
+
responseKind: "json"
|
|
10055
|
+
},
|
|
10056
|
+
unpublishContentEntry: {
|
|
10057
|
+
path: "/sites/{siteId}/content/{type}/{entryId}/unpublish",
|
|
10058
|
+
method: "POST",
|
|
10059
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}", "content-entry-{entryId}"],
|
|
10060
|
+
auth: "user",
|
|
10061
|
+
responseKind: "json"
|
|
10062
|
+
},
|
|
10063
|
+
// Content types: enable + setup
|
|
10064
|
+
listContentTypes: {
|
|
10065
|
+
path: "/sites/{siteId}/content-types",
|
|
10066
|
+
method: "GET",
|
|
10067
|
+
revalidate: 120,
|
|
10068
|
+
// 2 minutes
|
|
10069
|
+
tags: ["site-{siteId}", "content-types"],
|
|
10070
|
+
auth: "user",
|
|
10071
|
+
responseKind: "json"
|
|
10072
|
+
},
|
|
10073
|
+
enableContentType: {
|
|
10074
|
+
path: "/sites/{siteId}/content-types/{key}/enable",
|
|
10075
|
+
method: "POST",
|
|
10076
|
+
tags: ["site-{siteId}", "content-types", "content-type-{key}"],
|
|
10077
|
+
auth: "user",
|
|
10078
|
+
responseKind: "json"
|
|
10079
|
+
},
|
|
10080
|
+
setupContentType: {
|
|
10081
|
+
path: "/sites/{siteId}/content-types/{key}/setup",
|
|
10082
|
+
method: "POST",
|
|
10083
|
+
tags: ["site-{siteId}", "content-types", "content-type-{key}"],
|
|
10084
|
+
auth: "user",
|
|
10085
|
+
responseKind: "json"
|
|
10086
|
+
},
|
|
10087
|
+
deleteContentEntry: {
|
|
10088
|
+
path: "/sites/{siteId}/content/{type}/{entryId}",
|
|
10089
|
+
method: "DELETE",
|
|
10090
|
+
tags: ["site-{siteId}", "content-{siteId}-{type}", "content-entry-{entryId}"],
|
|
10091
|
+
auth: "user",
|
|
10092
|
+
responseKind: "json"
|
|
10093
|
+
},
|
|
10094
|
+
updateSiteGeneralSettings: {
|
|
10095
|
+
path: "/sites/{siteId}/settings/general",
|
|
10096
|
+
method: "POST",
|
|
10097
|
+
tags: ["site-{siteId}", "site-settings-{siteId}"],
|
|
10098
|
+
auth: "user",
|
|
10099
|
+
responseKind: "json"
|
|
10100
|
+
},
|
|
10101
|
+
getSiteLayoutSettings: {
|
|
10102
|
+
path: "/sites/{siteId}/settings/layout",
|
|
10103
|
+
method: "GET",
|
|
10104
|
+
revalidate: 120,
|
|
10105
|
+
tags: ["site-{siteId}", "site-settings-{siteId}"],
|
|
10106
|
+
auth: "user",
|
|
10107
|
+
responseKind: "json"
|
|
10108
|
+
},
|
|
10109
|
+
updateSiteLayoutSettings: {
|
|
10110
|
+
path: "/sites/{siteId}/settings/layout",
|
|
10111
|
+
method: "POST",
|
|
10112
|
+
tags: ["site-{siteId}", "site-settings-{siteId}"],
|
|
10113
|
+
auth: "user",
|
|
10114
|
+
responseKind: "json"
|
|
10115
|
+
},
|
|
10116
|
+
getMaintenanceSettings: {
|
|
10117
|
+
path: "/sites/{siteId}/settings/maintenance",
|
|
10118
|
+
method: "GET",
|
|
10119
|
+
revalidate: 120,
|
|
10120
|
+
tags: ["site-{siteId}", "site-settings-{siteId}"],
|
|
10121
|
+
auth: "user",
|
|
10122
|
+
responseKind: "json"
|
|
10123
|
+
},
|
|
10124
|
+
updateMaintenanceSettings: {
|
|
10125
|
+
path: "/sites/{siteId}/settings/maintenance",
|
|
10126
|
+
method: "POST",
|
|
10127
|
+
tags: ["site-{siteId}", "site-settings-{siteId}"],
|
|
10128
|
+
auth: "user",
|
|
10129
|
+
responseKind: "json"
|
|
10130
|
+
},
|
|
10131
|
+
setHomepage: {
|
|
10132
|
+
path: "/sites/{siteId}/settings/homepage",
|
|
10133
|
+
method: "POST",
|
|
10134
|
+
tags: ["site-{siteId}", "site-homepage"],
|
|
10135
|
+
auth: "user",
|
|
10136
|
+
responseKind: "json"
|
|
10137
|
+
},
|
|
10138
|
+
listAccountDomains: {
|
|
10139
|
+
path: "/domains",
|
|
10140
|
+
method: "GET",
|
|
10141
|
+
tags: ["domains"],
|
|
10142
|
+
auth: "user",
|
|
10143
|
+
responseKind: "json"
|
|
10144
|
+
},
|
|
10145
|
+
searchDomains: {
|
|
10146
|
+
path: "/domains/search",
|
|
10147
|
+
method: "POST",
|
|
10148
|
+
tags: ["domains"],
|
|
10149
|
+
auth: "user",
|
|
10150
|
+
responseKind: "json"
|
|
10151
|
+
},
|
|
10152
|
+
registerDomain: {
|
|
10153
|
+
path: "/domains/register",
|
|
10154
|
+
method: "POST",
|
|
10155
|
+
tags: ["domains"],
|
|
10156
|
+
auth: "user",
|
|
10157
|
+
responseKind: "json"
|
|
10158
|
+
},
|
|
10159
|
+
assignDomain: {
|
|
10160
|
+
path: "/domains/{domainId}/assign",
|
|
10161
|
+
method: "POST",
|
|
10162
|
+
tags: ["domains", "domain-{domainId}"],
|
|
10163
|
+
auth: "user",
|
|
10164
|
+
responseKind: "json"
|
|
10165
|
+
},
|
|
10166
|
+
adminStartImpersonation: {
|
|
10167
|
+
path: "/admin/impersonation/start",
|
|
10168
|
+
method: "POST",
|
|
10169
|
+
tags: ["admin", "impersonation"],
|
|
10170
|
+
auth: "user",
|
|
10171
|
+
responseKind: "json"
|
|
10172
|
+
},
|
|
10173
|
+
adminStopImpersonation: {
|
|
10174
|
+
path: "/admin/impersonation/stop",
|
|
10175
|
+
method: "POST",
|
|
10176
|
+
tags: ["admin", "impersonation"],
|
|
10177
|
+
auth: "user",
|
|
10178
|
+
responseKind: "json"
|
|
10179
|
+
},
|
|
10180
|
+
adminAssignRole: {
|
|
10181
|
+
path: "/admin/roles/assign",
|
|
10182
|
+
method: "POST",
|
|
10183
|
+
tags: ["admin", "roles"],
|
|
10184
|
+
auth: "user",
|
|
10185
|
+
responseKind: "json"
|
|
10186
|
+
},
|
|
10187
|
+
adminRevokeRole: {
|
|
10188
|
+
path: "/admin/roles/revoke",
|
|
10189
|
+
method: "POST",
|
|
10190
|
+
tags: ["admin", "roles"],
|
|
10191
|
+
auth: "user",
|
|
10192
|
+
responseKind: "json"
|
|
10193
|
+
},
|
|
10194
|
+
adminChangePlan: {
|
|
10195
|
+
path: "/admin/billing/plan",
|
|
10196
|
+
method: "POST",
|
|
10197
|
+
tags: ["admin", "billing"],
|
|
10198
|
+
auth: "user",
|
|
10199
|
+
responseKind: "json"
|
|
10200
|
+
},
|
|
10201
|
+
changePlan: {
|
|
10202
|
+
path: "/billing/plan/change",
|
|
10203
|
+
method: "POST",
|
|
10204
|
+
tags: ["billing"],
|
|
10205
|
+
auth: "user",
|
|
10206
|
+
responseKind: "json"
|
|
10207
|
+
},
|
|
10208
|
+
mfaTotpEnroll: {
|
|
10209
|
+
path: "/auth/mfa/totp/enroll",
|
|
10210
|
+
method: "POST",
|
|
10211
|
+
tags: ["mfa"],
|
|
10212
|
+
auth: "user",
|
|
10213
|
+
responseKind: "json"
|
|
10214
|
+
},
|
|
10215
|
+
mfaTotpVerify: {
|
|
10216
|
+
path: "/auth/mfa/totp/verify",
|
|
10217
|
+
method: "POST",
|
|
10218
|
+
tags: ["mfa"],
|
|
10219
|
+
auth: "user",
|
|
10220
|
+
responseKind: "json"
|
|
10221
|
+
},
|
|
10222
|
+
mfaTotpActivate: {
|
|
10223
|
+
path: "/auth/mfa/totp/activate",
|
|
10224
|
+
method: "POST",
|
|
10225
|
+
tags: ["mfa"],
|
|
10226
|
+
auth: "user",
|
|
10227
|
+
responseKind: "json"
|
|
10228
|
+
},
|
|
10229
|
+
mfaDeleteFactor: {
|
|
10230
|
+
path: "/auth/mfa/factors/{factorId}",
|
|
10231
|
+
method: "DELETE",
|
|
10232
|
+
tags: ["mfa"],
|
|
10233
|
+
auth: "user",
|
|
10234
|
+
responseKind: "json"
|
|
10235
|
+
},
|
|
10236
|
+
mfaBackupCodesGet: {
|
|
10237
|
+
path: "/auth/mfa/backup-codes",
|
|
10238
|
+
method: "GET",
|
|
10239
|
+
tags: ["mfa"],
|
|
10240
|
+
auth: "user",
|
|
10241
|
+
responseKind: "json"
|
|
10242
|
+
},
|
|
10243
|
+
mfaBackupCodesRotate: {
|
|
10244
|
+
path: "/auth/mfa/backup-codes/rotate",
|
|
10245
|
+
method: "POST",
|
|
10246
|
+
tags: ["mfa"],
|
|
10247
|
+
auth: "user",
|
|
10248
|
+
responseKind: "json"
|
|
10249
|
+
},
|
|
10250
|
+
mfaPhoneEnroll: {
|
|
10251
|
+
path: "/auth/mfa/phone/enroll",
|
|
10252
|
+
method: "POST",
|
|
10253
|
+
tags: ["mfa"],
|
|
10254
|
+
auth: "user",
|
|
10255
|
+
responseKind: "json"
|
|
10256
|
+
},
|
|
10257
|
+
mfaPhoneChallenge: {
|
|
10258
|
+
path: "/auth/mfa/phone/challenge",
|
|
10259
|
+
method: "POST",
|
|
10260
|
+
tags: ["mfa"],
|
|
10261
|
+
auth: "user",
|
|
10262
|
+
responseKind: "json"
|
|
10263
|
+
},
|
|
10264
|
+
mfaPhoneVerify: {
|
|
10265
|
+
path: "/auth/mfa/phone/verify",
|
|
10266
|
+
method: "POST",
|
|
10267
|
+
tags: ["mfa"],
|
|
10268
|
+
auth: "user",
|
|
10269
|
+
responseKind: "json"
|
|
10270
|
+
},
|
|
10271
|
+
accountUpdatePassword: {
|
|
10272
|
+
path: "/account/password/update",
|
|
10273
|
+
method: "POST",
|
|
10274
|
+
tags: ["account"],
|
|
10275
|
+
auth: "user",
|
|
10276
|
+
responseKind: "json"
|
|
10277
|
+
},
|
|
10278
|
+
accountRevokeSessions: {
|
|
10279
|
+
path: "/account/sessions/revoke",
|
|
10280
|
+
method: "POST",
|
|
10281
|
+
tags: ["account"],
|
|
10282
|
+
auth: "user",
|
|
10283
|
+
responseKind: "json"
|
|
10284
|
+
},
|
|
10285
|
+
adminListInvites: {
|
|
10286
|
+
path: "/admin/invites",
|
|
10287
|
+
method: "GET",
|
|
10288
|
+
tags: ["admin", "invites"],
|
|
10289
|
+
auth: "user",
|
|
10290
|
+
responseKind: "json"
|
|
10291
|
+
},
|
|
10292
|
+
adminCreateInvite: {
|
|
10293
|
+
path: "/admin/invites",
|
|
10294
|
+
method: "POST",
|
|
10295
|
+
tags: ["admin", "invites"],
|
|
10296
|
+
auth: "user",
|
|
10297
|
+
responseKind: "json"
|
|
10298
|
+
},
|
|
10299
|
+
adminRevokeInvite: {
|
|
10300
|
+
path: "/admin/invites/{inviteId}",
|
|
10301
|
+
method: "DELETE",
|
|
10302
|
+
tags: ["admin", "invites"],
|
|
10303
|
+
auth: "user",
|
|
10304
|
+
responseKind: "json"
|
|
10305
|
+
},
|
|
10306
|
+
adminCreateUser: {
|
|
10307
|
+
path: "/admin/users",
|
|
10308
|
+
method: "POST",
|
|
10309
|
+
tags: ["admin", "users"],
|
|
10310
|
+
auth: "admin",
|
|
10311
|
+
responseKind: "json"
|
|
10312
|
+
},
|
|
10313
|
+
adminListAllowedDomains: {
|
|
10314
|
+
path: "/admin/allowed-domains",
|
|
10315
|
+
method: "GET",
|
|
10316
|
+
tags: ["admin", "allowed-domains"],
|
|
10317
|
+
auth: "user",
|
|
10318
|
+
responseKind: "json"
|
|
10319
|
+
},
|
|
10320
|
+
adminAddAllowedDomain: {
|
|
10321
|
+
path: "/admin/allowed-domains",
|
|
10322
|
+
method: "POST",
|
|
10323
|
+
tags: ["admin", "allowed-domains"],
|
|
10324
|
+
auth: "user",
|
|
10325
|
+
responseKind: "json"
|
|
10326
|
+
},
|
|
10327
|
+
adminDeleteAllowedDomain: {
|
|
10328
|
+
path: "/admin/allowed-domains/{domainId}",
|
|
10329
|
+
method: "DELETE",
|
|
10330
|
+
tags: ["admin", "allowed-domains"],
|
|
10331
|
+
auth: "user",
|
|
10332
|
+
responseKind: "json"
|
|
10333
|
+
},
|
|
10334
|
+
authAcceptInvite: {
|
|
10335
|
+
path: "/auth/invite/accept",
|
|
10336
|
+
method: "POST",
|
|
10337
|
+
tags: ["auth", "invite"],
|
|
10338
|
+
auth: "user",
|
|
10339
|
+
responseKind: "json"
|
|
10340
|
+
},
|
|
10341
|
+
acceptSiteInvitation: {
|
|
10342
|
+
path: "/sites/invitations/accept",
|
|
10343
|
+
method: "POST",
|
|
10344
|
+
tags: ["site-invitations"],
|
|
10345
|
+
auth: "user",
|
|
10346
|
+
responseKind: "json"
|
|
10347
|
+
},
|
|
10348
|
+
getNavigationMenus: {
|
|
10349
|
+
path: "/sites/{siteId}/navigation/menus",
|
|
10350
|
+
method: "GET",
|
|
10351
|
+
tags: ["site-{siteId}", "navigation"],
|
|
10352
|
+
auth: "user",
|
|
10353
|
+
responseKind: "json"
|
|
10354
|
+
},
|
|
10355
|
+
createNavigationMenu: {
|
|
10356
|
+
path: "/sites/{siteId}/navigation/menus",
|
|
10357
|
+
method: "POST",
|
|
10358
|
+
tags: ["site-{siteId}", "navigation"],
|
|
10359
|
+
auth: "user",
|
|
10360
|
+
responseKind: "json"
|
|
10361
|
+
},
|
|
10362
|
+
updateNavigationMenu: {
|
|
10363
|
+
path: "/sites/{siteId}/navigation/menus/{menuId}",
|
|
10364
|
+
method: "PATCH",
|
|
10365
|
+
tags: ["site-{siteId}", "navigation", "navigation-menu-{menuId}"],
|
|
10366
|
+
auth: "user",
|
|
10367
|
+
responseKind: "json"
|
|
10368
|
+
},
|
|
10369
|
+
deleteNavigationMenu: {
|
|
10370
|
+
path: "/sites/{siteId}/navigation/menus/{menuId}",
|
|
10371
|
+
method: "DELETE",
|
|
10372
|
+
tags: ["site-{siteId}", "navigation", "navigation-menu-{menuId}"],
|
|
10373
|
+
auth: "user",
|
|
10374
|
+
responseKind: "json"
|
|
10375
|
+
},
|
|
10376
|
+
createNavigationItem: {
|
|
10377
|
+
path: "/sites/{siteId}/navigation/menus/{menuId}/items",
|
|
10378
|
+
method: "POST",
|
|
10379
|
+
tags: ["site-{siteId}", "navigation", "navigation-menu-{menuId}"],
|
|
10380
|
+
auth: "user",
|
|
10381
|
+
responseKind: "json"
|
|
10382
|
+
},
|
|
10383
|
+
updateNavigationItem: {
|
|
10384
|
+
path: "/sites/{siteId}/navigation/menus/{menuId}/items/{itemId}",
|
|
10385
|
+
method: "PATCH",
|
|
10386
|
+
tags: ["site-{siteId}", "navigation", "navigation-menu-{menuId}", "navigation-item-{itemId}"],
|
|
10387
|
+
auth: "user",
|
|
10388
|
+
responseKind: "json"
|
|
10389
|
+
},
|
|
10390
|
+
deleteNavigationItem: {
|
|
10391
|
+
path: "/sites/{siteId}/navigation/menus/{menuId}/items/{itemId}",
|
|
10392
|
+
method: "DELETE",
|
|
10393
|
+
tags: ["site-{siteId}", "navigation", "navigation-menu-{menuId}", "navigation-item-{itemId}"],
|
|
10394
|
+
auth: "user",
|
|
10395
|
+
responseKind: "json"
|
|
10396
|
+
},
|
|
10397
|
+
reorderNavigationItems: {
|
|
10398
|
+
path: "/sites/{siteId}/navigation/menus/{menuId}/items/reorder",
|
|
10399
|
+
method: "POST",
|
|
10400
|
+
tags: ["site-{siteId}", "navigation", "navigation-menu-{menuId}"],
|
|
10401
|
+
auth: "user",
|
|
10402
|
+
responseKind: "json"
|
|
10403
|
+
},
|
|
10404
|
+
getRoutableContent: {
|
|
10405
|
+
path: "/sites/{siteId}/routable-content",
|
|
10406
|
+
method: "GET",
|
|
10407
|
+
revalidate: 60,
|
|
10408
|
+
tags: ["site-{siteId}", "routable-content-{siteId}"],
|
|
10409
|
+
auth: "user",
|
|
10410
|
+
responseKind: "json"
|
|
10411
|
+
},
|
|
10412
|
+
// Public routable content for SDK SSG
|
|
10413
|
+
getPublicRoutableContent: {
|
|
10414
|
+
path: "/public/sites/{siteId}/routable-content",
|
|
10415
|
+
method: "GET",
|
|
10416
|
+
revalidate: 60,
|
|
10417
|
+
tags: ["site-{siteId}", "routable-content-{siteId}"],
|
|
10418
|
+
auth: "public",
|
|
10419
|
+
responseKind: "json"
|
|
10420
|
+
},
|
|
10421
|
+
// Generic public content preview (preferred)
|
|
10422
|
+
getPublishedEntryPreview: {
|
|
10423
|
+
path: "/public/content/{siteId}/{type}/{slug}/preview",
|
|
10424
|
+
method: "GET",
|
|
10425
|
+
revalidate: 60,
|
|
10426
|
+
tags: ["content-{siteId}-{type}-{slug}"],
|
|
10427
|
+
auth: "public",
|
|
10428
|
+
responseKind: "json"
|
|
10429
|
+
},
|
|
10430
|
+
listPublishedEntries: {
|
|
10431
|
+
path: "/public/content/{siteId}/{type}/entries",
|
|
10432
|
+
method: "GET",
|
|
10433
|
+
revalidate: 60,
|
|
10434
|
+
tags: ["content-{siteId}-{type}"],
|
|
10435
|
+
auth: "public",
|
|
10436
|
+
responseKind: "json"
|
|
10437
|
+
},
|
|
10438
|
+
getPublishedPostPreview: {
|
|
10439
|
+
path: "/public/posts/{siteId}/{slug}/preview",
|
|
10440
|
+
method: "GET",
|
|
10441
|
+
revalidate: 60,
|
|
10442
|
+
tags: ["blog-post:{siteId}:{slug}"],
|
|
10443
|
+
auth: "public",
|
|
10444
|
+
responseKind: "json"
|
|
10445
|
+
},
|
|
10446
|
+
proposalsSelect: {
|
|
10447
|
+
path: "/proposals/select",
|
|
10448
|
+
method: "POST",
|
|
10449
|
+
tags: ["proposal", "site"],
|
|
10450
|
+
auth: "user",
|
|
10451
|
+
responseKind: "json"
|
|
10452
|
+
},
|
|
10453
|
+
createTheme: {
|
|
10454
|
+
path: "/ai/chat/create-theme",
|
|
10455
|
+
method: "POST",
|
|
10456
|
+
revalidate: 60,
|
|
10457
|
+
// 1 minute cache for theme creation to avoid duplicate requests
|
|
10458
|
+
tags: ["theme", "ai-theme"],
|
|
10459
|
+
auth: "user",
|
|
10460
|
+
responseKind: "json"
|
|
10461
|
+
},
|
|
10462
|
+
generateThemes: {
|
|
10463
|
+
path: "/theme/generate",
|
|
10464
|
+
method: "POST",
|
|
10465
|
+
tags: ["theme"],
|
|
10466
|
+
auth: "user",
|
|
10467
|
+
responseKind: "json"
|
|
10468
|
+
},
|
|
10469
|
+
extractThemeFromInspiration: {
|
|
10470
|
+
path: "/theme/extract-from-inspiration",
|
|
10471
|
+
method: "POST",
|
|
10472
|
+
tags: ["theme", "preferences"],
|
|
10473
|
+
auth: "user",
|
|
10474
|
+
responseKind: "json"
|
|
10475
|
+
},
|
|
10476
|
+
saveSiteTheme: {
|
|
10477
|
+
path: "/sites/{siteId}/theme/save",
|
|
10478
|
+
method: "POST",
|
|
10479
|
+
tags: ["site-{siteId}", "theme"],
|
|
10480
|
+
auth: "user",
|
|
10481
|
+
responseKind: "json"
|
|
10482
|
+
},
|
|
10483
|
+
uploadSiteLogo: {
|
|
10484
|
+
path: "/storage/upload-site-logo",
|
|
10485
|
+
method: "POST",
|
|
10486
|
+
tags: ["site", "logo"],
|
|
10487
|
+
auth: "user",
|
|
10488
|
+
responseKind: "json"
|
|
10489
|
+
},
|
|
10490
|
+
upsertThemePreferences: {
|
|
10491
|
+
path: "/theme-preferences/upsert",
|
|
10492
|
+
method: "POST",
|
|
10493
|
+
tags: ["theme", "preferences"],
|
|
10494
|
+
auth: "user",
|
|
10495
|
+
responseKind: "json"
|
|
10496
|
+
},
|
|
10497
|
+
finalizeSite: {
|
|
10498
|
+
path: "/sites/finalize",
|
|
10499
|
+
method: "POST",
|
|
10500
|
+
tags: ["site"],
|
|
10501
|
+
auth: "user",
|
|
10502
|
+
responseKind: "json"
|
|
10503
|
+
},
|
|
10504
|
+
instagramUploadZip: {
|
|
10505
|
+
path: "/api/instagram/upload-zip",
|
|
10506
|
+
method: "POST",
|
|
10507
|
+
tags: ["instagram-import"],
|
|
10508
|
+
auth: "user",
|
|
10509
|
+
responseKind: "json"
|
|
10510
|
+
},
|
|
10511
|
+
getAnalyticsReport: {
|
|
10512
|
+
path: "/sites/{siteId}/analytics/report",
|
|
10513
|
+
method: "GET",
|
|
10514
|
+
revalidate: 300,
|
|
10515
|
+
tags: ["site-{siteId}", "analytics-{siteId}"],
|
|
10516
|
+
auth: "user",
|
|
10517
|
+
responseKind: "json"
|
|
10518
|
+
},
|
|
10519
|
+
getSeoOverview: {
|
|
10520
|
+
path: "/sites/{siteId}/seo/overview",
|
|
10521
|
+
method: "GET",
|
|
10522
|
+
revalidate: 300,
|
|
10523
|
+
tags: ["site-{siteId}", "seo-overview-{siteId}"],
|
|
10524
|
+
auth: "user",
|
|
10525
|
+
responseKind: "json"
|
|
10526
|
+
},
|
|
10527
|
+
getSeoPages: {
|
|
10528
|
+
path: "/sites/{siteId}/seo/pages",
|
|
10529
|
+
method: "GET",
|
|
10530
|
+
revalidate: 300,
|
|
10531
|
+
tags: ["site-{siteId}", "seo-pages-{siteId}"],
|
|
10532
|
+
auth: "user",
|
|
10533
|
+
responseKind: "json"
|
|
10534
|
+
},
|
|
10535
|
+
getSeoQueries: {
|
|
10536
|
+
path: "/sites/{siteId}/seo/queries",
|
|
10537
|
+
method: "GET",
|
|
10538
|
+
revalidate: 300,
|
|
10539
|
+
tags: ["site-{siteId}", "seo-queries-{siteId}"],
|
|
10540
|
+
auth: "user",
|
|
10541
|
+
responseKind: "json"
|
|
10542
|
+
},
|
|
10543
|
+
getPerformanceOverview: {
|
|
10544
|
+
path: "/sites/{siteId}/performance/overview",
|
|
10545
|
+
method: "GET",
|
|
10546
|
+
revalidate: 300,
|
|
10547
|
+
tags: ["site-{siteId}", "performance-overview-{siteId}"],
|
|
10548
|
+
auth: "user",
|
|
10549
|
+
responseKind: "json"
|
|
10550
|
+
},
|
|
10551
|
+
createMediaAsset: {
|
|
10552
|
+
path: "/media",
|
|
10553
|
+
method: "POST",
|
|
10554
|
+
tags: ["media"],
|
|
10555
|
+
auth: "user",
|
|
10556
|
+
responseKind: "json"
|
|
10557
|
+
},
|
|
10558
|
+
mediaList: {
|
|
10559
|
+
path: "/media",
|
|
10560
|
+
method: "GET",
|
|
10561
|
+
tags: ["media"],
|
|
10562
|
+
auth: "user",
|
|
10563
|
+
responseKind: "json"
|
|
10564
|
+
},
|
|
10565
|
+
mediaGet: {
|
|
10566
|
+
path: "/media/{assetId}",
|
|
10567
|
+
method: "GET",
|
|
10568
|
+
tags: ["media", "media-{assetId}"],
|
|
10569
|
+
auth: "user",
|
|
10570
|
+
responseKind: "json"
|
|
10571
|
+
},
|
|
10572
|
+
mediaUpdate: {
|
|
10573
|
+
path: "/media/{assetId}",
|
|
10574
|
+
method: "PATCH",
|
|
10575
|
+
tags: ["media", "media-{assetId}"],
|
|
10576
|
+
auth: "user",
|
|
10577
|
+
responseKind: "json"
|
|
10578
|
+
},
|
|
10579
|
+
mediaDelete: {
|
|
10580
|
+
path: "/media/{assetId}",
|
|
10581
|
+
method: "DELETE",
|
|
10582
|
+
tags: ["media", "media-{assetId}"],
|
|
10583
|
+
auth: "user",
|
|
10584
|
+
responseKind: "void"
|
|
10585
|
+
},
|
|
10586
|
+
mediaBulkDelete: {
|
|
10587
|
+
path: "/media/bulk-delete",
|
|
10588
|
+
method: "POST",
|
|
10589
|
+
tags: ["media"],
|
|
10590
|
+
auth: "user",
|
|
10591
|
+
responseKind: "json"
|
|
10592
|
+
},
|
|
10593
|
+
mediaGetSignedUrl: {
|
|
10594
|
+
path: "/media/{assetId}/signed-url",
|
|
10595
|
+
method: "GET",
|
|
10596
|
+
tags: ["media", "media-{assetId}"],
|
|
10597
|
+
auth: "user",
|
|
10598
|
+
responseKind: "json"
|
|
10599
|
+
},
|
|
10600
|
+
mediaUpload: {
|
|
10601
|
+
path: "/media/upload",
|
|
10602
|
+
method: "POST",
|
|
10603
|
+
tags: ["media"],
|
|
10604
|
+
auth: "user",
|
|
10605
|
+
responseKind: "json"
|
|
10606
|
+
},
|
|
10607
|
+
getMediaLabels: {
|
|
10608
|
+
path: "/media/labels",
|
|
10609
|
+
method: "GET",
|
|
10610
|
+
tags: ["media", "media-labels"],
|
|
10611
|
+
auth: "user",
|
|
10612
|
+
responseKind: "json"
|
|
10613
|
+
},
|
|
10614
|
+
getMediaSettings: {
|
|
10615
|
+
path: "/media/settings",
|
|
10616
|
+
method: "GET",
|
|
10617
|
+
tags: ["media-settings"],
|
|
10618
|
+
auth: "user",
|
|
10619
|
+
responseKind: "json"
|
|
10620
|
+
},
|
|
10621
|
+
updateMediaSettings: {
|
|
10622
|
+
path: "/media/settings",
|
|
10623
|
+
method: "POST",
|
|
10624
|
+
tags: ["media-settings"],
|
|
10625
|
+
auth: "user",
|
|
10626
|
+
responseKind: "json"
|
|
10627
|
+
},
|
|
10628
|
+
// Media endpoints
|
|
10629
|
+
mediaSearch: {
|
|
10630
|
+
path: "/media/search",
|
|
10631
|
+
method: "POST",
|
|
10632
|
+
tags: ["media-search"],
|
|
10633
|
+
auth: "user",
|
|
10634
|
+
responseKind: "json"
|
|
10635
|
+
},
|
|
10636
|
+
classifyMediaAsset: {
|
|
10637
|
+
path: "/media/{assetId}/classify",
|
|
10638
|
+
method: "POST",
|
|
10639
|
+
tags: ["media", "media-{assetId}"],
|
|
10640
|
+
auth: "user",
|
|
10641
|
+
responseKind: "json"
|
|
10642
|
+
},
|
|
10643
|
+
classifyMediaBatch: {
|
|
10644
|
+
path: "/media/classify",
|
|
10645
|
+
method: "POST",
|
|
10646
|
+
tags: ["media"],
|
|
10647
|
+
auth: "user",
|
|
10648
|
+
responseKind: "json"
|
|
10649
|
+
},
|
|
10650
|
+
mediaJobsStatus: {
|
|
10651
|
+
path: "/media/jobs/status",
|
|
10652
|
+
method: "GET",
|
|
10653
|
+
tags: ["media"],
|
|
10654
|
+
auth: "user",
|
|
10655
|
+
responseKind: "json"
|
|
10656
|
+
},
|
|
10657
|
+
enqueueMediaClassifyJob: {
|
|
10658
|
+
path: "/media/jobs",
|
|
10659
|
+
method: "POST",
|
|
10660
|
+
tags: ["media"],
|
|
10661
|
+
auth: "user",
|
|
10662
|
+
responseKind: "json"
|
|
10663
|
+
},
|
|
10664
|
+
runMediaClassifyJob: {
|
|
10665
|
+
path: "/media/jobs/run",
|
|
10666
|
+
method: "POST",
|
|
10667
|
+
tags: ["media"],
|
|
10668
|
+
auth: "user",
|
|
10669
|
+
responseKind: "json"
|
|
10670
|
+
},
|
|
10671
|
+
runAllMediaClassifyJobs: {
|
|
10672
|
+
path: "/media/jobs/run-all",
|
|
10673
|
+
method: "POST",
|
|
10674
|
+
tags: ["media"],
|
|
10675
|
+
auth: "user",
|
|
10676
|
+
responseKind: "json"
|
|
10677
|
+
},
|
|
10678
|
+
initSiteContent: {
|
|
10679
|
+
path: "/sites/{siteId}/content/init",
|
|
10680
|
+
method: "POST",
|
|
10681
|
+
tags: ["site", "content"],
|
|
10682
|
+
auth: "user",
|
|
10683
|
+
responseKind: "json"
|
|
10684
|
+
},
|
|
10685
|
+
updateBlockContent: {
|
|
10686
|
+
path: "/blocks/{blockId}/content",
|
|
10687
|
+
method: "POST",
|
|
10688
|
+
tags: ["block", "block-{blockId}", "content"],
|
|
10689
|
+
auth: "user",
|
|
10690
|
+
responseKind: "json"
|
|
10691
|
+
},
|
|
10692
|
+
saveBlockContent: {
|
|
10693
|
+
path: "/blocks/{blockId}/content",
|
|
10694
|
+
method: "POST",
|
|
10695
|
+
tags: ["block", "block-{blockId}", "content"],
|
|
10696
|
+
auth: "user",
|
|
10697
|
+
responseKind: "json"
|
|
10698
|
+
},
|
|
10699
|
+
createBlock: {
|
|
10700
|
+
path: "/sites/{siteId}/pages/{pageId}/blocks",
|
|
10701
|
+
method: "POST",
|
|
10702
|
+
tags: ["site-{siteId}", "page-{pageId}", "blocks"],
|
|
10703
|
+
auth: "user",
|
|
10704
|
+
responseKind: "json"
|
|
10705
|
+
},
|
|
10706
|
+
reorderBlocks: {
|
|
10707
|
+
path: "/sites/{siteId}/pages/{pageId}/blocks/reorder",
|
|
10708
|
+
method: "POST",
|
|
10709
|
+
tags: ["site-{siteId}", "page-{pageId}", "blocks"],
|
|
10710
|
+
auth: "user",
|
|
10711
|
+
responseKind: "json"
|
|
10712
|
+
},
|
|
10713
|
+
deleteBlock: {
|
|
10714
|
+
path: "/sites/{siteId}/pages/{pageId}/blocks/{blockId}",
|
|
10715
|
+
method: "DELETE",
|
|
10716
|
+
tags: ["site-{siteId}", "page-{pageId}", "blocks"],
|
|
10717
|
+
auth: "user",
|
|
10718
|
+
responseKind: "json"
|
|
10719
|
+
},
|
|
10720
|
+
listBlocks: {
|
|
10721
|
+
path: "/sites/{siteId}/pages/{pageId}/blocks",
|
|
10722
|
+
method: "GET",
|
|
10723
|
+
tags: ["site-{siteId}", "page-{pageId}", "blocks"],
|
|
10724
|
+
auth: "user",
|
|
10725
|
+
responseKind: "json"
|
|
10726
|
+
},
|
|
10727
|
+
getBlockContent: {
|
|
10728
|
+
path: "/blocks/{blockId}/content",
|
|
10729
|
+
method: "GET",
|
|
10730
|
+
tags: ["block-{blockId}", "content"],
|
|
10731
|
+
auth: "user",
|
|
10732
|
+
responseKind: "json"
|
|
10733
|
+
},
|
|
10734
|
+
// DEPRECATED: Use getSite with ?id={id} instead
|
|
10735
|
+
getSiteById: {
|
|
10736
|
+
path: "/sites/by-id/{id}",
|
|
10737
|
+
method: "GET",
|
|
10738
|
+
revalidate: 900,
|
|
10739
|
+
// 15 minutes
|
|
10740
|
+
tags: ["site-{id}"],
|
|
10741
|
+
auth: "user",
|
|
10742
|
+
responseKind: "json"
|
|
10743
|
+
},
|
|
10744
|
+
// Page data endpoints
|
|
10745
|
+
getPageById: {
|
|
10746
|
+
path: "/sites/{siteId}/pages/{pageId}",
|
|
10747
|
+
method: "GET",
|
|
10748
|
+
revalidate: 900,
|
|
10749
|
+
// 15 minutes
|
|
10750
|
+
tags: ["site-{siteId}", "page-{pageId}"],
|
|
10751
|
+
auth: "user",
|
|
10752
|
+
responseKind: "json"
|
|
10753
|
+
},
|
|
10754
|
+
getContentByPath: {
|
|
10755
|
+
path: "/sites/{siteId}/pages",
|
|
10756
|
+
method: "GET",
|
|
10757
|
+
tags: ["site-{siteId}", "routable-content-{siteId}"],
|
|
10758
|
+
auth: "public",
|
|
10759
|
+
responseKind: "json"
|
|
10760
|
+
},
|
|
10761
|
+
getPageByPath: {
|
|
10762
|
+
path: "/sites/{siteId}/pages",
|
|
10763
|
+
method: "GET",
|
|
10764
|
+
tags: ["site-{siteId}"],
|
|
10765
|
+
auth: "user",
|
|
10766
|
+
responseKind: "json"
|
|
10767
|
+
},
|
|
10768
|
+
// Forms CRUD
|
|
10769
|
+
listForms: {
|
|
10770
|
+
path: "/sites/{siteId}/forms",
|
|
10771
|
+
method: "GET",
|
|
10772
|
+
tags: ["site-{siteId}", "forms"],
|
|
10773
|
+
auth: "user",
|
|
10774
|
+
responseKind: "json"
|
|
10775
|
+
},
|
|
10776
|
+
listBookingForms: {
|
|
10777
|
+
path: "/sites/{siteId}/bookings/forms",
|
|
10778
|
+
method: "GET",
|
|
10779
|
+
tags: ["site-{siteId}", "forms"],
|
|
10780
|
+
auth: "user",
|
|
10781
|
+
responseKind: "json"
|
|
10782
|
+
},
|
|
10783
|
+
createForm: {
|
|
10784
|
+
path: "/sites/{siteId}/forms",
|
|
10785
|
+
method: "POST",
|
|
10786
|
+
tags: ["site-{siteId}", "forms"],
|
|
10787
|
+
auth: "user",
|
|
10788
|
+
responseKind: "json"
|
|
10789
|
+
},
|
|
10790
|
+
updateForm: {
|
|
10791
|
+
path: "/sites/{siteId}/forms/{slug}",
|
|
10792
|
+
method: "PATCH",
|
|
10793
|
+
tags: ["site-{siteId}", "forms", "form-{slug}"],
|
|
10794
|
+
auth: "user",
|
|
10795
|
+
responseKind: "json"
|
|
10796
|
+
},
|
|
10797
|
+
deleteForm: {
|
|
10798
|
+
path: "/sites/{siteId}/forms/{slug}",
|
|
10799
|
+
method: "DELETE",
|
|
10800
|
+
tags: ["site-{siteId}", "forms", "form-{slug}"],
|
|
10801
|
+
auth: "user",
|
|
10802
|
+
responseKind: "json"
|
|
10803
|
+
},
|
|
10804
|
+
listFormSubmissions: {
|
|
10805
|
+
path: "/sites/{siteId}/forms/{slug}/submissions",
|
|
10806
|
+
method: "GET",
|
|
10807
|
+
revalidate: 30,
|
|
10808
|
+
tags: ["site-{siteId}", "form-{slug}", "form-submissions-{slug}"],
|
|
10809
|
+
auth: "user",
|
|
10810
|
+
responseKind: "json"
|
|
10811
|
+
},
|
|
10812
|
+
// Public submit
|
|
10813
|
+
submitForm: {
|
|
10814
|
+
path: "/forms/submit",
|
|
10815
|
+
method: "POST",
|
|
10816
|
+
tags: ["forms-submit"],
|
|
10817
|
+
auth: "public",
|
|
10818
|
+
responseKind: "json"
|
|
10819
|
+
},
|
|
10820
|
+
// Public forms
|
|
10821
|
+
getPublicFormById: {
|
|
10822
|
+
path: "/public/forms/{formId}",
|
|
10823
|
+
method: "GET",
|
|
10824
|
+
revalidate: 60,
|
|
10825
|
+
tags: ["form-{formId}"],
|
|
10826
|
+
auth: "public",
|
|
10827
|
+
responseKind: "json"
|
|
10828
|
+
},
|
|
10829
|
+
// Public booking services
|
|
10830
|
+
getPublicBookingServices: {
|
|
10831
|
+
path: "/public/bookings/services",
|
|
10832
|
+
method: "GET",
|
|
10833
|
+
revalidate: 60,
|
|
10834
|
+
tags: ["site-{siteId}"],
|
|
10835
|
+
auth: "public",
|
|
10836
|
+
responseKind: "json"
|
|
10837
|
+
},
|
|
10838
|
+
devToolsImpersonateAdmin: {
|
|
10839
|
+
path: "/dev-tools/impersonate/admin",
|
|
10840
|
+
method: "POST",
|
|
10841
|
+
auth: "admin",
|
|
10842
|
+
responseKind: "json"
|
|
10843
|
+
},
|
|
10844
|
+
devToolsImpersonateRandom: {
|
|
10845
|
+
path: "/dev-tools/impersonate/random",
|
|
10846
|
+
method: "POST",
|
|
10847
|
+
auth: "admin",
|
|
10848
|
+
responseKind: "json"
|
|
10849
|
+
},
|
|
10850
|
+
devToolsSeedDemoData: {
|
|
10851
|
+
path: "/dev-tools/seed",
|
|
10852
|
+
method: "POST",
|
|
10853
|
+
auth: "admin",
|
|
10854
|
+
responseKind: "json"
|
|
10855
|
+
},
|
|
10856
|
+
// Public analytics collection endpoint
|
|
10857
|
+
analyticsCollect: {
|
|
10858
|
+
path: "/api/analytics/collect",
|
|
10859
|
+
method: "POST",
|
|
10860
|
+
auth: "public",
|
|
10861
|
+
responseKind: "json"
|
|
10862
|
+
},
|
|
10863
|
+
// Public events for event calendar block
|
|
10864
|
+
listPublicEvents: {
|
|
10865
|
+
path: "/public/sites/{siteId}/events",
|
|
10866
|
+
method: "GET",
|
|
10867
|
+
revalidate: 60,
|
|
10868
|
+
tags: ["public-events-{siteId}"],
|
|
10869
|
+
auth: "public",
|
|
10870
|
+
responseKind: "json"
|
|
10871
|
+
},
|
|
10872
|
+
// Resolve event occurrence by URL segment (date or UUID)
|
|
10873
|
+
resolveEventOccurrence: {
|
|
10874
|
+
path: "/public/sites/{siteId}/events/occurrences/resolve",
|
|
10875
|
+
method: "GET",
|
|
10876
|
+
revalidate: 60,
|
|
10877
|
+
tags: ["public-events-{siteId}", "event-occurrence"],
|
|
10878
|
+
auth: "public",
|
|
10879
|
+
responseKind: "json"
|
|
10880
|
+
},
|
|
10881
|
+
// Public event registration
|
|
10882
|
+
registerForEvent: {
|
|
10883
|
+
path: "/public/sites/{siteId}/events/register",
|
|
10884
|
+
method: "POST",
|
|
10885
|
+
tags: ["public-events-{siteId}", "event-registration"],
|
|
10886
|
+
auth: "public",
|
|
10887
|
+
responseKind: "json"
|
|
10888
|
+
},
|
|
10889
|
+
// Content Types CRUD
|
|
10890
|
+
listSiteContentTypes: {
|
|
10891
|
+
path: "/sites/{siteId}/content-types",
|
|
10892
|
+
method: "GET",
|
|
10893
|
+
revalidate: 60,
|
|
10894
|
+
tags: ["site-{siteId}", "content-types-{siteId}"],
|
|
10895
|
+
auth: "user",
|
|
10896
|
+
responseKind: "json"
|
|
10897
|
+
},
|
|
10898
|
+
getContentType: {
|
|
10899
|
+
path: "/sites/{siteId}/content-types/by-id/{typeId}",
|
|
10900
|
+
method: "GET",
|
|
10901
|
+
revalidate: 60,
|
|
10902
|
+
tags: ["site-{siteId}", "content-type-{typeId}"],
|
|
10903
|
+
auth: "user",
|
|
10904
|
+
responseKind: "json"
|
|
10905
|
+
},
|
|
10906
|
+
createContentType: {
|
|
10907
|
+
path: "/sites/{siteId}/content-types",
|
|
10908
|
+
method: "POST",
|
|
10909
|
+
tags: ["site-{siteId}", "content-types-{siteId}"],
|
|
10910
|
+
auth: "user",
|
|
10911
|
+
responseKind: "json"
|
|
10912
|
+
},
|
|
10913
|
+
updateContentType: {
|
|
10914
|
+
path: "/sites/{siteId}/content-types/by-id/{typeId}",
|
|
10915
|
+
method: "PATCH",
|
|
10916
|
+
tags: ["site-{siteId}", "content-type-{typeId}", "content-types-{siteId}"],
|
|
10917
|
+
auth: "user",
|
|
10918
|
+
responseKind: "json"
|
|
10919
|
+
},
|
|
10920
|
+
deleteContentType: {
|
|
10921
|
+
path: "/sites/{siteId}/content-types/by-id/{typeId}",
|
|
10922
|
+
method: "DELETE",
|
|
10923
|
+
tags: ["site-{siteId}", "content-type-{typeId}", "content-types-{sideId}"],
|
|
10924
|
+
auth: "user",
|
|
10925
|
+
responseKind: "json"
|
|
10926
|
+
},
|
|
10927
|
+
duplicateContentType: {
|
|
10928
|
+
path: "/sites/{siteId}/content-types/by-id/{typeId}/duplicate",
|
|
10929
|
+
method: "POST",
|
|
10930
|
+
tags: ["site-{siteId}", "content-type-{typeId}", "content-types-{siteId}"],
|
|
10931
|
+
auth: "user",
|
|
10932
|
+
responseKind: "json"
|
|
10933
|
+
},
|
|
10934
|
+
// Admin site creation
|
|
10935
|
+
adminCreateSite: {
|
|
10936
|
+
path: "/admin/sites",
|
|
10937
|
+
method: "POST",
|
|
10938
|
+
tags: ["admin", "sites"],
|
|
10939
|
+
auth: "admin",
|
|
10940
|
+
responseKind: "json"
|
|
10941
|
+
},
|
|
10942
|
+
// SDK Config
|
|
10943
|
+
refreshSdkConfig: {
|
|
10944
|
+
path: "/sites/{siteId}/refresh-sdk-config",
|
|
10945
|
+
method: "POST",
|
|
10946
|
+
tags: ["site-{siteId}", "sdk-config"],
|
|
10947
|
+
auth: "user",
|
|
10948
|
+
responseKind: "json"
|
|
10949
|
+
},
|
|
10950
|
+
// Stripe Connect - Site billing
|
|
10951
|
+
stripeConnectAuthorize: {
|
|
10952
|
+
path: "/sites/{siteId}/billing/connect/authorize",
|
|
10953
|
+
method: "POST",
|
|
10954
|
+
tags: ["site-{siteId}", "stripe-connect"],
|
|
10955
|
+
auth: "user",
|
|
10956
|
+
responseKind: "json"
|
|
10957
|
+
},
|
|
10958
|
+
stripeConnectStatus: {
|
|
10959
|
+
path: "/sites/{siteId}/billing/connect/status",
|
|
10960
|
+
method: "GET",
|
|
10961
|
+
tags: ["site-{siteId}", "stripe-connect"],
|
|
10962
|
+
auth: "user",
|
|
10963
|
+
responseKind: "json"
|
|
10964
|
+
},
|
|
10965
|
+
stripeConnectDisconnect: {
|
|
10966
|
+
path: "/sites/{siteId}/billing/connect/disconnect",
|
|
10967
|
+
method: "DELETE",
|
|
10968
|
+
tags: ["site-{siteId}", "stripe-connect"],
|
|
10969
|
+
auth: "user",
|
|
10970
|
+
responseKind: "json"
|
|
10971
|
+
},
|
|
10972
|
+
// Backup
|
|
10973
|
+
importSiteBackup: {
|
|
10974
|
+
path: "/sites/{siteId}/backup/import",
|
|
10975
|
+
method: "POST",
|
|
10976
|
+
tags: ["site-{siteId}", "backup"],
|
|
10977
|
+
auth: "user",
|
|
10978
|
+
responseKind: "json"
|
|
10979
|
+
},
|
|
10980
|
+
previewBackup: {
|
|
10981
|
+
path: "/backup/preview",
|
|
10982
|
+
method: "POST",
|
|
10983
|
+
tags: ["backup"],
|
|
10984
|
+
auth: "user",
|
|
10985
|
+
responseKind: "json"
|
|
10986
|
+
},
|
|
10987
|
+
importBackupAsNewSite: {
|
|
10988
|
+
path: "/sites/backup/import-as-new",
|
|
10989
|
+
method: "POST",
|
|
10990
|
+
tags: ["site", "backup"],
|
|
10991
|
+
auth: "user",
|
|
10992
|
+
responseKind: "json"
|
|
10993
|
+
},
|
|
10994
|
+
// Admin billing price overrides
|
|
10995
|
+
adminGetPriceOverride: {
|
|
10996
|
+
path: "/admin/billing/price-override",
|
|
10997
|
+
method: "GET",
|
|
10998
|
+
tags: ["admin", "billing", "price-override"],
|
|
10999
|
+
auth: "admin",
|
|
11000
|
+
responseKind: "json"
|
|
11001
|
+
},
|
|
11002
|
+
adminUpsertPriceOverride: {
|
|
11003
|
+
path: "/admin/billing/price-override",
|
|
11004
|
+
method: "POST",
|
|
11005
|
+
tags: ["admin", "billing", "price-override"],
|
|
11006
|
+
auth: "admin",
|
|
11007
|
+
responseKind: "json"
|
|
11008
|
+
},
|
|
11009
|
+
adminDeletePriceOverride: {
|
|
11010
|
+
path: "/admin/billing/price-override",
|
|
11011
|
+
method: "DELETE",
|
|
11012
|
+
tags: ["admin", "billing", "price-override"],
|
|
11013
|
+
auth: "admin",
|
|
11014
|
+
responseKind: "json"
|
|
11015
|
+
}
|
|
11016
|
+
};
|
|
11017
|
+
var API_ENDPOINTS = ENDPOINT_DEFINITIONS;
|
|
11018
|
+
|
|
11019
|
+
// ../api/src/url.ts
|
|
11020
|
+
function getCmsApiUrl() {
|
|
11021
|
+
if (typeof window !== "undefined") {
|
|
11022
|
+
return "/api";
|
|
11023
|
+
}
|
|
11024
|
+
const internalUrl = process.env.CMS_API_URL;
|
|
11025
|
+
if (internalUrl) {
|
|
11026
|
+
return internalUrl.replace(/\/$/, "");
|
|
11027
|
+
}
|
|
11028
|
+
const dashboardUrl = process.env.NEXT_PUBLIC_DASHBOARD_URL;
|
|
11029
|
+
if (dashboardUrl) {
|
|
11030
|
+
const base = dashboardUrl.replace(/\/$/, "");
|
|
11031
|
+
return `${base}/api`;
|
|
11032
|
+
}
|
|
11033
|
+
const legacyApiUrl = process.env.NEXT_PUBLIC_CMS_API_URL;
|
|
11034
|
+
if (legacyApiUrl) {
|
|
11035
|
+
return legacyApiUrl.replace(/\/$/, "");
|
|
11036
|
+
}
|
|
11037
|
+
throw new Error(
|
|
11038
|
+
"NEXT_PUBLIC_DASHBOARD_URL is not configured. Set it to your dashboard URL (e.g., http://dashboard.local:4000)"
|
|
11039
|
+
);
|
|
11040
|
+
}
|
|
11041
|
+
function resolveApiBaseUrl() {
|
|
11042
|
+
return getCmsApiUrl();
|
|
11043
|
+
}
|
|
11044
|
+
|
|
11045
|
+
// ../api/src/request.ts
|
|
11046
|
+
var revalidateTag = null;
|
|
11047
|
+
if (typeof window === "undefined") {
|
|
11048
|
+
try {
|
|
11049
|
+
const dynamicRequire = new Function("modulePath", "return require(modulePath)");
|
|
11050
|
+
const nextCache = dynamicRequire("next/cache");
|
|
11051
|
+
revalidateTag = nextCache.revalidateTag ?? null;
|
|
11052
|
+
} catch {
|
|
11053
|
+
}
|
|
11054
|
+
}
|
|
11055
|
+
var sdkVersion;
|
|
11056
|
+
function generateRequestId() {
|
|
11057
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
11058
|
+
return crypto.randomUUID();
|
|
11059
|
+
}
|
|
11060
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
11061
|
+
const r = Math.random() * 16 | 0;
|
|
11062
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
11063
|
+
return v.toString(16);
|
|
11064
|
+
});
|
|
11065
|
+
}
|
|
11066
|
+
function setSdkVersion(version) {
|
|
11067
|
+
sdkVersion = version;
|
|
11068
|
+
}
|
|
11069
|
+
var ApiRequestError = class extends Error {
|
|
11070
|
+
constructor(message, options) {
|
|
11071
|
+
super(message);
|
|
11072
|
+
this.name = "ApiRequestError";
|
|
11073
|
+
this.endpoint = options.endpoint;
|
|
11074
|
+
this.status = options.status;
|
|
11075
|
+
this.method = options.method;
|
|
11076
|
+
this.auth = options.auth;
|
|
11077
|
+
this.requestId = options.requestId;
|
|
11078
|
+
this.body = options.body;
|
|
11079
|
+
this.cause = options.cause;
|
|
11080
|
+
this.errorCode = options.errorCode;
|
|
11081
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
11082
|
+
}
|
|
11083
|
+
};
|
|
11084
|
+
function parseRetryAfterHeader(headerValue) {
|
|
11085
|
+
if (!headerValue) return void 0;
|
|
11086
|
+
if (/^\d+$/.test(headerValue)) {
|
|
11087
|
+
const seconds = parseInt(headerValue, 10);
|
|
11088
|
+
return seconds * 1e3;
|
|
11089
|
+
}
|
|
11090
|
+
const date = new Date(headerValue);
|
|
11091
|
+
if (!isNaN(date.getTime())) {
|
|
11092
|
+
const delayMs = date.getTime() - Date.now();
|
|
11093
|
+
return delayMs > 0 ? delayMs : void 0;
|
|
11094
|
+
}
|
|
11095
|
+
return void 0;
|
|
11096
|
+
}
|
|
11097
|
+
function buildEndpointURL(baseURL, endpoint) {
|
|
11098
|
+
return baseURL + API_ENDPOINTS[endpoint].path;
|
|
11099
|
+
}
|
|
11100
|
+
function invalidateCacheTags(tags, params) {
|
|
11101
|
+
if (typeof window !== "undefined" || !revalidateTag || !tags) return;
|
|
11102
|
+
tags.forEach((tag) => {
|
|
11103
|
+
let processedTag = tag;
|
|
11104
|
+
if (params) {
|
|
11105
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
11106
|
+
processedTag = processedTag.replace(`{${key}}`, String(value));
|
|
11107
|
+
});
|
|
11108
|
+
}
|
|
11109
|
+
try {
|
|
11110
|
+
revalidateTag(processedTag, "max");
|
|
11111
|
+
} catch {
|
|
11112
|
+
}
|
|
11113
|
+
});
|
|
11114
|
+
}
|
|
11115
|
+
async function parseErrorBody(response) {
|
|
11116
|
+
const clone = response.clone();
|
|
11117
|
+
const contentType = clone.headers.get("content-type") ?? "";
|
|
11118
|
+
if (contentType.includes("application/json")) {
|
|
11119
|
+
try {
|
|
11120
|
+
return await clone.json();
|
|
11121
|
+
} catch {
|
|
11122
|
+
}
|
|
11123
|
+
}
|
|
11124
|
+
try {
|
|
11125
|
+
const text2 = await clone.text();
|
|
11126
|
+
return text2.length ? text2 : null;
|
|
11127
|
+
} catch {
|
|
11128
|
+
return null;
|
|
11129
|
+
}
|
|
11130
|
+
}
|
|
11131
|
+
function buildSuccessEnvelope(data, requestId) {
|
|
11132
|
+
return {
|
|
11133
|
+
success: true,
|
|
11134
|
+
data,
|
|
11135
|
+
meta: {
|
|
11136
|
+
requestId: requestId ?? generateRequestId(),
|
|
11137
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11138
|
+
apiVersion: "2025-01-01"
|
|
11139
|
+
}
|
|
11140
|
+
};
|
|
11141
|
+
}
|
|
11142
|
+
function buildErrorEnvelope(code, message, status, requestId) {
|
|
11143
|
+
return {
|
|
11144
|
+
success: false,
|
|
11145
|
+
error: {
|
|
11146
|
+
code,
|
|
11147
|
+
message,
|
|
11148
|
+
requestId: requestId ?? generateRequestId(),
|
|
11149
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11150
|
+
status
|
|
11151
|
+
}
|
|
11152
|
+
};
|
|
11153
|
+
}
|
|
11154
|
+
async function parseSuccessResponse(endpoint, response, config3) {
|
|
11155
|
+
const responseKind = config3.responseKind ?? "json";
|
|
11156
|
+
const auth = config3.auth ?? "user";
|
|
11157
|
+
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
11158
|
+
switch (responseKind) {
|
|
11159
|
+
case "json": {
|
|
11160
|
+
if (response.status === 204 || response.status === 205 || response.status === 304) {
|
|
11161
|
+
return buildSuccessEnvelope(void 0, requestId);
|
|
11162
|
+
}
|
|
11163
|
+
const raw = await response.text();
|
|
11164
|
+
if (!raw.trim()) {
|
|
11165
|
+
return buildSuccessEnvelope(void 0, requestId);
|
|
11166
|
+
}
|
|
11167
|
+
let parsed;
|
|
11168
|
+
try {
|
|
11169
|
+
parsed = JSON.parse(raw);
|
|
11170
|
+
} catch (cause) {
|
|
11171
|
+
throw new ApiRequestError(
|
|
11172
|
+
`Failed to parse JSON response for endpoint ${String(endpoint)}`,
|
|
11173
|
+
{
|
|
11174
|
+
endpoint,
|
|
11175
|
+
status: response.status,
|
|
11176
|
+
method: config3.method,
|
|
11177
|
+
auth,
|
|
11178
|
+
requestId,
|
|
11179
|
+
body: raw,
|
|
11180
|
+
cause
|
|
11181
|
+
}
|
|
11182
|
+
);
|
|
11183
|
+
}
|
|
11184
|
+
if (parsed && typeof parsed === "object" && "success" in parsed && typeof parsed.success === "boolean") {
|
|
11185
|
+
return parsed;
|
|
11186
|
+
}
|
|
11187
|
+
return buildSuccessEnvelope(parsed, requestId);
|
|
11188
|
+
}
|
|
11189
|
+
case "text": {
|
|
11190
|
+
const text2 = await response.text();
|
|
11191
|
+
return buildSuccessEnvelope(text2, requestId);
|
|
11192
|
+
}
|
|
11193
|
+
case "stream": {
|
|
11194
|
+
const body = response.body;
|
|
11195
|
+
if (!body) {
|
|
11196
|
+
return buildErrorEnvelope(
|
|
11197
|
+
"server:internal_error",
|
|
11198
|
+
`Expected a streamed body for endpoint ${String(endpoint)}`,
|
|
11199
|
+
response.status,
|
|
11200
|
+
requestId
|
|
11201
|
+
);
|
|
11202
|
+
}
|
|
11203
|
+
const stream = body;
|
|
11204
|
+
return buildSuccessEnvelope(stream, requestId);
|
|
11205
|
+
}
|
|
11206
|
+
case "void": {
|
|
11207
|
+
return buildSuccessEnvelope(void 0, requestId);
|
|
11208
|
+
}
|
|
11209
|
+
default: {
|
|
11210
|
+
return buildSuccessEnvelope(void 0, requestId);
|
|
11211
|
+
}
|
|
11212
|
+
}
|
|
11213
|
+
}
|
|
11214
|
+
function createRawCMSClient(headers = {}, baseUrl) {
|
|
11215
|
+
return ({
|
|
11216
|
+
endpoint,
|
|
11217
|
+
body,
|
|
11218
|
+
params,
|
|
11219
|
+
options = {}
|
|
11220
|
+
}) => {
|
|
11221
|
+
const resolvedBaseUrl = baseUrl ?? resolveApiBaseUrl();
|
|
11222
|
+
let url = buildEndpointURL(resolvedBaseUrl, endpoint);
|
|
11223
|
+
const originalPath = API_ENDPOINTS[endpoint].path;
|
|
11224
|
+
const unusedParams = {};
|
|
11225
|
+
if (params) {
|
|
11226
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
11227
|
+
const placeholder = `{${key}}`;
|
|
11228
|
+
if (originalPath.includes(placeholder)) {
|
|
11229
|
+
url = url.replace(placeholder, value);
|
|
11230
|
+
} else {
|
|
11231
|
+
unusedParams[key] = value;
|
|
11232
|
+
}
|
|
11233
|
+
});
|
|
11234
|
+
}
|
|
11235
|
+
const endpointConfig = API_ENDPOINTS[endpoint];
|
|
11236
|
+
const method = endpointConfig.method;
|
|
11237
|
+
const isGetOrHead = method === "GET" || method === "HEAD";
|
|
11238
|
+
if (isGetOrHead) {
|
|
11239
|
+
const queryParams = new URLSearchParams();
|
|
11240
|
+
Object.entries(unusedParams).forEach(([key, value]) => {
|
|
11241
|
+
if (value !== void 0 && value !== null) {
|
|
11242
|
+
queryParams.append(key, String(value));
|
|
11243
|
+
}
|
|
11244
|
+
});
|
|
11245
|
+
if (body) {
|
|
11246
|
+
Object.entries(body).forEach(([key, value]) => {
|
|
11247
|
+
if (value !== void 0 && value !== null) {
|
|
11248
|
+
queryParams.append(key, String(value));
|
|
11249
|
+
}
|
|
11250
|
+
});
|
|
11251
|
+
}
|
|
11252
|
+
const queryString = queryParams.toString();
|
|
11253
|
+
if (queryString) {
|
|
11254
|
+
url += (url.includes("?") ? "&" : "?") + queryString;
|
|
11255
|
+
}
|
|
11256
|
+
}
|
|
11257
|
+
const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
|
|
11258
|
+
const nextOptions = {};
|
|
11259
|
+
if (isGetOrHead && "revalidate" in endpointConfig && typeof endpointConfig.revalidate === "number") {
|
|
11260
|
+
nextOptions.revalidate = endpointConfig.revalidate;
|
|
11261
|
+
}
|
|
11262
|
+
if ("tags" in endpointConfig && Array.isArray(endpointConfig.tags)) {
|
|
11263
|
+
nextOptions.tags = endpointConfig.tags.map((tag) => {
|
|
11264
|
+
let processedTag = tag;
|
|
11265
|
+
if (params) {
|
|
11266
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
11267
|
+
processedTag = processedTag.replace(`{${key}}`, String(value));
|
|
11268
|
+
});
|
|
11269
|
+
}
|
|
11270
|
+
return processedTag;
|
|
11271
|
+
});
|
|
11272
|
+
}
|
|
11273
|
+
const requestInit = {
|
|
11274
|
+
method,
|
|
11275
|
+
...options,
|
|
11276
|
+
// Include credentials for same-origin requests (sends cookies for auth)
|
|
11277
|
+
credentials: "same-origin",
|
|
11278
|
+
// Don't include body for GET/HEAD requests
|
|
11279
|
+
body: isGetOrHead ? void 0 : isFormData ? body : body ? JSON.stringify(body) : void 0,
|
|
11280
|
+
headers: {
|
|
11281
|
+
...options.headers,
|
|
11282
|
+
...headers,
|
|
11283
|
+
// Include SDK version if set
|
|
11284
|
+
...sdkVersion && { "x-sdk-version": sdkVersion },
|
|
11285
|
+
// Don't set Content-Type for GET/HEAD requests without body
|
|
11286
|
+
...isGetOrHead ? {} : isFormData ? {} : { "Content-Type": "application/json" }
|
|
11287
|
+
},
|
|
11288
|
+
// Add Next.js caching options
|
|
11289
|
+
next: Object.keys(nextOptions).length > 0 ? nextOptions : void 0
|
|
11290
|
+
};
|
|
11291
|
+
const fetchPromise = fetch(url, requestInit);
|
|
11292
|
+
if (!isGetOrHead && "tags" in endpointConfig && Array.isArray(endpointConfig.tags)) {
|
|
11293
|
+
const tags = endpointConfig.tags.map((tag) => tag);
|
|
11294
|
+
return fetchPromise.then((response) => {
|
|
11295
|
+
if (response.ok) {
|
|
11296
|
+
invalidateCacheTags(tags, params);
|
|
11297
|
+
}
|
|
11298
|
+
return response;
|
|
11299
|
+
});
|
|
11300
|
+
}
|
|
11301
|
+
return fetchPromise;
|
|
11302
|
+
};
|
|
11303
|
+
}
|
|
11304
|
+
function createParsedClient(rawClient) {
|
|
11305
|
+
return async (params) => {
|
|
11306
|
+
const response = await rawClient(params);
|
|
11307
|
+
const endpoint = params.endpoint;
|
|
11308
|
+
const config3 = API_ENDPOINTS[endpoint];
|
|
11309
|
+
const auth = config3.auth ?? "user";
|
|
11310
|
+
if (!response.ok) {
|
|
11311
|
+
const body = await parseErrorBody(response);
|
|
11312
|
+
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
11313
|
+
const retryAfterMs = parseRetryAfterHeader(response.headers.get("retry-after"));
|
|
11314
|
+
throw new ApiRequestError(
|
|
11315
|
+
`Request to ${String(endpoint)} failed with status ${response.status}`,
|
|
11316
|
+
{
|
|
11317
|
+
endpoint,
|
|
11318
|
+
status: response.status,
|
|
11319
|
+
method: config3.method,
|
|
11320
|
+
auth,
|
|
11321
|
+
requestId,
|
|
11322
|
+
body,
|
|
11323
|
+
retryAfterMs
|
|
11324
|
+
}
|
|
11325
|
+
);
|
|
11326
|
+
}
|
|
11327
|
+
return parseSuccessResponse(endpoint, response, config3);
|
|
11328
|
+
};
|
|
11329
|
+
}
|
|
11330
|
+
function createCMSClient(headers = {}, baseUrl) {
|
|
11331
|
+
return createParsedClient(createRawCMSClient(headers, baseUrl));
|
|
11332
|
+
}
|
|
11333
|
+
function createBearerAPIClient(token, baseUrl) {
|
|
11334
|
+
const authHeaders = {
|
|
11335
|
+
Authorization: `Bearer ${token}`
|
|
11336
|
+
};
|
|
11337
|
+
return createCMSClient(authHeaders, baseUrl);
|
|
11338
|
+
}
|
|
11339
|
+
|
|
11340
|
+
// ../api/src/common/envelope.ts
|
|
11341
|
+
function isApiError(result) {
|
|
11342
|
+
return result.success === false;
|
|
11343
|
+
}
|
|
11344
|
+
function isApiSuccess(result) {
|
|
11345
|
+
return result.success === true;
|
|
11346
|
+
}
|
|
11347
|
+
var ApiEnvelopeError = class extends Error {
|
|
11348
|
+
constructor(error) {
|
|
11349
|
+
super(error.message);
|
|
11350
|
+
this.name = "ApiEnvelopeError";
|
|
11351
|
+
this.code = error.code;
|
|
11352
|
+
this.requestId = error.requestId;
|
|
11353
|
+
this.timestamp = error.timestamp;
|
|
11354
|
+
this.status = error.status;
|
|
11355
|
+
this.fieldErrors = error.fieldErrors;
|
|
11356
|
+
}
|
|
11357
|
+
};
|
|
11358
|
+
function unwrapResponse(result) {
|
|
11359
|
+
if (isApiSuccess(result)) {
|
|
11360
|
+
return result.data;
|
|
11361
|
+
}
|
|
11362
|
+
throw new ApiEnvelopeError(result.error);
|
|
11363
|
+
}
|
|
11364
|
+
|
|
11365
|
+
// src/client/cache.ts
|
|
11366
|
+
var SimpleCache = class {
|
|
11367
|
+
constructor(options = {}) {
|
|
11368
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
11369
|
+
this.maxSize = options.maxSize ?? 100;
|
|
11370
|
+
this.ttl = options.ttl ?? 3e5;
|
|
11371
|
+
this.staleTtl = options.staleTtl ?? 3e5;
|
|
11372
|
+
}
|
|
11373
|
+
/**
|
|
11374
|
+
* Get a fresh value (within TTL)
|
|
11375
|
+
* @returns The value if fresh, null otherwise
|
|
11376
|
+
*/
|
|
11377
|
+
getFresh(key) {
|
|
11378
|
+
const entry = this.cache.get(key);
|
|
11379
|
+
if (!entry) return null;
|
|
11380
|
+
const now = Date.now();
|
|
11381
|
+
if (now <= entry.freshUntil) {
|
|
11382
|
+
return entry.value;
|
|
11383
|
+
}
|
|
11384
|
+
return null;
|
|
11385
|
+
}
|
|
11386
|
+
/**
|
|
11387
|
+
* Get a value that may be stale (past TTL but within staleTtl)
|
|
11388
|
+
* @returns Object with value and stale age, or null if expired
|
|
11389
|
+
*/
|
|
11390
|
+
getStale(key) {
|
|
11391
|
+
const entry = this.cache.get(key);
|
|
11392
|
+
if (!entry) return null;
|
|
11393
|
+
const now = Date.now();
|
|
11394
|
+
if (now > entry.staleUntil) {
|
|
11395
|
+
this.cache.delete(key);
|
|
11396
|
+
return null;
|
|
11397
|
+
}
|
|
11398
|
+
const staleAgeSec = now <= entry.freshUntil ? 0 : Math.floor((now - entry.freshUntil) / 1e3);
|
|
11399
|
+
return {
|
|
11400
|
+
value: entry.value,
|
|
11401
|
+
staleAgeSec
|
|
11402
|
+
};
|
|
11403
|
+
}
|
|
11404
|
+
/**
|
|
11405
|
+
* Store a value with TTL and stale window
|
|
11406
|
+
*/
|
|
11407
|
+
set(key, value, options) {
|
|
11408
|
+
const ttl = options?.ttl ?? this.ttl;
|
|
11409
|
+
const staleTtl = options?.staleTtl ?? this.staleTtl;
|
|
11410
|
+
const now = Date.now();
|
|
11411
|
+
while (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
11412
|
+
this.evictOne(now);
|
|
11413
|
+
}
|
|
11414
|
+
this.cache.set(key, {
|
|
11415
|
+
value,
|
|
11416
|
+
createdAt: now,
|
|
11417
|
+
freshUntil: now + ttl,
|
|
11418
|
+
staleUntil: now + ttl + staleTtl
|
|
11419
|
+
});
|
|
11420
|
+
}
|
|
11421
|
+
/**
|
|
11422
|
+
* Evict one entry to make room for a new one
|
|
11423
|
+
* Priority: oldest stale entry, then oldest fresh entry
|
|
11424
|
+
*/
|
|
11425
|
+
evictOne(now) {
|
|
11426
|
+
let oldestStaleKey = null;
|
|
11427
|
+
let oldestStaleTime = Infinity;
|
|
11428
|
+
let oldestFreshKey = null;
|
|
11429
|
+
let oldestFreshTime = Infinity;
|
|
11430
|
+
for (const [key, entry] of this.cache) {
|
|
11431
|
+
if (now > entry.freshUntil) {
|
|
11432
|
+
if (entry.createdAt < oldestStaleTime) {
|
|
11433
|
+
oldestStaleTime = entry.createdAt;
|
|
11434
|
+
oldestStaleKey = key;
|
|
11435
|
+
}
|
|
11436
|
+
} else {
|
|
11437
|
+
if (entry.createdAt < oldestFreshTime) {
|
|
11438
|
+
oldestFreshTime = entry.createdAt;
|
|
11439
|
+
oldestFreshKey = key;
|
|
11440
|
+
}
|
|
11441
|
+
}
|
|
11442
|
+
}
|
|
11443
|
+
const keyToEvict = oldestStaleKey ?? oldestFreshKey;
|
|
11444
|
+
if (keyToEvict) {
|
|
11445
|
+
this.cache.delete(keyToEvict);
|
|
11446
|
+
}
|
|
11447
|
+
}
|
|
11448
|
+
/**
|
|
11449
|
+
* Remove all fully expired entries (past staleUntil)
|
|
11450
|
+
*/
|
|
11451
|
+
prune() {
|
|
11452
|
+
const now = Date.now();
|
|
11453
|
+
for (const [key, entry] of this.cache) {
|
|
11454
|
+
if (now > entry.staleUntil) {
|
|
11455
|
+
this.cache.delete(key);
|
|
11456
|
+
}
|
|
11457
|
+
}
|
|
11458
|
+
}
|
|
11459
|
+
/**
|
|
11460
|
+
* Clear all entries
|
|
11461
|
+
*/
|
|
11462
|
+
clear() {
|
|
11463
|
+
this.cache.clear();
|
|
11464
|
+
}
|
|
11465
|
+
/**
|
|
11466
|
+
* Check if a key exists and is not fully expired
|
|
11467
|
+
*/
|
|
11468
|
+
has(key) {
|
|
11469
|
+
const entry = this.cache.get(key);
|
|
11470
|
+
if (!entry) return false;
|
|
11471
|
+
const now = Date.now();
|
|
11472
|
+
if (now > entry.staleUntil) {
|
|
11473
|
+
this.cache.delete(key);
|
|
11474
|
+
return false;
|
|
11475
|
+
}
|
|
11476
|
+
return now <= entry.freshUntil;
|
|
11477
|
+
}
|
|
11478
|
+
};
|
|
11479
|
+
|
|
11480
|
+
// src/version.ts
|
|
11481
|
+
var SDK_VERSION = "0.8.1";
|
|
11482
|
+
|
|
11483
|
+
// src/client/error.ts
|
|
11484
|
+
var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
11485
|
+
constructor(apiError) {
|
|
11486
|
+
super(apiError.message);
|
|
11487
|
+
this.name = "RiverbankApiError";
|
|
11488
|
+
if ("cause" in apiError && apiError.cause) {
|
|
11489
|
+
this.cause = apiError.cause;
|
|
11490
|
+
}
|
|
11491
|
+
this.code = apiError.code;
|
|
11492
|
+
this.requestId = apiError.requestId;
|
|
11493
|
+
this.status = apiError.status;
|
|
11494
|
+
this.fieldErrors = apiError.fieldErrors;
|
|
11495
|
+
this.timestamp = apiError.timestamp;
|
|
11496
|
+
this.retryAfterMs = "retryAfterMs" in apiError ? apiError.retryAfterMs : void 0;
|
|
11497
|
+
this.isRetryable = this.computeRetryable();
|
|
11498
|
+
Object.setPrototypeOf(this, _RiverbankApiError.prototype);
|
|
11499
|
+
}
|
|
11500
|
+
/**
|
|
11501
|
+
* Compute whether this error is retryable based on HTTP status code.
|
|
11502
|
+
* - 0 (network errors - no HTTP response): retryable
|
|
11503
|
+
* - 429 (rate limit): retryable
|
|
11504
|
+
* - 5xx (server errors): retryable
|
|
11505
|
+
* - 4xx (client errors, except 429): NOT retryable
|
|
11506
|
+
*/
|
|
11507
|
+
computeRetryable() {
|
|
11508
|
+
if (this.status === 0) return true;
|
|
11509
|
+
if (this.status === 429) return true;
|
|
11510
|
+
if (this.status >= 500) return true;
|
|
11511
|
+
return false;
|
|
11512
|
+
}
|
|
11513
|
+
/**
|
|
11514
|
+
* Check if this is a network error (no HTTP response received)
|
|
11515
|
+
*
|
|
11516
|
+
* Matches: network:connection_error, network:timeout, network:dns_error
|
|
11517
|
+
*/
|
|
11518
|
+
isNetworkError() {
|
|
11519
|
+
return this.code.startsWith("network:");
|
|
11520
|
+
}
|
|
11521
|
+
/**
|
|
11522
|
+
* Check if this error matches a specific error code
|
|
11523
|
+
*
|
|
11524
|
+
* @example
|
|
11525
|
+
* ```ts
|
|
11526
|
+
* if (error.is('auth:unauthenticated')) {
|
|
11527
|
+
* // Redirect to login
|
|
11528
|
+
* }
|
|
11529
|
+
* ```
|
|
11530
|
+
*/
|
|
11531
|
+
is(code) {
|
|
11532
|
+
return this.code === code;
|
|
11533
|
+
}
|
|
11534
|
+
/**
|
|
11535
|
+
* Check if this is an authentication or authorization error
|
|
11536
|
+
*
|
|
11537
|
+
* Matches: auth:unauthenticated, auth:token_expired, auth:token_invalid,
|
|
11538
|
+
* auth:forbidden, auth:mfa_required, auth:insufficient_permissions
|
|
11539
|
+
*/
|
|
11540
|
+
isAuthError() {
|
|
11541
|
+
return this.code.startsWith("auth:");
|
|
11542
|
+
}
|
|
11543
|
+
/**
|
|
11544
|
+
* Check if this is a validation error
|
|
11545
|
+
*
|
|
11546
|
+
* Matches: validation:invalid_input, validation:missing_field, validation:invalid_format
|
|
11547
|
+
*/
|
|
11548
|
+
isValidationError() {
|
|
11549
|
+
return this.code.startsWith("validation:");
|
|
11550
|
+
}
|
|
11551
|
+
/**
|
|
11552
|
+
* Check if this is a resource error (not found, conflict, etc.)
|
|
11553
|
+
*
|
|
11554
|
+
* Matches: resource:not_found, resource:already_exists, resource:conflict, resource:gone
|
|
11555
|
+
*/
|
|
11556
|
+
isResourceError() {
|
|
11557
|
+
return this.code.startsWith("resource:");
|
|
11558
|
+
}
|
|
11559
|
+
/**
|
|
11560
|
+
* Check if this is a rate limiting error
|
|
11561
|
+
*/
|
|
11562
|
+
isRateLimitError() {
|
|
11563
|
+
return this.code.startsWith("rate_limit:");
|
|
11564
|
+
}
|
|
11565
|
+
/**
|
|
11566
|
+
* Check if this is a billing/payment error
|
|
11567
|
+
*/
|
|
11568
|
+
isBillingError() {
|
|
11569
|
+
return this.code.startsWith("billing:");
|
|
11570
|
+
}
|
|
11571
|
+
/**
|
|
11572
|
+
* Check if this is a server error
|
|
11573
|
+
*/
|
|
11574
|
+
isServerError() {
|
|
11575
|
+
return this.code.startsWith("server:");
|
|
11576
|
+
}
|
|
11577
|
+
/**
|
|
11578
|
+
* Returns a human-readable string representation of the error.
|
|
11579
|
+
* Includes all key details for debugging.
|
|
11580
|
+
*
|
|
11581
|
+
* @example
|
|
11582
|
+
* "RiverbankApiError: Content keys cannot access preview content | Code: auth:forbidden | Status: 401 | RequestId: req-abc123"
|
|
11583
|
+
*/
|
|
11584
|
+
toString() {
|
|
11585
|
+
const parts = [`RiverbankApiError: ${this.message}`];
|
|
11586
|
+
if (this.code) parts.push(`Code: ${this.code}`);
|
|
11587
|
+
if (this.status) parts.push(`Status: ${this.status}`);
|
|
11588
|
+
if (this.requestId) parts.push(`RequestId: ${this.requestId}`);
|
|
11589
|
+
return parts.join(" | ");
|
|
11590
|
+
}
|
|
11591
|
+
/**
|
|
11592
|
+
* Custom Node.js inspect output for better console.log display.
|
|
11593
|
+
* This ensures that console.log(error) shows all relevant details
|
|
11594
|
+
* instead of just "[Object]" for nested properties.
|
|
11595
|
+
*/
|
|
11596
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
11597
|
+
return this.toDetailedString();
|
|
11598
|
+
}
|
|
11599
|
+
/**
|
|
11600
|
+
* Returns a detailed multi-line string for debugging.
|
|
11601
|
+
* Used by the Node.js inspect symbol for console output.
|
|
11602
|
+
*/
|
|
11603
|
+
toDetailedString() {
|
|
11604
|
+
const lines = [
|
|
11605
|
+
`RiverbankApiError: ${this.message}`,
|
|
11606
|
+
` Code: ${this.code}`,
|
|
11607
|
+
` Status: ${this.status}`,
|
|
11608
|
+
` RequestId: ${this.requestId}`,
|
|
11609
|
+
` Timestamp: ${this.timestamp}`
|
|
11610
|
+
];
|
|
11611
|
+
if (this.isRetryable) {
|
|
11612
|
+
lines.push(` Retryable: true`);
|
|
11613
|
+
if (this.retryAfterMs) {
|
|
11614
|
+
lines.push(` RetryAfter: ${this.retryAfterMs}ms`);
|
|
11615
|
+
}
|
|
11616
|
+
}
|
|
11617
|
+
if (this.fieldErrors && this.fieldErrors.length > 0) {
|
|
11618
|
+
lines.push(" FieldErrors:");
|
|
11619
|
+
this.fieldErrors.forEach((fe) => {
|
|
11620
|
+
lines.push(` - ${fe.field}: ${fe.message}`);
|
|
11621
|
+
});
|
|
11622
|
+
}
|
|
11623
|
+
return lines.join("\n");
|
|
11624
|
+
}
|
|
11625
|
+
};
|
|
11626
|
+
|
|
11627
|
+
// src/client/resilience.ts
|
|
11628
|
+
var DEFAULT_RETRY_CONFIG2 = {
|
|
11629
|
+
maxAttempts: 3,
|
|
11630
|
+
baseDelayMs: 200,
|
|
11631
|
+
maxDelayMs: 2e3,
|
|
11632
|
+
jitter: "full"
|
|
11633
|
+
};
|
|
11634
|
+
var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
|
|
11635
|
+
failureThreshold: 5,
|
|
11636
|
+
resetTimeoutMs: 3e4,
|
|
11637
|
+
halfOpenMaxRequests: 2
|
|
11638
|
+
};
|
|
11639
|
+
var PERMANENT_NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
11640
|
+
"ECONNREFUSED",
|
|
11641
|
+
// Server is not running / port not listening
|
|
11642
|
+
"ENOTFOUND",
|
|
11643
|
+
// DNS lookup failed - hostname doesn't exist
|
|
11644
|
+
"EAI_AGAIN"
|
|
11645
|
+
// DNS lookup timeout (usually permanent for invalid hosts)
|
|
11646
|
+
]);
|
|
11647
|
+
var TRANSIENT_NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
11648
|
+
"ECONNRESET",
|
|
11649
|
+
// Connection was reset mid-request (server dropped it)
|
|
11650
|
+
"EPIPE",
|
|
11651
|
+
// Broken pipe (connection closed while writing)
|
|
11652
|
+
"ETIMEDOUT",
|
|
11653
|
+
// Connection timed out (could be temporary congestion)
|
|
11654
|
+
"ESOCKETTIMEDOUT"
|
|
11655
|
+
// Socket timeout
|
|
11656
|
+
]);
|
|
11657
|
+
var NODE_NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
11658
|
+
// Permanent
|
|
11659
|
+
"ECONNREFUSED",
|
|
11660
|
+
"ENOTFOUND",
|
|
11661
|
+
"EAI_AGAIN",
|
|
11662
|
+
// Transient
|
|
11663
|
+
"ECONNRESET",
|
|
11664
|
+
"EPIPE",
|
|
11665
|
+
"ETIMEDOUT",
|
|
11666
|
+
"ESOCKETTIMEDOUT"
|
|
11667
|
+
]);
|
|
11668
|
+
function isNodeNetworkErrorCode(code) {
|
|
11669
|
+
return !code.includes(":") && NODE_NETWORK_ERROR_CODES.has(code);
|
|
11670
|
+
}
|
|
11671
|
+
function getErrorCodeFromCause(error) {
|
|
11672
|
+
let current = error;
|
|
11673
|
+
while (current) {
|
|
11674
|
+
const nodeError = current;
|
|
11675
|
+
if (nodeError.code && typeof nodeError.code === "string") {
|
|
11676
|
+
if (isNodeNetworkErrorCode(nodeError.code)) {
|
|
11677
|
+
return nodeError.code;
|
|
11678
|
+
}
|
|
11679
|
+
}
|
|
11680
|
+
const errorWithCause = current;
|
|
11681
|
+
current = errorWithCause.cause;
|
|
11682
|
+
}
|
|
11683
|
+
return void 0;
|
|
11684
|
+
}
|
|
11685
|
+
function isTransientError(error) {
|
|
11686
|
+
if (error instanceof RiverbankApiError) {
|
|
11687
|
+
const errorCode = getErrorCodeFromCause(error);
|
|
11688
|
+
if (errorCode && PERMANENT_NETWORK_ERROR_CODES.has(errorCode)) {
|
|
11689
|
+
return false;
|
|
11690
|
+
}
|
|
11691
|
+
if (error.status === 0) return true;
|
|
11692
|
+
if (error.status === 429) return true;
|
|
11693
|
+
if (error.status >= 500) return true;
|
|
11694
|
+
return false;
|
|
11695
|
+
}
|
|
11696
|
+
if (error instanceof TypeError) {
|
|
11697
|
+
const errorCode = getErrorCodeFromCause(error);
|
|
11698
|
+
if (errorCode) {
|
|
11699
|
+
if (PERMANENT_NETWORK_ERROR_CODES.has(errorCode)) {
|
|
11700
|
+
return false;
|
|
11701
|
+
}
|
|
11702
|
+
if (TRANSIENT_NETWORK_ERROR_CODES.has(errorCode)) {
|
|
11703
|
+
return true;
|
|
11704
|
+
}
|
|
11705
|
+
}
|
|
11706
|
+
return true;
|
|
11707
|
+
}
|
|
11708
|
+
return true;
|
|
11709
|
+
}
|
|
11710
|
+
function calculateBackoff(attempt, config3) {
|
|
11711
|
+
const baseDelayMs = config3.baseDelayMs ?? DEFAULT_RETRY_CONFIG2.baseDelayMs;
|
|
11712
|
+
const maxDelayMs = config3.maxDelayMs ?? DEFAULT_RETRY_CONFIG2.maxDelayMs;
|
|
11713
|
+
const jitter = config3.jitter ?? DEFAULT_RETRY_CONFIG2.jitter;
|
|
11714
|
+
const exponential = baseDelayMs * Math.pow(2, attempt - 1);
|
|
11715
|
+
const capped = Math.min(exponential, maxDelayMs);
|
|
11716
|
+
if (jitter === "full") {
|
|
11717
|
+
return Math.random() * capped;
|
|
11718
|
+
}
|
|
11719
|
+
return capped;
|
|
11720
|
+
}
|
|
11721
|
+
var CircuitBreaker = class {
|
|
11722
|
+
constructor(config3) {
|
|
11723
|
+
this.state = "closed";
|
|
11724
|
+
this.failureCount = 0;
|
|
11725
|
+
this.successCount = 0;
|
|
11726
|
+
this.openUntil = 0;
|
|
11727
|
+
this.halfOpenRequests = 0;
|
|
11728
|
+
this.config = {
|
|
11729
|
+
failureThreshold: config3?.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.failureThreshold,
|
|
11730
|
+
resetTimeoutMs: config3?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs,
|
|
11731
|
+
halfOpenMaxRequests: config3?.halfOpenMaxRequests ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.halfOpenMaxRequests
|
|
11732
|
+
};
|
|
11733
|
+
}
|
|
11734
|
+
/**
|
|
11735
|
+
* Check if circuit is open (requests should be blocked)
|
|
11736
|
+
* Also handles automatic transition from open to half-open after timeout
|
|
11737
|
+
*/
|
|
11738
|
+
isOpen() {
|
|
11739
|
+
if (this.state === "open" && Date.now() >= this.openUntil) {
|
|
11740
|
+
this.transitionTo("half-open");
|
|
11741
|
+
}
|
|
11742
|
+
return this.state === "open";
|
|
11743
|
+
}
|
|
11744
|
+
/**
|
|
11745
|
+
* Check if a request can be attempted
|
|
11746
|
+
* - closed: always yes
|
|
11747
|
+
* - open: always no
|
|
11748
|
+
* - half-open: limited number of probes
|
|
11749
|
+
*/
|
|
11750
|
+
canAttempt() {
|
|
11751
|
+
if (this.state === "closed") return true;
|
|
11752
|
+
if (this.state === "open") return false;
|
|
11753
|
+
return this.halfOpenRequests < this.config.halfOpenMaxRequests;
|
|
11754
|
+
}
|
|
11755
|
+
/**
|
|
11756
|
+
* Increment half-open request counter (call before making request in half-open)
|
|
11757
|
+
*/
|
|
11758
|
+
incrementHalfOpenRequests() {
|
|
11759
|
+
if (this.state === "half-open") {
|
|
11760
|
+
this.halfOpenRequests++;
|
|
11761
|
+
}
|
|
11762
|
+
}
|
|
11763
|
+
/**
|
|
11764
|
+
* Record a successful request
|
|
11765
|
+
*/
|
|
11766
|
+
recordSuccess() {
|
|
11767
|
+
if (this.state === "half-open") {
|
|
11768
|
+
this.successCount++;
|
|
11769
|
+
if (this.successCount >= this.config.halfOpenMaxRequests) {
|
|
11770
|
+
this.transitionTo("closed");
|
|
11771
|
+
}
|
|
11772
|
+
} else {
|
|
11773
|
+
this.failureCount = 0;
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
/**
|
|
11777
|
+
* Record a failed request
|
|
11778
|
+
* Only counts transient failures toward circuit breaker threshold
|
|
11779
|
+
*/
|
|
11780
|
+
recordFailure(error) {
|
|
11781
|
+
if (!isTransientError(error)) return;
|
|
11782
|
+
this.failureCount++;
|
|
11783
|
+
if (this.state === "half-open") {
|
|
11784
|
+
this.transitionTo("open");
|
|
11785
|
+
} else if (this.failureCount >= this.config.failureThreshold) {
|
|
11786
|
+
this.transitionTo("open");
|
|
11787
|
+
}
|
|
11788
|
+
}
|
|
11789
|
+
/**
|
|
11790
|
+
* Get current circuit state
|
|
11791
|
+
*/
|
|
11792
|
+
getState() {
|
|
11793
|
+
return {
|
|
11794
|
+
state: this.state,
|
|
11795
|
+
failureCount: this.failureCount,
|
|
11796
|
+
openUntil: this.state === "open" ? this.openUntil : void 0
|
|
11797
|
+
};
|
|
11798
|
+
}
|
|
11799
|
+
/**
|
|
11800
|
+
* Transition to a new state
|
|
11801
|
+
*/
|
|
11802
|
+
transitionTo(newState) {
|
|
11803
|
+
this.state = newState;
|
|
11804
|
+
if (newState === "open") {
|
|
11805
|
+
this.openUntil = Date.now() + this.config.resetTimeoutMs;
|
|
11806
|
+
} else if (newState === "half-open") {
|
|
11807
|
+
this.halfOpenRequests = 0;
|
|
11808
|
+
this.successCount = 0;
|
|
11809
|
+
} else if (newState === "closed") {
|
|
11810
|
+
this.failureCount = 0;
|
|
11811
|
+
this.successCount = 0;
|
|
11812
|
+
this.halfOpenRequests = 0;
|
|
11813
|
+
}
|
|
11814
|
+
}
|
|
11815
|
+
};
|
|
11816
|
+
async function fetchWithTimeoutAndRetry(fetcher, config3) {
|
|
11817
|
+
const maxAttempts = config3.maxAttempts ?? DEFAULT_RETRY_CONFIG2.maxAttempts;
|
|
11818
|
+
const requestTimeoutMs = config3.requestTimeoutMs ?? 8e3;
|
|
11819
|
+
let lastError;
|
|
11820
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
11821
|
+
try {
|
|
11822
|
+
const controller = new AbortController();
|
|
11823
|
+
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
11824
|
+
try {
|
|
11825
|
+
const result = await fetcher(controller.signal);
|
|
11826
|
+
return result;
|
|
11827
|
+
} finally {
|
|
11828
|
+
clearTimeout(timeoutId);
|
|
11829
|
+
}
|
|
11830
|
+
} catch (error) {
|
|
11831
|
+
lastError = error;
|
|
11832
|
+
const shouldRetry = shouldRetryError(error, config3.retryOn);
|
|
11833
|
+
if (!shouldRetry) {
|
|
11834
|
+
throw error;
|
|
11835
|
+
}
|
|
11836
|
+
if (attempt < maxAttempts) {
|
|
11837
|
+
const delay = getRetryDelay(error, attempt, config3);
|
|
11838
|
+
await sleep2(delay);
|
|
11839
|
+
}
|
|
11840
|
+
}
|
|
11841
|
+
}
|
|
11842
|
+
throw lastError;
|
|
11843
|
+
}
|
|
11844
|
+
function isAbortError(error) {
|
|
11845
|
+
if (typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError") {
|
|
11846
|
+
return true;
|
|
11847
|
+
}
|
|
11848
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
11849
|
+
return true;
|
|
11850
|
+
}
|
|
11851
|
+
return false;
|
|
11852
|
+
}
|
|
11853
|
+
function shouldRetryError(error, customRetryOn) {
|
|
11854
|
+
if (isAbortError(error)) {
|
|
11855
|
+
return false;
|
|
11856
|
+
}
|
|
11857
|
+
if (customRetryOn) {
|
|
11858
|
+
const statusCode = error instanceof RiverbankApiError ? error.status : void 0;
|
|
11859
|
+
return customRetryOn(error, statusCode);
|
|
11860
|
+
}
|
|
11861
|
+
return isTransientError(error);
|
|
11862
|
+
}
|
|
11863
|
+
function getRetryDelay(error, attempt, config3) {
|
|
11864
|
+
if (error instanceof RiverbankApiError && error.retryAfterMs) {
|
|
11865
|
+
return error.retryAfterMs;
|
|
11866
|
+
}
|
|
11867
|
+
return calculateBackoff(attempt, config3);
|
|
11868
|
+
}
|
|
11869
|
+
function sleep2(ms) {
|
|
11870
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
11871
|
+
}
|
|
11872
|
+
var CircuitOpenError = class extends Error {
|
|
11873
|
+
constructor(state) {
|
|
11874
|
+
super("Circuit breaker is open");
|
|
11875
|
+
this.name = "CircuitOpenError";
|
|
11876
|
+
this.circuitState = state;
|
|
11877
|
+
}
|
|
11878
|
+
};
|
|
11879
|
+
|
|
11880
|
+
// src/client/index.ts
|
|
11881
|
+
var prebuildModule = null;
|
|
11882
|
+
function getPrebuildModule() {
|
|
11883
|
+
if (prebuildModule !== null) return prebuildModule;
|
|
11884
|
+
if (typeof process === "undefined" || !process.versions?.node) {
|
|
11885
|
+
return null;
|
|
11886
|
+
}
|
|
11887
|
+
try {
|
|
11888
|
+
prebuildModule = (init_loader(), __toCommonJS(loader_exports));
|
|
11889
|
+
return prebuildModule;
|
|
11890
|
+
} catch {
|
|
11891
|
+
return null;
|
|
11892
|
+
}
|
|
11893
|
+
}
|
|
11894
|
+
setSdkVersion(SDK_VERSION);
|
|
11895
|
+
var DEFAULT_BROWSER_TIMEOUT_MS = 5e3;
|
|
11896
|
+
var DEFAULT_SERVER_TIMEOUT_MS = 8e3;
|
|
11897
|
+
function generateRequestId2() {
|
|
11898
|
+
return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
11899
|
+
}
|
|
11900
|
+
function isAbortError2(error) {
|
|
11901
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
11902
|
+
return true;
|
|
11903
|
+
}
|
|
11904
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
11905
|
+
return true;
|
|
11906
|
+
}
|
|
11907
|
+
return false;
|
|
11908
|
+
}
|
|
11909
|
+
function getNetworkErrorCode(error) {
|
|
11910
|
+
if (error.name === "TimeoutError" || error.name === "AbortError") {
|
|
11911
|
+
return "network:timeout";
|
|
11912
|
+
}
|
|
11913
|
+
const nodeError = error;
|
|
11914
|
+
if (nodeError.code) {
|
|
11915
|
+
switch (nodeError.code) {
|
|
11916
|
+
case "ETIMEDOUT":
|
|
11917
|
+
case "ESOCKETTIMEDOUT":
|
|
11918
|
+
return "network:timeout";
|
|
11919
|
+
case "ENOTFOUND":
|
|
11920
|
+
case "EAI_AGAIN":
|
|
11921
|
+
return "network:dns_error";
|
|
11922
|
+
case "ECONNREFUSED":
|
|
11923
|
+
case "ECONNRESET":
|
|
11924
|
+
case "EPIPE":
|
|
11925
|
+
return "network:connection_error";
|
|
11926
|
+
}
|
|
11927
|
+
}
|
|
11928
|
+
const message = error.message.toLowerCase();
|
|
11929
|
+
if (message.includes("timeout") || message.includes("timed out")) {
|
|
11930
|
+
return "network:timeout";
|
|
11931
|
+
}
|
|
11932
|
+
if (message.includes("dns") || message.includes("getaddrinfo") || message.includes("enotfound")) {
|
|
11933
|
+
return "network:dns_error";
|
|
11934
|
+
}
|
|
11935
|
+
return "network:connection_error";
|
|
11936
|
+
}
|
|
11937
|
+
function convertToTypedError(error) {
|
|
11938
|
+
if (isAbortError2(error)) {
|
|
11939
|
+
throw error;
|
|
11940
|
+
}
|
|
11941
|
+
if (error instanceof ApiEnvelopeError) {
|
|
11942
|
+
throw new RiverbankApiError({
|
|
11943
|
+
code: error.code,
|
|
11944
|
+
message: error.message,
|
|
11945
|
+
requestId: error.requestId,
|
|
11946
|
+
timestamp: error.timestamp,
|
|
11947
|
+
status: error.status,
|
|
11948
|
+
fieldErrors: error.fieldErrors
|
|
11949
|
+
});
|
|
11950
|
+
}
|
|
11951
|
+
if (error instanceof ApiRequestError && error.body && typeof error.body === "object") {
|
|
11952
|
+
const body = error.body;
|
|
11953
|
+
if (isApiError(body)) {
|
|
11954
|
+
const envelopeError = body.error;
|
|
11955
|
+
throw new RiverbankApiError({
|
|
11956
|
+
...envelopeError,
|
|
11957
|
+
retryAfterMs: error.retryAfterMs
|
|
11958
|
+
});
|
|
11959
|
+
}
|
|
11960
|
+
}
|
|
11961
|
+
if (error instanceof TypeError || error instanceof Error && !("status" in error)) {
|
|
11962
|
+
const networkError = error;
|
|
11963
|
+
throw new RiverbankApiError({
|
|
11964
|
+
code: getNetworkErrorCode(networkError),
|
|
11965
|
+
message: networkError.message || "Network request failed",
|
|
11966
|
+
requestId: `local-${Date.now()}`,
|
|
11967
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11968
|
+
status: 0,
|
|
11969
|
+
// No HTTP response received
|
|
11970
|
+
cause: networkError
|
|
11971
|
+
// Preserve original error for retry/circuit breaker classification
|
|
11972
|
+
});
|
|
11973
|
+
}
|
|
11974
|
+
throw error;
|
|
11975
|
+
}
|
|
11976
|
+
function detectKeyType(apiKey) {
|
|
11977
|
+
if (apiKey.startsWith("bld_live_sk_") || apiKey.startsWith("bld_test_sk_")) {
|
|
11978
|
+
return "content";
|
|
11979
|
+
}
|
|
11980
|
+
if (apiKey.startsWith("bld_preview_sk_")) {
|
|
11981
|
+
return "preview";
|
|
11982
|
+
}
|
|
11983
|
+
return "unknown";
|
|
11984
|
+
}
|
|
11985
|
+
function createRiverbankClient(config3) {
|
|
11986
|
+
if (!config3.baseUrl) {
|
|
11987
|
+
throw new Error(
|
|
11988
|
+
"baseUrl is required when creating a Riverbank client. Expected format: https://dashboard.example.com/api (must include /api path)"
|
|
11989
|
+
);
|
|
11990
|
+
}
|
|
11991
|
+
if (!config3.baseUrl.endsWith("/api")) {
|
|
11992
|
+
throw new Error(
|
|
11993
|
+
`baseUrl must end with '/api'. Received: ${config3.baseUrl}. Expected format: https://dashboard.example.com/api`
|
|
11994
|
+
);
|
|
11995
|
+
}
|
|
11996
|
+
const cacheEnabled = config3.cache?.enabled ?? true;
|
|
11997
|
+
const cacheTTL = (config3.cache?.ttl ?? 300) * 1e3;
|
|
11998
|
+
const cacheMaxSize = config3.cache?.maxSize ?? 100;
|
|
11999
|
+
const resilienceEnabled = config3.resilience?.enabled ?? true;
|
|
12000
|
+
const staleIfError = config3.resilience?.staleIfError ?? true;
|
|
12001
|
+
const staleTtlMs = (config3.resilience?.staleTtlSec ?? 300) * 1e3;
|
|
12002
|
+
const requestTimeoutMs = config3.resilience?.requestTimeoutMs ?? (typeof window !== "undefined" ? DEFAULT_BROWSER_TIMEOUT_MS : DEFAULT_SERVER_TIMEOUT_MS);
|
|
12003
|
+
const retryConfig = {
|
|
12004
|
+
maxAttempts: config3.resilience?.retry?.maxAttempts ?? DEFAULT_RETRY_CONFIG2.maxAttempts,
|
|
12005
|
+
baseDelayMs: config3.resilience?.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG2.baseDelayMs,
|
|
12006
|
+
maxDelayMs: config3.resilience?.retry?.maxDelayMs ?? DEFAULT_RETRY_CONFIG2.maxDelayMs,
|
|
12007
|
+
jitter: config3.resilience?.retry?.jitter ?? DEFAULT_RETRY_CONFIG2.jitter,
|
|
12008
|
+
retryOn: config3.resilience?.retry?.retryOn
|
|
12009
|
+
};
|
|
12010
|
+
const circuitBreakerConfig = {
|
|
12011
|
+
failureThreshold: config3.resilience?.circuitBreaker?.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.failureThreshold,
|
|
12012
|
+
resetTimeoutMs: config3.resilience?.circuitBreaker?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs,
|
|
12013
|
+
halfOpenMaxRequests: config3.resilience?.circuitBreaker?.halfOpenMaxRequests ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.halfOpenMaxRequests
|
|
12014
|
+
};
|
|
12015
|
+
const keyType = detectKeyType(config3.apiKey);
|
|
12016
|
+
const apiClient = createBearerAPIClient(config3.apiKey, config3.baseUrl);
|
|
12017
|
+
const cache = new SimpleCache({
|
|
12018
|
+
maxSize: cacheMaxSize,
|
|
12019
|
+
ttl: cacheTTL,
|
|
12020
|
+
staleTtl: staleTtlMs
|
|
12021
|
+
});
|
|
12022
|
+
const circuitBreaker = new CircuitBreaker(circuitBreakerConfig);
|
|
12023
|
+
const prebuildDir = config3.resilience?.prebuildDir;
|
|
12024
|
+
const prebuildMod = prebuildDir ? getPrebuildModule() : null;
|
|
12025
|
+
const prebuildLoader = prebuildMod?.canUsePrebuild() && prebuildDir ? new prebuildMod.PrebuildLoader({
|
|
12026
|
+
prebuildDir,
|
|
12027
|
+
maxPrebuildAgeSec: config3.resilience?.maxPrebuildAgeSec
|
|
12028
|
+
}) : null;
|
|
12029
|
+
let lastStatus = null;
|
|
12030
|
+
let isDegraded = false;
|
|
12031
|
+
function emitStatus(source, data, details) {
|
|
12032
|
+
const status = {
|
|
12033
|
+
source,
|
|
12034
|
+
isPreview: details.isPreview,
|
|
12035
|
+
cacheKey: details.cacheKey,
|
|
12036
|
+
error: details.error,
|
|
12037
|
+
staleAgeSec: details.staleAgeSec,
|
|
12038
|
+
prebuildAgeSec: details.prebuildAgeSec,
|
|
12039
|
+
circuit: circuitBreaker.getState(),
|
|
12040
|
+
requestId: details.requestId,
|
|
12041
|
+
durationMs: details.durationMs
|
|
12042
|
+
};
|
|
12043
|
+
lastStatus = status;
|
|
12044
|
+
config3.resilience?.onStatusChange?.(status);
|
|
12045
|
+
const nowDegraded = source === "stale" || source === "error";
|
|
12046
|
+
if (nowDegraded !== isDegraded) {
|
|
12047
|
+
isDegraded = nowDegraded;
|
|
12048
|
+
config3.resilience?.onDegradedMode?.(nowDegraded, status);
|
|
12049
|
+
}
|
|
12050
|
+
return data;
|
|
12051
|
+
}
|
|
12052
|
+
async function resilientFetch(cacheKey, fetcher, options) {
|
|
12053
|
+
const requestId = generateRequestId2();
|
|
12054
|
+
const startTime = Date.now();
|
|
12055
|
+
const isPreview = options.preview ?? false;
|
|
12056
|
+
const statusDetails = (extra = {}) => ({
|
|
12057
|
+
requestId,
|
|
12058
|
+
cacheKey,
|
|
12059
|
+
isPreview,
|
|
12060
|
+
durationMs: Date.now() - startTime,
|
|
12061
|
+
...extra
|
|
12062
|
+
});
|
|
12063
|
+
if (cacheEnabled && !options.force) {
|
|
12064
|
+
const fresh = cache.getFresh(cacheKey);
|
|
12065
|
+
if (fresh !== null) {
|
|
12066
|
+
return emitStatus("cache", fresh, statusDetails());
|
|
12067
|
+
}
|
|
12068
|
+
}
|
|
12069
|
+
if (resilienceEnabled && circuitBreaker.isOpen()) {
|
|
12070
|
+
if (!isPreview && staleIfError) {
|
|
12071
|
+
const stale = cache.getStale(cacheKey);
|
|
12072
|
+
if (stale) {
|
|
12073
|
+
return emitStatus("stale", stale.value, statusDetails({
|
|
12074
|
+
staleAgeSec: stale.staleAgeSec,
|
|
12075
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
12076
|
+
}));
|
|
12077
|
+
}
|
|
12078
|
+
}
|
|
12079
|
+
if (!isPreview && options.prebuildFallback) {
|
|
12080
|
+
const prebuildResult = options.prebuildFallback();
|
|
12081
|
+
if (prebuildResult) {
|
|
12082
|
+
return emitStatus("prebuild", prebuildResult.data, statusDetails({
|
|
12083
|
+
prebuildAgeSec: prebuildResult.prebuildAgeSec,
|
|
12084
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
12085
|
+
}));
|
|
12086
|
+
}
|
|
12087
|
+
}
|
|
12088
|
+
const circuitState = circuitBreaker.getState();
|
|
12089
|
+
emitStatus("error", null, statusDetails({
|
|
12090
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
12091
|
+
}));
|
|
12092
|
+
throw new CircuitOpenError(circuitState);
|
|
12093
|
+
}
|
|
12094
|
+
try {
|
|
12095
|
+
let data;
|
|
12096
|
+
if (resilienceEnabled) {
|
|
12097
|
+
if (circuitBreaker.getState().state === "half-open") {
|
|
12098
|
+
circuitBreaker.incrementHalfOpenRequests();
|
|
12099
|
+
}
|
|
12100
|
+
data = await fetchWithTimeoutAndRetry(
|
|
12101
|
+
async (timeoutSignal) => {
|
|
12102
|
+
const combinedSignal = options.signal ? combineAbortSignals(timeoutSignal, options.signal) : timeoutSignal;
|
|
12103
|
+
try {
|
|
12104
|
+
const response = await fetcher(combinedSignal);
|
|
12105
|
+
return unwrapResponse(response);
|
|
12106
|
+
} catch (error) {
|
|
12107
|
+
convertToTypedError(error);
|
|
12108
|
+
}
|
|
12109
|
+
},
|
|
12110
|
+
{
|
|
12111
|
+
...retryConfig,
|
|
12112
|
+
requestTimeoutMs
|
|
12113
|
+
}
|
|
12114
|
+
);
|
|
12115
|
+
circuitBreaker.recordSuccess();
|
|
12116
|
+
} else {
|
|
12117
|
+
try {
|
|
12118
|
+
const response = await fetcher(options.signal ?? new AbortController().signal);
|
|
12119
|
+
data = unwrapResponse(response);
|
|
12120
|
+
} catch (error) {
|
|
12121
|
+
convertToTypedError(error);
|
|
12122
|
+
}
|
|
12123
|
+
}
|
|
12124
|
+
if (cacheEnabled) {
|
|
12125
|
+
cache.set(cacheKey, data);
|
|
12126
|
+
}
|
|
12127
|
+
return emitStatus("live", data, statusDetails());
|
|
12128
|
+
} catch (error) {
|
|
12129
|
+
if (resilienceEnabled && error instanceof Error) {
|
|
12130
|
+
circuitBreaker.recordFailure(error);
|
|
12131
|
+
}
|
|
12132
|
+
if (!isPreview && staleIfError && cacheEnabled) {
|
|
12133
|
+
const stale = cache.getStale(cacheKey);
|
|
12134
|
+
if (stale) {
|
|
12135
|
+
const errorInfo2 = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
12136
|
+
return emitStatus("stale", stale.value, statusDetails({
|
|
12137
|
+
staleAgeSec: stale.staleAgeSec,
|
|
12138
|
+
error: errorInfo2
|
|
12139
|
+
}));
|
|
12140
|
+
}
|
|
12141
|
+
}
|
|
12142
|
+
if (!isPreview && options.prebuildFallback) {
|
|
12143
|
+
const prebuildResult = options.prebuildFallback();
|
|
12144
|
+
if (prebuildResult) {
|
|
12145
|
+
const errorInfo2 = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
12146
|
+
return emitStatus("prebuild", prebuildResult.data, statusDetails({
|
|
12147
|
+
prebuildAgeSec: prebuildResult.prebuildAgeSec,
|
|
12148
|
+
error: errorInfo2
|
|
12149
|
+
}));
|
|
12150
|
+
}
|
|
12151
|
+
}
|
|
12152
|
+
const errorInfo = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
12153
|
+
emitStatus("error", null, statusDetails({ error: errorInfo }));
|
|
12154
|
+
throw error;
|
|
12155
|
+
}
|
|
12156
|
+
}
|
|
12157
|
+
function combineAbortSignals(...signals) {
|
|
12158
|
+
const controller = new AbortController();
|
|
12159
|
+
for (const signal of signals) {
|
|
12160
|
+
if (signal.aborted) {
|
|
12161
|
+
controller.abort(signal.reason);
|
|
12162
|
+
break;
|
|
12163
|
+
}
|
|
12164
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
|
|
12165
|
+
}
|
|
12166
|
+
return controller.signal;
|
|
12167
|
+
}
|
|
12168
|
+
return {
|
|
12169
|
+
async getSite(params) {
|
|
12170
|
+
const { slug, domain, id, signal } = params;
|
|
12171
|
+
if (!slug && !domain && !id) {
|
|
12172
|
+
throw new Error(
|
|
12173
|
+
`getSite() requires at least one identifier: slug, domain, or id. Received: ${JSON.stringify(params)}`
|
|
12174
|
+
);
|
|
12175
|
+
}
|
|
12176
|
+
const cacheKey = `site:${slug || domain || id}`;
|
|
12177
|
+
const siteId = id || slug || domain;
|
|
12178
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12179
|
+
const apiParams = {};
|
|
12180
|
+
if (params.slug) apiParams.slug = params.slug;
|
|
12181
|
+
if (params.domain) apiParams.domain = params.domain;
|
|
12182
|
+
if (params.id) apiParams.id = params.id;
|
|
12183
|
+
return await apiClient({ endpoint: "getSite", params: apiParams, options: { signal: sig } });
|
|
12184
|
+
}, {
|
|
12185
|
+
signal,
|
|
12186
|
+
prebuildFallback: prebuildLoader && siteId ? () => prebuildLoader.loadSite(siteId) : void 0
|
|
12187
|
+
});
|
|
12188
|
+
},
|
|
12189
|
+
async getPage(params) {
|
|
12190
|
+
const { siteId, path: path13, preview = false, signal } = params;
|
|
12191
|
+
const cacheKey = `page:${siteId}:${path13}:${preview}`;
|
|
12192
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12193
|
+
return await apiClient({ endpoint: "getContentByPath", params: { siteId }, body: { path: path13, preview }, options: { signal: sig } });
|
|
12194
|
+
}, {
|
|
12195
|
+
preview,
|
|
12196
|
+
signal,
|
|
12197
|
+
// Prebuild fallback only for published pages (not preview)
|
|
12198
|
+
prebuildFallback: prebuildLoader && !preview ? () => prebuildLoader.loadPage(siteId, path13) : void 0
|
|
12199
|
+
});
|
|
12200
|
+
},
|
|
12201
|
+
async getEntries(params) {
|
|
12202
|
+
const { siteId, contentType, limit, offset, order, preview = false, mode, entryIds, includeMeta, signal } = params;
|
|
12203
|
+
const entryIdsCacheKey = mode === "manual" && entryIds?.length ? entryIds.join(",") : "";
|
|
12204
|
+
const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ""}:${offset ?? ""}:${order ?? ""}:${preview}:${mode ?? ""}:${entryIdsCacheKey}:${includeMeta ?? ""}`;
|
|
12205
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12206
|
+
let orderParam;
|
|
12207
|
+
if (order === "newest") {
|
|
12208
|
+
orderParam = "published_at.desc";
|
|
12209
|
+
} else if (order === "oldest") {
|
|
12210
|
+
orderParam = "published_at.asc";
|
|
12211
|
+
} else if (order === "title") {
|
|
12212
|
+
orderParam = "title.asc";
|
|
12213
|
+
}
|
|
12214
|
+
const apiParams = {
|
|
12215
|
+
siteId,
|
|
12216
|
+
type: contentType,
|
|
12217
|
+
...typeof limit === "number" && { limit: String(limit) },
|
|
12218
|
+
...typeof offset === "number" && { offset: String(offset) },
|
|
12219
|
+
...includeMeta && { meta: "true" },
|
|
12220
|
+
...orderParam && { order: orderParam },
|
|
12221
|
+
...preview && { stage: "preview" },
|
|
12222
|
+
...mode === "manual" && entryIds?.length && {
|
|
12223
|
+
mode: "manual",
|
|
12224
|
+
entryIds: JSON.stringify(entryIds)
|
|
12225
|
+
}
|
|
12226
|
+
};
|
|
12227
|
+
return await apiClient({ endpoint: "listPublishedEntries", params: apiParams, options: { signal: sig } });
|
|
12228
|
+
}, {
|
|
12229
|
+
preview,
|
|
12230
|
+
signal,
|
|
12231
|
+
// Prebuild fallback only for published entries (not preview, not manual mode)
|
|
12232
|
+
prebuildFallback: prebuildLoader && !preview ? () => prebuildLoader.loadEntries(siteId, params) : void 0
|
|
12233
|
+
});
|
|
12234
|
+
},
|
|
12235
|
+
async getEntry(params) {
|
|
12236
|
+
const { siteId, contentType, slug, signal } = params;
|
|
12237
|
+
const cacheKey = `entry:${siteId}:${contentType}:${slug}`;
|
|
12238
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12239
|
+
return await apiClient({ endpoint: "getPublishedEntryPreview", params: { siteId, type: contentType, slug }, options: { signal: sig } });
|
|
12240
|
+
}, { signal });
|
|
12241
|
+
},
|
|
12242
|
+
async getPublicFormById(params) {
|
|
12243
|
+
const { formId, signal } = params;
|
|
12244
|
+
if (!formId) {
|
|
12245
|
+
throw new Error("getPublicFormById() requires formId");
|
|
12246
|
+
}
|
|
12247
|
+
const cacheKey = `public-form:${formId}`;
|
|
12248
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12249
|
+
return await apiClient({ endpoint: "getPublicFormById", params: { formId }, options: { signal: sig } });
|
|
12250
|
+
}, { signal });
|
|
12251
|
+
},
|
|
12252
|
+
async getPublicBookingServices(params) {
|
|
12253
|
+
const { siteId, ids, signal } = params;
|
|
12254
|
+
if (!siteId) {
|
|
12255
|
+
throw new Error("getPublicBookingServices() requires siteId");
|
|
12256
|
+
}
|
|
12257
|
+
const cacheKey = `public-booking-services:${siteId}:${ids ?? ""}`;
|
|
12258
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12259
|
+
const apiParams = {
|
|
12260
|
+
siteId,
|
|
12261
|
+
...ids && { ids }
|
|
12262
|
+
};
|
|
12263
|
+
return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams, options: { signal: sig } });
|
|
12264
|
+
}, { signal });
|
|
12265
|
+
},
|
|
12266
|
+
async listPublicEvents(params) {
|
|
12267
|
+
const { siteId, limit, from, to, stage, signal } = params;
|
|
12268
|
+
if (!siteId) {
|
|
12269
|
+
throw new Error("listPublicEvents() requires siteId");
|
|
12270
|
+
}
|
|
12271
|
+
const cacheKey = `public-events:${siteId}:${limit ?? ""}:${from ?? ""}:${to ?? ""}:${stage ?? ""}`;
|
|
12272
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12273
|
+
const apiParams = {
|
|
12274
|
+
siteId,
|
|
12275
|
+
...typeof limit === "number" && { limit: String(limit) },
|
|
12276
|
+
...from && { from },
|
|
12277
|
+
...to && { to },
|
|
12278
|
+
...stage && { stage }
|
|
12279
|
+
};
|
|
12280
|
+
return await apiClient({ endpoint: "listPublicEvents", params: apiParams, options: { signal: sig } });
|
|
12281
|
+
}, { signal });
|
|
12282
|
+
},
|
|
12283
|
+
async resolveEventOccurrence(params) {
|
|
12284
|
+
const { siteId, entryId, segment, signal } = params;
|
|
12285
|
+
if (!siteId || !entryId || !segment) {
|
|
12286
|
+
throw new Error("resolveEventOccurrence() requires siteId, entryId, and segment");
|
|
12287
|
+
}
|
|
12288
|
+
const cacheKey = `event-occurrence:${siteId}:${entryId}:${segment}`;
|
|
12289
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12290
|
+
return await apiClient({
|
|
12291
|
+
endpoint: "resolveEventOccurrence",
|
|
12292
|
+
params: { siteId, entryId, segment },
|
|
12293
|
+
options: { signal: sig }
|
|
12294
|
+
});
|
|
12295
|
+
}, { signal });
|
|
12296
|
+
},
|
|
12297
|
+
async checkRedirect(params) {
|
|
12298
|
+
const { siteId, path: path13, signal } = params;
|
|
12299
|
+
if (!siteId || !path13) {
|
|
12300
|
+
throw new Error("checkRedirect() requires siteId and path");
|
|
12301
|
+
}
|
|
12302
|
+
const cacheKey = `redirect:${siteId}:${path13}`;
|
|
12303
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12304
|
+
return await apiClient({
|
|
12305
|
+
endpoint: "checkRedirect",
|
|
12306
|
+
params: { site: siteId, path: path13 },
|
|
12307
|
+
options: { signal: sig }
|
|
12308
|
+
});
|
|
12309
|
+
}, { signal });
|
|
12310
|
+
},
|
|
12311
|
+
async getAllPublishedRoutes(params) {
|
|
12312
|
+
const { siteId, signal } = params;
|
|
12313
|
+
if (!siteId) {
|
|
12314
|
+
throw new Error("getAllPublishedRoutes() requires siteId");
|
|
12315
|
+
}
|
|
12316
|
+
const cacheKey = `routable-content:${siteId}:published`;
|
|
12317
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
12318
|
+
return await apiClient({
|
|
12319
|
+
endpoint: "getPublicRoutableContent",
|
|
12320
|
+
params: { siteId, publishedOnly: "true" },
|
|
12321
|
+
options: { signal: sig }
|
|
12322
|
+
});
|
|
12323
|
+
}, { signal });
|
|
12324
|
+
},
|
|
12325
|
+
clearCache() {
|
|
12326
|
+
cache.clear();
|
|
12327
|
+
},
|
|
12328
|
+
getKeyType() {
|
|
12329
|
+
return keyType;
|
|
12330
|
+
},
|
|
12331
|
+
getLastEmittedStatus() {
|
|
12332
|
+
return lastStatus;
|
|
12333
|
+
},
|
|
12334
|
+
getCircuitState() {
|
|
12335
|
+
return circuitBreaker.getState();
|
|
12336
|
+
}
|
|
12337
|
+
};
|
|
12338
|
+
}
|
|
12339
|
+
init_constants();
|
|
12340
|
+
var MANIFEST_VERSION = "1.0.0";
|
|
12341
|
+
var MANIFEST_FILENAME = "manifest.json";
|
|
12342
|
+
function pathToFilename(routePath) {
|
|
12343
|
+
if (routePath === "/") return "_home.json";
|
|
12344
|
+
return routePath.slice(1).replace(/[^a-zA-Z0-9\-\/]/g, "_").replace(/\//g, "-") + ".json";
|
|
12345
|
+
}
|
|
12346
|
+
function ensureDir3(dirPath) {
|
|
12347
|
+
if (!fs6__namespace.existsSync(dirPath)) {
|
|
12348
|
+
fs6__namespace.mkdirSync(dirPath, { recursive: true });
|
|
12349
|
+
}
|
|
12350
|
+
}
|
|
12351
|
+
function writeJsonFile2(filePath, data) {
|
|
12352
|
+
const content = JSON.stringify(data, null, 2);
|
|
12353
|
+
fs6__namespace.writeFileSync(filePath, content, "utf-8");
|
|
12354
|
+
return Buffer.byteLength(content, "utf-8");
|
|
12355
|
+
}
|
|
12356
|
+
function calculateChecksum(data) {
|
|
12357
|
+
const { checksum: _, ...rest } = data;
|
|
12358
|
+
const content = JSON.stringify(rest);
|
|
12359
|
+
return crypto2__namespace.createHash("sha256").update(content).digest("hex");
|
|
12360
|
+
}
|
|
12361
|
+
async function fetchAllEntries(client, siteId, contentType, onProgress) {
|
|
12362
|
+
const allEntries = [];
|
|
12363
|
+
let offset = 0;
|
|
12364
|
+
let hasMore = true;
|
|
12365
|
+
while (hasMore) {
|
|
12366
|
+
onProgress?.(`${contentType} (batch ${Math.floor(offset / PREBUILD_PAGE_SIZE) + 1})`);
|
|
12367
|
+
const response = await client.getEntries({
|
|
12368
|
+
siteId,
|
|
12369
|
+
contentType,
|
|
12370
|
+
limit: PREBUILD_PAGE_SIZE,
|
|
12371
|
+
offset,
|
|
12372
|
+
preview: false
|
|
12373
|
+
// Published content only
|
|
12374
|
+
});
|
|
12375
|
+
allEntries.push(...response.entries);
|
|
12376
|
+
offset += PREBUILD_PAGE_SIZE;
|
|
12377
|
+
hasMore = response.entries.length === PREBUILD_PAGE_SIZE;
|
|
12378
|
+
}
|
|
12379
|
+
return allEntries;
|
|
12380
|
+
}
|
|
12381
|
+
async function prebuildSite(client, siteId, outputDir) {
|
|
12382
|
+
const site = await client.getSite({ id: siteId });
|
|
12383
|
+
const filename = "site.json";
|
|
12384
|
+
const filePath = path9__namespace.join(outputDir, filename);
|
|
12385
|
+
const cacheFile = {
|
|
12386
|
+
data: site,
|
|
12387
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12388
|
+
};
|
|
12389
|
+
const size = writeJsonFile2(filePath, cacheFile);
|
|
12390
|
+
return { files: [filename], size, siteData: site };
|
|
12391
|
+
}
|
|
12392
|
+
async function prebuildPages(client, siteId, routes, outputDir, onProgress) {
|
|
12393
|
+
const pagesDir = path9__namespace.join(outputDir, "pages");
|
|
12394
|
+
ensureDir3(pagesDir);
|
|
12395
|
+
const files = [];
|
|
12396
|
+
let totalSize = 0;
|
|
12397
|
+
const publishedPaths = Object.entries(routes).filter(([_, route]) => route.status === "published").map(([_, route]) => route.path);
|
|
12398
|
+
const pageIndex = [];
|
|
12399
|
+
const total = publishedPaths.length;
|
|
12400
|
+
let current = 0;
|
|
12401
|
+
for (const pagePath of publishedPaths) {
|
|
12402
|
+
current++;
|
|
12403
|
+
onProgress?.({
|
|
12404
|
+
current,
|
|
12405
|
+
total,
|
|
12406
|
+
item: `Page: ${pagePath}`,
|
|
12407
|
+
contentType: "pages"
|
|
12408
|
+
});
|
|
12409
|
+
try {
|
|
12410
|
+
const pageData = await client.getPage({ siteId, path: pagePath, preview: false });
|
|
12411
|
+
const filename = pathToFilename(pagePath);
|
|
12412
|
+
const filePath = path9__namespace.join(pagesDir, filename);
|
|
12413
|
+
const cacheFile = {
|
|
12414
|
+
data: pageData,
|
|
12415
|
+
path: pagePath,
|
|
12416
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12417
|
+
};
|
|
12418
|
+
totalSize += writeJsonFile2(filePath, cacheFile);
|
|
12419
|
+
files.push(`pages/${filename}`);
|
|
12420
|
+
if ("page" in pageData) {
|
|
12421
|
+
pageIndex.push({
|
|
12422
|
+
path: pagePath,
|
|
12423
|
+
pageId: pageData.page.routeId || "",
|
|
12424
|
+
title: pageData.page.name || "Untitled"
|
|
12425
|
+
});
|
|
12426
|
+
}
|
|
12427
|
+
} catch (error) {
|
|
12428
|
+
console.warn(`[Prebuild] Failed to fetch page ${pagePath}:`, error.message);
|
|
12429
|
+
}
|
|
12430
|
+
}
|
|
12431
|
+
const indexFile = {
|
|
12432
|
+
pages: pageIndex,
|
|
12433
|
+
totalCount: pageIndex.length,
|
|
12434
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12435
|
+
};
|
|
12436
|
+
const indexPath = path9__namespace.join(pagesDir, "_index.json");
|
|
12437
|
+
totalSize += writeJsonFile2(indexPath, indexFile);
|
|
12438
|
+
files.push("pages/_index.json");
|
|
12439
|
+
return { files, size: totalSize };
|
|
12440
|
+
}
|
|
12441
|
+
async function prebuildEntries(client, siteId, contentTypes, outputDir, onProgress) {
|
|
12442
|
+
const entriesDir = path9__namespace.join(outputDir, "entries");
|
|
12443
|
+
ensureDir3(entriesDir);
|
|
12444
|
+
const files = [];
|
|
12445
|
+
let totalSize = 0;
|
|
12446
|
+
const total = contentTypes.length;
|
|
12447
|
+
let current = 0;
|
|
12448
|
+
for (const contentType of contentTypes) {
|
|
12449
|
+
current++;
|
|
12450
|
+
const entries = await fetchAllEntries(
|
|
12451
|
+
client,
|
|
12452
|
+
siteId,
|
|
12453
|
+
contentType,
|
|
12454
|
+
(item) => {
|
|
12455
|
+
onProgress?.({
|
|
12456
|
+
current,
|
|
12457
|
+
total,
|
|
12458
|
+
item: `Entries: ${item}`,
|
|
12459
|
+
contentType: "entries"
|
|
12460
|
+
});
|
|
12461
|
+
}
|
|
12462
|
+
);
|
|
12463
|
+
const typeDir = path9__namespace.join(entriesDir, contentType);
|
|
12464
|
+
ensureDir3(typeDir);
|
|
12465
|
+
const cacheFile = {
|
|
12466
|
+
entries,
|
|
12467
|
+
contentType,
|
|
12468
|
+
totalCount: entries.length,
|
|
12469
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12470
|
+
};
|
|
12471
|
+
const filename = `entries/${contentType}/all.json`;
|
|
12472
|
+
const filePath = path9__namespace.join(entriesDir, contentType, "all.json");
|
|
12473
|
+
totalSize += writeJsonFile2(filePath, cacheFile);
|
|
12474
|
+
files.push(filename);
|
|
12475
|
+
}
|
|
12476
|
+
return { files, size: totalSize };
|
|
12477
|
+
}
|
|
12478
|
+
async function prebuildNavigation(siteData, outputDir, onProgress) {
|
|
12479
|
+
const navDir = path9__namespace.join(outputDir, "navigation");
|
|
12480
|
+
ensureDir3(navDir);
|
|
12481
|
+
const files = [];
|
|
12482
|
+
let totalSize = 0;
|
|
12483
|
+
const menus = siteData.navigation || [];
|
|
12484
|
+
onProgress?.({
|
|
12485
|
+
current: 1,
|
|
12486
|
+
total: 1,
|
|
12487
|
+
item: "Navigation menus",
|
|
12488
|
+
contentType: "navigation"
|
|
12489
|
+
});
|
|
12490
|
+
const cacheFile = {
|
|
12491
|
+
menus: menus.map((menu) => ({
|
|
12492
|
+
identifier: menu.identifier,
|
|
12493
|
+
id: menu.id,
|
|
12494
|
+
name: menu.name,
|
|
12495
|
+
items: menu.items
|
|
12496
|
+
})),
|
|
12497
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12498
|
+
};
|
|
12499
|
+
const filename = "navigation/menus.json";
|
|
12500
|
+
const filePath = path9__namespace.join(navDir, "menus.json");
|
|
12501
|
+
totalSize += writeJsonFile2(filePath, cacheFile);
|
|
12502
|
+
files.push(filename);
|
|
12503
|
+
return { files, size: totalSize };
|
|
12504
|
+
}
|
|
12505
|
+
async function prebuildCache(options) {
|
|
12506
|
+
const {
|
|
12507
|
+
client,
|
|
12508
|
+
siteId,
|
|
12509
|
+
outputDir = DEFAULT_PREBUILD_DIR,
|
|
12510
|
+
include = ["site", "pages", "entries", "navigation"],
|
|
12511
|
+
contentTypes,
|
|
12512
|
+
onProgress
|
|
12513
|
+
} = options;
|
|
12514
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12515
|
+
const files = [];
|
|
12516
|
+
let totalSize = 0;
|
|
12517
|
+
const errors = [];
|
|
12518
|
+
let siteData = null;
|
|
12519
|
+
ensureDir3(outputDir);
|
|
12520
|
+
if (include.includes("site") || include.includes("pages") || include.includes("navigation")) {
|
|
12521
|
+
try {
|
|
12522
|
+
const result = await prebuildSite(client, siteId, outputDir);
|
|
12523
|
+
siteData = result.siteData;
|
|
12524
|
+
if (include.includes("site")) {
|
|
12525
|
+
files.push(...result.files);
|
|
12526
|
+
totalSize += result.size;
|
|
12527
|
+
}
|
|
12528
|
+
onProgress?.({ current: 1, total: 4, item: "Site data", contentType: "site" });
|
|
12529
|
+
} catch (error) {
|
|
12530
|
+
errors.push(`Site prebuild failed: ${error.message}`);
|
|
12531
|
+
}
|
|
12532
|
+
}
|
|
12533
|
+
if (include.includes("pages") && siteData) {
|
|
12534
|
+
try {
|
|
12535
|
+
const result = await prebuildPages(
|
|
12536
|
+
client,
|
|
12537
|
+
siteId,
|
|
12538
|
+
siteData.routes,
|
|
12539
|
+
outputDir,
|
|
12540
|
+
onProgress
|
|
12541
|
+
);
|
|
12542
|
+
files.push(...result.files);
|
|
12543
|
+
totalSize += result.size;
|
|
12544
|
+
} catch (error) {
|
|
12545
|
+
errors.push(`Pages prebuild failed: ${error.message}`);
|
|
12546
|
+
}
|
|
12547
|
+
}
|
|
12548
|
+
if (include.includes("entries")) {
|
|
12549
|
+
try {
|
|
12550
|
+
const typesToPrebuild = contentTypes || [];
|
|
12551
|
+
if (typesToPrebuild.length > 0) {
|
|
12552
|
+
const result = await prebuildEntries(
|
|
12553
|
+
client,
|
|
12554
|
+
siteId,
|
|
12555
|
+
typesToPrebuild,
|
|
12556
|
+
outputDir,
|
|
12557
|
+
onProgress
|
|
12558
|
+
);
|
|
12559
|
+
files.push(...result.files);
|
|
12560
|
+
totalSize += result.size;
|
|
12561
|
+
} else {
|
|
12562
|
+
console.warn("[Prebuild] No contentTypes provided - skipping entries prebuild");
|
|
12563
|
+
}
|
|
12564
|
+
} catch (error) {
|
|
12565
|
+
errors.push(`Entries prebuild failed: ${error.message}`);
|
|
12566
|
+
}
|
|
12567
|
+
}
|
|
12568
|
+
if (include.includes("navigation") && siteData) {
|
|
12569
|
+
try {
|
|
12570
|
+
const result = await prebuildNavigation(siteData, outputDir, onProgress);
|
|
12571
|
+
files.push(...result.files);
|
|
12572
|
+
totalSize += result.size;
|
|
12573
|
+
} catch (error) {
|
|
12574
|
+
errors.push(`Navigation prebuild failed: ${error.message}`);
|
|
12575
|
+
}
|
|
12576
|
+
}
|
|
12577
|
+
const keyToFile = {};
|
|
12578
|
+
if (files.includes("site.json")) {
|
|
12579
|
+
keyToFile[`site:${siteId}`] = "site.json";
|
|
12580
|
+
}
|
|
12581
|
+
for (const file of files) {
|
|
12582
|
+
if (file.startsWith("pages/") && file !== "pages/_index.json") {
|
|
12583
|
+
const filename = file.replace("pages/", "").replace(".json", "");
|
|
12584
|
+
const pagePath = filename === "_home" ? "/" : "/" + filename.replace(/-/g, "/");
|
|
12585
|
+
keyToFile[`page:${siteId}:${pagePath}:false`] = file;
|
|
12586
|
+
}
|
|
12587
|
+
if (file.startsWith("entries/") && file.endsWith("/all.json")) {
|
|
12588
|
+
const contentType = file.split("/")[1];
|
|
12589
|
+
keyToFile[`entries-all:${siteId}:${contentType}`] = file;
|
|
12590
|
+
}
|
|
12591
|
+
}
|
|
12592
|
+
const manifest = {
|
|
12593
|
+
version: MANIFEST_VERSION,
|
|
12594
|
+
generatedAt,
|
|
12595
|
+
siteId,
|
|
12596
|
+
sdkVersion: SDK_VERSION,
|
|
12597
|
+
keyToFile,
|
|
12598
|
+
includedTypes: include,
|
|
12599
|
+
fileCount: files.length,
|
|
12600
|
+
totalSize
|
|
12601
|
+
};
|
|
12602
|
+
const checksum = calculateChecksum(manifest);
|
|
12603
|
+
manifest.checksum = checksum;
|
|
12604
|
+
const manifestPath = path9__namespace.join(outputDir, MANIFEST_FILENAME);
|
|
12605
|
+
writeJsonFile2(manifestPath, manifest);
|
|
12606
|
+
return {
|
|
12607
|
+
success: errors.length === 0,
|
|
12608
|
+
outputDir,
|
|
12609
|
+
files,
|
|
12610
|
+
totalSize,
|
|
12611
|
+
generatedAt,
|
|
12612
|
+
checksum,
|
|
12613
|
+
errors: errors.length > 0 ? errors : void 0
|
|
12614
|
+
};
|
|
12615
|
+
}
|
|
12616
|
+
|
|
12617
|
+
// src/cli/commands/deploy.ts
|
|
12618
|
+
init_constants();
|
|
12619
|
+
async function loadDeployConfig() {
|
|
12620
|
+
const rawConfig = await loadConfigFile();
|
|
12621
|
+
const config3 = rawConfig;
|
|
12622
|
+
if (!config3.siteId) {
|
|
12623
|
+
throw new Error("siteId is required in riverbank.config.ts");
|
|
12624
|
+
}
|
|
12625
|
+
const contentTypes = config3.content?.contentTypes?.map((ct) => ct.key);
|
|
12626
|
+
return {
|
|
12627
|
+
siteId: config3.siteId,
|
|
12628
|
+
deploy: config3.deploy ?? {},
|
|
12629
|
+
contentTypes
|
|
12630
|
+
};
|
|
12631
|
+
}
|
|
12632
|
+
async function checkWorkingDirectoryClean(git, prebuildOutput) {
|
|
12633
|
+
const status = await git.status();
|
|
12634
|
+
const dirtyFiles = status.files.map((f) => f.path).filter((path13) => !path13.startsWith(prebuildOutput));
|
|
12635
|
+
return {
|
|
12636
|
+
clean: dirtyFiles.length === 0,
|
|
12637
|
+
dirtyFiles
|
|
12638
|
+
};
|
|
12639
|
+
}
|
|
12640
|
+
async function commitCacheIfChanged(git, prebuildOutput) {
|
|
12641
|
+
const status = await git.status();
|
|
12642
|
+
const cacheChanges = status.files.filter((f) => f.path.startsWith(prebuildOutput));
|
|
12643
|
+
if (cacheChanges.length === 0) {
|
|
12644
|
+
return false;
|
|
12645
|
+
}
|
|
12646
|
+
await git.add(prebuildOutput);
|
|
12647
|
+
const log = await git.log({ maxCount: 1 });
|
|
12648
|
+
const lastCommit = log.latest;
|
|
12649
|
+
const isLastCommitCache = lastCommit?.message.startsWith("chore: update prebuild cache");
|
|
12650
|
+
let isUnpushed = false;
|
|
12651
|
+
if (isLastCommitCache && lastCommit) {
|
|
12652
|
+
try {
|
|
12653
|
+
const branches = await git.branch(["-r", "--contains", lastCommit.hash]);
|
|
12654
|
+
isUnpushed = branches.all.length === 0;
|
|
12655
|
+
} catch (error) {
|
|
12656
|
+
if (process.env.DEBUG) {
|
|
12657
|
+
console.debug("[deploy] Could not check remote branches:", error);
|
|
12658
|
+
}
|
|
12659
|
+
isUnpushed = true;
|
|
12660
|
+
}
|
|
12661
|
+
}
|
|
12662
|
+
if (isLastCommitCache && isUnpushed) {
|
|
12663
|
+
await git.commit([], { "--amend": null, "--no-edit": null });
|
|
12664
|
+
} else {
|
|
12665
|
+
await git.commit("chore: update prebuild cache");
|
|
12666
|
+
}
|
|
12667
|
+
return true;
|
|
12668
|
+
}
|
|
12669
|
+
var deployCommand = new commander.Command("deploy").description("Deploy site with prebuild cache generation").option("--preview", "Preview deploy - skip prebuild cache generation").addHelpText("after", `
|
|
12670
|
+
Examples:
|
|
12671
|
+
$ riverbankcms deploy # Full deploy with cache generation
|
|
12672
|
+
$ riverbankcms deploy --preview # Deploy without regenerating cache
|
|
12673
|
+
|
|
12674
|
+
Configuration (riverbank.config.ts):
|
|
12675
|
+
deploy: {
|
|
12676
|
+
verifyCommand: 'pnpm verify', // Command to run before deploy
|
|
12677
|
+
prebuildOutput: '.riverbank-cache', // Prebuild output directory
|
|
12678
|
+
}
|
|
12679
|
+
|
|
12680
|
+
Workflow:
|
|
12681
|
+
1. Run verifyCommand (if configured)
|
|
12682
|
+
2. Check working directory is clean
|
|
12683
|
+
3. Generate prebuild cache (unless --preview)
|
|
12684
|
+
4. Commit cache changes (squashes consecutive unpushed commits)
|
|
12685
|
+
5. Push to remote
|
|
12686
|
+
|
|
12687
|
+
Notes:
|
|
12688
|
+
- Requires git repository with remote configured
|
|
12689
|
+
- CMS failure during prebuild continues with existing cache
|
|
12690
|
+
- Uses --remote environment variables for API access
|
|
12691
|
+
`).action(async (options, command) => {
|
|
12692
|
+
const { output } = getOutputContext(command);
|
|
12693
|
+
const git = simpleGit__default.default();
|
|
12694
|
+
try {
|
|
12695
|
+
if (!await git.checkIsRepo()) {
|
|
12696
|
+
return output.error("Not a git repository", {
|
|
12697
|
+
suggestion: "Run this command from your project root directory"
|
|
12698
|
+
});
|
|
12699
|
+
}
|
|
12700
|
+
output.info("Loading configuration...");
|
|
12701
|
+
const config3 = await loadDeployConfig();
|
|
12702
|
+
const prebuildOutput = config3.deploy.prebuildOutput ?? DEFAULT_PREBUILD_DIR;
|
|
12703
|
+
if (config3.deploy.verifyCommand) {
|
|
12704
|
+
output.info(`Running verification: ${config3.deploy.verifyCommand}`);
|
|
12705
|
+
try {
|
|
12706
|
+
child_process.execSync(config3.deploy.verifyCommand, { stdio: "inherit" });
|
|
12707
|
+
output.success("Verification passed");
|
|
12708
|
+
} catch {
|
|
12709
|
+
return output.error("Verification failed", {
|
|
12710
|
+
suggestion: "Fix the issues reported by the verify command and try again"
|
|
12711
|
+
});
|
|
12712
|
+
}
|
|
12713
|
+
} else {
|
|
12714
|
+
output.warn("No verifyCommand configured, skipping verification");
|
|
12715
|
+
}
|
|
12716
|
+
const { clean, dirtyFiles } = await checkWorkingDirectoryClean(git, prebuildOutput);
|
|
12717
|
+
if (!clean) {
|
|
12718
|
+
output.warn("Uncommitted changes detected:");
|
|
12719
|
+
dirtyFiles.slice(0, 5).forEach((f) => output.warn(` - ${f}`));
|
|
12720
|
+
if (dirtyFiles.length > 5) {
|
|
12721
|
+
output.warn(` ... and ${dirtyFiles.length - 5} more files`);
|
|
12722
|
+
}
|
|
12723
|
+
return output.error("Working directory not clean", {
|
|
12724
|
+
suggestion: "Commit or stash your changes before deploying"
|
|
12725
|
+
});
|
|
12726
|
+
}
|
|
12727
|
+
if (!options.preview) {
|
|
12728
|
+
output.info("Generating prebuild cache...");
|
|
12729
|
+
try {
|
|
12730
|
+
const env = loadEnvironment(true);
|
|
12731
|
+
const client = createRiverbankClient({
|
|
12732
|
+
apiKey: env.managementApiKey,
|
|
12733
|
+
baseUrl: env.dashboardUrl
|
|
12734
|
+
});
|
|
12735
|
+
const result = await prebuildCache({
|
|
12736
|
+
client,
|
|
12737
|
+
siteId: config3.siteId,
|
|
12738
|
+
outputDir: prebuildOutput,
|
|
12739
|
+
contentTypes: config3.contentTypes,
|
|
12740
|
+
onProgress: (p) => {
|
|
12741
|
+
output.info(` ${p.item}`);
|
|
12742
|
+
}
|
|
12743
|
+
});
|
|
12744
|
+
if (result.success) {
|
|
12745
|
+
output.success(`Prebuild complete: ${result.files.length} files (${formatBytes(result.totalSize)})`);
|
|
12746
|
+
} else {
|
|
12747
|
+
output.warn("Prebuild completed with errors:");
|
|
12748
|
+
result.errors?.forEach((e) => output.warn(` - ${e}`));
|
|
12749
|
+
}
|
|
12750
|
+
const committed = await commitCacheIfChanged(git, prebuildOutput);
|
|
12751
|
+
if (committed) {
|
|
12752
|
+
output.success("Cache committed");
|
|
12753
|
+
} else {
|
|
12754
|
+
output.info("Cache unchanged, no commit needed");
|
|
12755
|
+
}
|
|
12756
|
+
} catch (error) {
|
|
12757
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
12758
|
+
output.warn(`Prebuild failed: ${message}`);
|
|
12759
|
+
output.warn("Continuing with existing cache...");
|
|
12760
|
+
}
|
|
12761
|
+
} else {
|
|
12762
|
+
output.info("Skipping prebuild cache (--preview mode)");
|
|
12763
|
+
}
|
|
12764
|
+
output.info("Pushing to remote...");
|
|
12765
|
+
try {
|
|
12766
|
+
await git.push();
|
|
12767
|
+
output.success("Deploy triggered successfully!");
|
|
12768
|
+
} catch (error) {
|
|
12769
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
12770
|
+
return output.error(`Push failed: ${message}`, {
|
|
12771
|
+
suggestion: "Check your git remote configuration and permissions"
|
|
12772
|
+
});
|
|
12773
|
+
}
|
|
12774
|
+
} catch (error) {
|
|
12775
|
+
handleCommandError(error, output);
|
|
12776
|
+
}
|
|
12777
|
+
});
|
|
12778
|
+
function formatBytes(bytes) {
|
|
12779
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
12780
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
12781
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
12782
|
+
}
|
|
12783
|
+
|
|
8695
12784
|
// src/cli/index.ts
|
|
8696
12785
|
dotenv.config({ path: ".env.local" });
|
|
8697
12786
|
dotenv.config({ path: ".env" });
|
|
@@ -8729,6 +12818,7 @@ program.addCommand(deleteCommand);
|
|
|
8729
12818
|
program.addCommand(previewCommand);
|
|
8730
12819
|
program.addCommand(initDocsCommand);
|
|
8731
12820
|
program.addCommand(identifiersCommand);
|
|
12821
|
+
program.addCommand(deployCommand);
|
|
8732
12822
|
program.parse();
|
|
8733
12823
|
//# sourceMappingURL=index.js.map
|
|
8734
12824
|
//# sourceMappingURL=index.js.map
|