@riverbankcms/sdk 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +568 -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 +23 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +23 -1
- 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/usePage-BXjk8BhD.d.mts +6704 -0
- package/dist/client/usePage-BiOReg0_.d.ts +6704 -0
- package/dist/server/{Layout-Cc5HUXAH.d.mts → Layout-BClXUTsd.d.mts} +1 -1
- package/dist/server/{Layout-B-q2Py4v.d.ts → Layout-UXGjXv8M.d.ts} +1 -1
- package/dist/server/{chunk-4HIRA33Z.mjs → chunk-CMABGYGI.mjs} +24 -2
- package/dist/server/{chunk-6OSNCH4F.js.map → chunk-CMABGYGI.mjs.map} +1 -1
- package/dist/server/{chunk-6OSNCH4F.js → chunk-KA74YRK6.js} +24 -2
- package/dist/server/chunk-KA74YRK6.js.map +1 -0
- package/dist/server/{components-CU46ZkAv.d.mts → components-BmaJxgCV.d.mts} +3 -3
- package/dist/server/{components-DvozDwRN.d.ts → components-DppHY5oD.d.ts} +3 -3
- package/dist/server/components.d.mts +5 -5
- package/dist/server/components.d.ts +5 -5
- package/dist/server/data.d.mts +2 -2
- package/dist/server/data.d.ts +2 -2
- package/dist/server/{index-Q7RLMAQ6.d.mts → index-Bucs6UqG.d.mts} +1 -1
- package/dist/server/{index-CJfMXZQr.d.ts → index-Cp7tJuRt.d.ts} +1 -1
- package/dist/server/index.d.mts +24 -3
- package/dist/server/index.d.ts +24 -3
- package/dist/server/index.js +2 -2
- package/dist/server/index.mjs +1 -1
- package/dist/server/{loadContent-DgpSKWqY.d.mts → loadContent-BS-3wesN.d.mts} +3 -3
- package/dist/server/{loadContent-GPvUI1bN.d.ts → loadContent-Buvmudee.d.ts} +3 -3
- package/dist/server/{loadPage-DGnIK7s4.d.mts → loadPage-B8mQUUSo.d.mts} +2 -2
- package/dist/server/{loadPage-DW9WB-u9.d.ts → loadPage-DP3nrHBi.d.ts} +2 -2
- package/dist/server/metadata.d.mts +3 -3
- package/dist/server/metadata.d.ts +3 -3
- package/dist/server/navigation.d.mts +2 -2
- package/dist/server/navigation.d.ts +2 -2
- package/dist/server/rendering/server.d.mts +4 -4
- package/dist/server/rendering/server.d.ts +4 -4
- package/dist/server/rendering.d.mts +7 -7
- package/dist/server/rendering.d.ts +7 -7
- package/dist/server/routing.d.mts +3 -3
- package/dist/server/routing.d.ts +3 -3
- package/dist/server/server.d.mts +5 -5
- package/dist/server/server.d.ts +5 -5
- package/dist/server/server.js +2 -2
- package/dist/server/server.mjs +1 -1
- package/dist/server/{types-0f4PIlgx.d.mts → types-1cLz0vnq.d.mts} +1 -1
- package/dist/server/{types-kOQyCFXO.d.ts → types-BvcJU7zk.d.ts} +1 -1
- package/dist/server/{types-C28kMfa1.d.ts → types-CVykEqXN.d.ts} +35 -1
- package/dist/server/{types-DuzJZKJI.d.mts → types-Dsu9wsUh.d.mts} +35 -1
- package/package.json +3 -1
- package/dist/server/chunk-4HIRA33Z.mjs.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -7,7 +7,8 @@ var fs = require('fs');
|
|
|
7
7
|
var dotenv = require('dotenv');
|
|
8
8
|
var commander = require('commander');
|
|
9
9
|
var zod = require('zod');
|
|
10
|
-
var
|
|
10
|
+
var prompts = require('prompts');
|
|
11
|
+
var fs3 = require('fs/promises');
|
|
11
12
|
var readline = require('readline');
|
|
12
13
|
var equal = require('fast-deep-equal');
|
|
13
14
|
var os = require('os');
|
|
@@ -35,7 +36,8 @@ function _interopNamespace(e) {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
var path9__namespace = /*#__PURE__*/_interopNamespace(path9);
|
|
38
|
-
var
|
|
39
|
+
var prompts__default = /*#__PURE__*/_interopDefault(prompts);
|
|
40
|
+
var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
|
|
39
41
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
40
42
|
var equal__default = /*#__PURE__*/_interopDefault(equal);
|
|
41
43
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
@@ -5480,66 +5482,6 @@ Examples:
|
|
|
5480
5482
|
const dashboard = resolveDashboardUrl(options.dashboard);
|
|
5481
5483
|
return pushConfigAction({ ...options, dashboard });
|
|
5482
5484
|
});
|
|
5483
|
-
async function ensureDir(dirPath) {
|
|
5484
|
-
try {
|
|
5485
|
-
await fs5__namespace.mkdir(dirPath, { recursive: true });
|
|
5486
|
-
} catch (error) {
|
|
5487
|
-
if (error.code !== "EEXIST") {
|
|
5488
|
-
throw error;
|
|
5489
|
-
}
|
|
5490
|
-
}
|
|
5491
|
-
}
|
|
5492
|
-
async function writeJsonFile(filePath, data) {
|
|
5493
|
-
const content = JSON.stringify(data, null, 2) + "\n";
|
|
5494
|
-
await fs5__namespace.writeFile(filePath, content, "utf-8");
|
|
5495
|
-
}
|
|
5496
|
-
async function writeEntries(contentDir, pulledEntries) {
|
|
5497
|
-
const entriesDir = path9__namespace.join(contentDir, "entries");
|
|
5498
|
-
const metaDir = path9__namespace.join(contentDir, ".meta");
|
|
5499
|
-
await ensureDir(entriesDir);
|
|
5500
|
-
await ensureDir(metaDir);
|
|
5501
|
-
const { contentType, entries, meta } = pulledEntries;
|
|
5502
|
-
const entriesFile = {
|
|
5503
|
-
contentType,
|
|
5504
|
-
entries: entries.map((entry) => ({
|
|
5505
|
-
identifier: entry.identifier,
|
|
5506
|
-
data: entry.data,
|
|
5507
|
-
status: entry.status,
|
|
5508
|
-
hasUnpublishedChanges: entry.hasUnpublishedChanges,
|
|
5509
|
-
slug: entry.slug
|
|
5510
|
-
}))
|
|
5511
|
-
};
|
|
5512
|
-
const filePath = path9__namespace.join(entriesDir, `${contentType}.json`);
|
|
5513
|
-
await writeJsonFile(filePath, entriesFile);
|
|
5514
|
-
const metaPath = path9__namespace.join(metaDir, `${contentType}.json`);
|
|
5515
|
-
await writeJsonFile(metaPath, meta);
|
|
5516
|
-
return { filePath, metaPath };
|
|
5517
|
-
}
|
|
5518
|
-
async function writePages(contentDir, pulledPages) {
|
|
5519
|
-
const pagesDir = path9__namespace.join(contentDir, "pages");
|
|
5520
|
-
await ensureDir(pagesDir);
|
|
5521
|
-
const filePaths = [];
|
|
5522
|
-
for (const page of pulledPages.pages) {
|
|
5523
|
-
const filePath = path9__namespace.join(pagesDir, `${page.identifier}.json`);
|
|
5524
|
-
await writeJsonFile(filePath, page);
|
|
5525
|
-
filePaths.push(filePath);
|
|
5526
|
-
}
|
|
5527
|
-
return filePaths;
|
|
5528
|
-
}
|
|
5529
|
-
async function writeNavigation(contentDir, pulledNavigation) {
|
|
5530
|
-
await ensureDir(contentDir);
|
|
5531
|
-
const filePath = path9__namespace.join(contentDir, "navigation.json");
|
|
5532
|
-
await writeJsonFile(filePath, {
|
|
5533
|
-
menus: pulledNavigation.menus
|
|
5534
|
-
});
|
|
5535
|
-
return filePath;
|
|
5536
|
-
}
|
|
5537
|
-
async function writeSettings(contentDir, pulledSettings) {
|
|
5538
|
-
await ensureDir(contentDir);
|
|
5539
|
-
const filePath = path9__namespace.join(contentDir, "settings.json");
|
|
5540
|
-
await writeJsonFile(filePath, pulledSettings.settings);
|
|
5541
|
-
return filePath;
|
|
5542
|
-
}
|
|
5543
5485
|
|
|
5544
5486
|
// src/client/management/http.ts
|
|
5545
5487
|
var ManagementApiError = class extends Error {
|
|
@@ -5828,12 +5770,15 @@ function createPullOperations(http) {
|
|
|
5828
5770
|
http.get("/pull/settings")
|
|
5829
5771
|
]);
|
|
5830
5772
|
return {
|
|
5831
|
-
entries: entriesResult
|
|
5832
|
-
pages: pagesResult
|
|
5833
|
-
navigation: navigationResult
|
|
5834
|
-
settings: settingsResult
|
|
5773
|
+
entries: entriesResult?.entries || {},
|
|
5774
|
+
pages: pagesResult?.pages || [],
|
|
5775
|
+
navigation: navigationResult?.menus || [],
|
|
5776
|
+
settings: settingsResult?.settings || {},
|
|
5835
5777
|
meta: {
|
|
5836
|
-
pulledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5778
|
+
pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5779
|
+
entries: entriesResult?.meta?.entries,
|
|
5780
|
+
truncated: entriesResult?.meta?.truncated,
|
|
5781
|
+
truncationMessage: entriesResult?.meta?.truncationMessage
|
|
5837
5782
|
}
|
|
5838
5783
|
};
|
|
5839
5784
|
}
|
|
@@ -5849,6 +5794,16 @@ function createPreviewOperations(http) {
|
|
|
5849
5794
|
};
|
|
5850
5795
|
}
|
|
5851
5796
|
|
|
5797
|
+
// src/client/management/identifiers.ts
|
|
5798
|
+
function createIdentifiersOperations(http) {
|
|
5799
|
+
return {
|
|
5800
|
+
async backfill() {
|
|
5801
|
+
const response = await http.post("/identifiers/backfill");
|
|
5802
|
+
return response.data;
|
|
5803
|
+
}
|
|
5804
|
+
};
|
|
5805
|
+
}
|
|
5806
|
+
|
|
5852
5807
|
// src/client/management/index.ts
|
|
5853
5808
|
function createManagementClient(config3) {
|
|
5854
5809
|
if (!config3.dashboardUrl) {
|
|
@@ -5879,7 +5834,8 @@ function createManagementClient(config3) {
|
|
|
5879
5834
|
navigation: createNavigationOperations(http),
|
|
5880
5835
|
settings: createSettingsOperations(http),
|
|
5881
5836
|
pull: createPullOperations(http),
|
|
5882
|
-
preview: createPreviewOperations(http)
|
|
5837
|
+
preview: createPreviewOperations(http),
|
|
5838
|
+
identifiers: createIdentifiersOperations(http)
|
|
5883
5839
|
};
|
|
5884
5840
|
}
|
|
5885
5841
|
|
|
@@ -6063,6 +6019,18 @@ function formatError(error) {
|
|
|
6063
6019
|
}
|
|
6064
6020
|
|
|
6065
6021
|
// src/cli/helpers.ts
|
|
6022
|
+
function mapEntryForOutput(entry) {
|
|
6023
|
+
const result = {
|
|
6024
|
+
identifier: entry.identifier,
|
|
6025
|
+
data: entry.data,
|
|
6026
|
+
status: entry.status,
|
|
6027
|
+
hasUnpublishedChanges: entry.hasUnpublishedChanges
|
|
6028
|
+
};
|
|
6029
|
+
if (entry.slug !== void 0) {
|
|
6030
|
+
result.slug = entry.slug;
|
|
6031
|
+
}
|
|
6032
|
+
return result;
|
|
6033
|
+
}
|
|
6066
6034
|
function parsePaginationOptions(options) {
|
|
6067
6035
|
return {
|
|
6068
6036
|
limit: parseInt(options.limit ?? "20", 10),
|
|
@@ -6075,7 +6043,7 @@ function formatDateShort(isoDate) {
|
|
|
6075
6043
|
async function parseJsonData(options) {
|
|
6076
6044
|
if (options.file) {
|
|
6077
6045
|
const filePath = path9__namespace.resolve(options.file);
|
|
6078
|
-
const content = await
|
|
6046
|
+
const content = await fs3__namespace.readFile(filePath, "utf-8");
|
|
6079
6047
|
return JSON.parse(content);
|
|
6080
6048
|
}
|
|
6081
6049
|
if (options.data) {
|
|
@@ -6280,20 +6248,253 @@ function createListCommand(config3) {
|
|
|
6280
6248
|
);
|
|
6281
6249
|
}
|
|
6282
6250
|
|
|
6251
|
+
// src/cli/content/writer.ts
|
|
6252
|
+
async function ensureDir(dirPath) {
|
|
6253
|
+
try {
|
|
6254
|
+
await fs3__namespace.mkdir(dirPath, { recursive: true });
|
|
6255
|
+
} catch (error) {
|
|
6256
|
+
if (error.code !== "EEXIST") {
|
|
6257
|
+
throw error;
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6260
|
+
}
|
|
6261
|
+
async function writeJsonFile(filePath, data) {
|
|
6262
|
+
const content = JSON.stringify(data, null, 2) + "\n";
|
|
6263
|
+
await fs3__namespace.writeFile(filePath, content, "utf-8");
|
|
6264
|
+
}
|
|
6265
|
+
async function writeEntries(contentDir, pulledEntries) {
|
|
6266
|
+
const entriesDir = path9__namespace.join(contentDir, "entries");
|
|
6267
|
+
const metaDir = path9__namespace.join(contentDir, ".meta");
|
|
6268
|
+
await ensureDir(entriesDir);
|
|
6269
|
+
await ensureDir(metaDir);
|
|
6270
|
+
const { contentType, entries, meta } = pulledEntries;
|
|
6271
|
+
const entriesFile = {
|
|
6272
|
+
contentType,
|
|
6273
|
+
entries: entries.map(mapEntryForOutput)
|
|
6274
|
+
};
|
|
6275
|
+
const filePath = path9__namespace.join(entriesDir, `${contentType}.json`);
|
|
6276
|
+
await writeJsonFile(filePath, entriesFile);
|
|
6277
|
+
const metaPath = path9__namespace.join(metaDir, `${contentType}.json`);
|
|
6278
|
+
await writeJsonFile(metaPath, meta);
|
|
6279
|
+
return { filePath, metaPath };
|
|
6280
|
+
}
|
|
6281
|
+
async function writePages(contentDir, pulledPages) {
|
|
6282
|
+
const pagesDir = path9__namespace.join(contentDir, "pages");
|
|
6283
|
+
const metaDir = path9__namespace.join(contentDir, ".meta");
|
|
6284
|
+
await ensureDir(pagesDir);
|
|
6285
|
+
await ensureDir(metaDir);
|
|
6286
|
+
const filePaths = [];
|
|
6287
|
+
const pagesMeta = {};
|
|
6288
|
+
for (const page of pulledPages.pages) {
|
|
6289
|
+
const filePath = path9__namespace.join(pagesDir, `${page.identifier}.json`);
|
|
6290
|
+
await writeJsonFile(filePath, page);
|
|
6291
|
+
filePaths.push(filePath);
|
|
6292
|
+
pagesMeta[page.identifier] = {
|
|
6293
|
+
createdAt: page.createdAt,
|
|
6294
|
+
updatedAt: page.updatedAt
|
|
6295
|
+
};
|
|
6296
|
+
}
|
|
6297
|
+
const metaPath = path9__namespace.join(metaDir, "pages.json");
|
|
6298
|
+
await writeJsonFile(metaPath, {
|
|
6299
|
+
pulledAt: pulledPages.meta.pulledAt,
|
|
6300
|
+
pages: pagesMeta
|
|
6301
|
+
});
|
|
6302
|
+
return { filePaths, metaPath };
|
|
6303
|
+
}
|
|
6304
|
+
async function writeNavigation(contentDir, pulledNavigation) {
|
|
6305
|
+
const metaDir = path9__namespace.join(contentDir, ".meta");
|
|
6306
|
+
await ensureDir(contentDir);
|
|
6307
|
+
await ensureDir(metaDir);
|
|
6308
|
+
const filePath = path9__namespace.join(contentDir, "navigation.json");
|
|
6309
|
+
await writeJsonFile(filePath, {
|
|
6310
|
+
menus: pulledNavigation.menus
|
|
6311
|
+
});
|
|
6312
|
+
const menusMeta = {};
|
|
6313
|
+
for (const menu of pulledNavigation.menus) {
|
|
6314
|
+
menusMeta[menu.name] = {
|
|
6315
|
+
createdAt: menu.createdAt,
|
|
6316
|
+
updatedAt: menu.updatedAt
|
|
6317
|
+
};
|
|
6318
|
+
}
|
|
6319
|
+
const metaPath = path9__namespace.join(metaDir, "navigation.json");
|
|
6320
|
+
await writeJsonFile(metaPath, {
|
|
6321
|
+
pulledAt: pulledNavigation.meta.pulledAt,
|
|
6322
|
+
menus: menusMeta
|
|
6323
|
+
});
|
|
6324
|
+
return { filePath, metaPath };
|
|
6325
|
+
}
|
|
6326
|
+
async function writeSettings(contentDir, pulledSettings) {
|
|
6327
|
+
await ensureDir(contentDir);
|
|
6328
|
+
const filePath = path9__namespace.join(contentDir, "settings.json");
|
|
6329
|
+
await writeJsonFile(filePath, pulledSettings.settings);
|
|
6330
|
+
return filePath;
|
|
6331
|
+
}
|
|
6332
|
+
async function fileExists(filePath) {
|
|
6333
|
+
try {
|
|
6334
|
+
await fs3__namespace.access(filePath);
|
|
6335
|
+
return true;
|
|
6336
|
+
} catch {
|
|
6337
|
+
return false;
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
6340
|
+
async function readJsonFile(filePath) {
|
|
6341
|
+
const content = await fs3__namespace.readFile(filePath, "utf-8");
|
|
6342
|
+
return JSON.parse(content);
|
|
6343
|
+
}
|
|
6344
|
+
async function listFiles(dirPath, extension) {
|
|
6345
|
+
try {
|
|
6346
|
+
const files = await fs3__namespace.readdir(dirPath);
|
|
6347
|
+
return files.filter((f) => f.endsWith(extension)).map((f) => path9__namespace.join(dirPath, f));
|
|
6348
|
+
} catch {
|
|
6349
|
+
return [];
|
|
6350
|
+
}
|
|
6351
|
+
}
|
|
6352
|
+
async function readEntries(contentDir, contentType) {
|
|
6353
|
+
const entriesDir = path9__namespace.join(contentDir, "entries");
|
|
6354
|
+
const result = /* @__PURE__ */ new Map();
|
|
6355
|
+
{
|
|
6356
|
+
const files = await listFiles(entriesDir, ".json");
|
|
6357
|
+
for (const filePath of files) {
|
|
6358
|
+
try {
|
|
6359
|
+
const file = await readJsonFile(filePath);
|
|
6360
|
+
result.set(file.contentType, file.entries);
|
|
6361
|
+
} catch (error) {
|
|
6362
|
+
console.warn(`Warning: Could not parse ${filePath}:`, error);
|
|
6363
|
+
}
|
|
6364
|
+
}
|
|
6365
|
+
}
|
|
6366
|
+
return result;
|
|
6367
|
+
}
|
|
6368
|
+
async function readPages(contentDir) {
|
|
6369
|
+
const pagesDir = path9__namespace.join(contentDir, "pages");
|
|
6370
|
+
const pages = [];
|
|
6371
|
+
const files = await listFiles(pagesDir, ".json");
|
|
6372
|
+
for (const filePath of files) {
|
|
6373
|
+
try {
|
|
6374
|
+
const page = await readJsonFile(filePath);
|
|
6375
|
+
pages.push(page);
|
|
6376
|
+
} catch (error) {
|
|
6377
|
+
console.warn(`Warning: Could not parse ${filePath}:`, error);
|
|
6378
|
+
}
|
|
6379
|
+
}
|
|
6380
|
+
return pages;
|
|
6381
|
+
}
|
|
6382
|
+
async function readNavigation(contentDir) {
|
|
6383
|
+
const filePath = path9__namespace.join(contentDir, "navigation.json");
|
|
6384
|
+
if (!await fileExists(filePath)) {
|
|
6385
|
+
return null;
|
|
6386
|
+
}
|
|
6387
|
+
return readJsonFile(filePath);
|
|
6388
|
+
}
|
|
6389
|
+
async function readSettings(contentDir) {
|
|
6390
|
+
const filePath = path9__namespace.join(contentDir, "settings.json");
|
|
6391
|
+
if (!await fileExists(filePath)) {
|
|
6392
|
+
return null;
|
|
6393
|
+
}
|
|
6394
|
+
return readJsonFile(filePath);
|
|
6395
|
+
}
|
|
6396
|
+
async function readAllContent(contentDir) {
|
|
6397
|
+
const [entries, pages, navigation, settings] = await Promise.all([
|
|
6398
|
+
readEntries(contentDir),
|
|
6399
|
+
readPages(contentDir),
|
|
6400
|
+
readNavigation(contentDir),
|
|
6401
|
+
readSettings(contentDir)
|
|
6402
|
+
]);
|
|
6403
|
+
return { entries, pages, navigation, settings };
|
|
6404
|
+
}
|
|
6405
|
+
async function contentDirExists(contentDir) {
|
|
6406
|
+
return fileExists(contentDir);
|
|
6407
|
+
}
|
|
6408
|
+
async function getContentSummary(contentDir) {
|
|
6409
|
+
const content = await readAllContent(contentDir);
|
|
6410
|
+
const entryTypes = Array.from(content.entries.keys());
|
|
6411
|
+
let totalEntries = 0;
|
|
6412
|
+
for (const entries of content.entries.values()) {
|
|
6413
|
+
totalEntries += entries.length;
|
|
6414
|
+
}
|
|
6415
|
+
return {
|
|
6416
|
+
hasEntries: content.entries.size > 0,
|
|
6417
|
+
entryTypes,
|
|
6418
|
+
totalEntries,
|
|
6419
|
+
hasPages: content.pages.length > 0,
|
|
6420
|
+
pageCount: content.pages.length,
|
|
6421
|
+
hasNavigation: content.navigation !== null && content.navigation.menus.length > 0,
|
|
6422
|
+
menuCount: content.navigation?.menus.length ?? 0,
|
|
6423
|
+
hasSettings: content.settings !== null
|
|
6424
|
+
};
|
|
6425
|
+
}
|
|
6426
|
+
async function readEntriesMeta(contentDir, contentType) {
|
|
6427
|
+
const metaPath = path9__namespace.join(contentDir, ".meta", `${contentType}.json`);
|
|
6428
|
+
try {
|
|
6429
|
+
const content = await fs3__namespace.readFile(metaPath, "utf-8");
|
|
6430
|
+
return JSON.parse(content);
|
|
6431
|
+
} catch (error) {
|
|
6432
|
+
if (error.code === "ENOENT") {
|
|
6433
|
+
return null;
|
|
6434
|
+
}
|
|
6435
|
+
throw error;
|
|
6436
|
+
}
|
|
6437
|
+
}
|
|
6438
|
+
async function readAllMeta(contentDir) {
|
|
6439
|
+
const metaDir = path9__namespace.join(contentDir, ".meta");
|
|
6440
|
+
const metaMap = /* @__PURE__ */ new Map();
|
|
6441
|
+
try {
|
|
6442
|
+
const files = await fs3__namespace.readdir(metaDir);
|
|
6443
|
+
for (const file of files) {
|
|
6444
|
+
if (file.endsWith(".json") && file !== "pages.json" && file !== "navigation.json") {
|
|
6445
|
+
const contentType = file.replace(".json", "");
|
|
6446
|
+
const meta = await readEntriesMeta(contentDir, contentType);
|
|
6447
|
+
if (meta) metaMap.set(contentType, meta);
|
|
6448
|
+
}
|
|
6449
|
+
}
|
|
6450
|
+
} catch (error) {
|
|
6451
|
+
if (error.code !== "ENOENT") throw error;
|
|
6452
|
+
}
|
|
6453
|
+
return metaMap;
|
|
6454
|
+
}
|
|
6455
|
+
async function readPagesMeta(contentDir) {
|
|
6456
|
+
const metaPath = path9__namespace.join(contentDir, ".meta", "pages.json");
|
|
6457
|
+
try {
|
|
6458
|
+
const content = await fs3__namespace.readFile(metaPath, "utf-8");
|
|
6459
|
+
return JSON.parse(content);
|
|
6460
|
+
} catch (error) {
|
|
6461
|
+
if (error.code === "ENOENT") {
|
|
6462
|
+
return null;
|
|
6463
|
+
}
|
|
6464
|
+
throw error;
|
|
6465
|
+
}
|
|
6466
|
+
}
|
|
6467
|
+
async function readNavigationMeta(contentDir) {
|
|
6468
|
+
const metaPath = path9__namespace.join(contentDir, ".meta", "navigation.json");
|
|
6469
|
+
try {
|
|
6470
|
+
const content = await fs3__namespace.readFile(metaPath, "utf-8");
|
|
6471
|
+
return JSON.parse(content);
|
|
6472
|
+
} catch (error) {
|
|
6473
|
+
if (error.code === "ENOENT") {
|
|
6474
|
+
return null;
|
|
6475
|
+
}
|
|
6476
|
+
throw error;
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6479
|
+
|
|
6283
6480
|
// src/cli/commands/pull.ts
|
|
6284
6481
|
var DEFAULT_PAGE_LIMIT = 500;
|
|
6285
6482
|
async function pullEntriesWithPagination(client, contentType, output) {
|
|
6286
6483
|
const allEntries = [];
|
|
6484
|
+
const aggregatedMeta = {};
|
|
6287
6485
|
let page = 1;
|
|
6288
6486
|
let hasMore = true;
|
|
6289
|
-
let
|
|
6487
|
+
let pulledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6290
6488
|
while (hasMore) {
|
|
6291
6489
|
const result = await client.pull.entries(contentType, {
|
|
6292
6490
|
page,
|
|
6293
6491
|
limit: DEFAULT_PAGE_LIMIT
|
|
6294
6492
|
});
|
|
6295
6493
|
allEntries.push(...result.entries);
|
|
6296
|
-
|
|
6494
|
+
if (result.meta.entries) {
|
|
6495
|
+
Object.assign(aggregatedMeta, result.meta.entries);
|
|
6496
|
+
}
|
|
6497
|
+
pulledAt = result.meta.pulledAt;
|
|
6297
6498
|
hasMore = result.pagination.hasMore;
|
|
6298
6499
|
if (hasMore) {
|
|
6299
6500
|
output.info(`Fetched ${allEntries.length} entries (page ${page})...`);
|
|
@@ -6303,7 +6504,7 @@ async function pullEntriesWithPagination(client, contentType, output) {
|
|
|
6303
6504
|
return {
|
|
6304
6505
|
contentType,
|
|
6305
6506
|
entries: allEntries,
|
|
6306
|
-
meta:
|
|
6507
|
+
meta: { pulledAt, entries: aggregatedMeta },
|
|
6307
6508
|
pagination: {
|
|
6308
6509
|
page: 1,
|
|
6309
6510
|
limit: allEntries.length,
|
|
@@ -6312,20 +6513,21 @@ async function pullEntriesWithPagination(client, contentType, output) {
|
|
|
6312
6513
|
}
|
|
6313
6514
|
};
|
|
6314
6515
|
}
|
|
6315
|
-
async function writeAllEntries(contentDir, entriesByType, pulledAt) {
|
|
6516
|
+
async function writeAllEntries(contentDir, entriesByType, pulledAt, entriesMeta) {
|
|
6316
6517
|
let totalCount = 0;
|
|
6317
6518
|
const files = [];
|
|
6318
6519
|
for (const [contentType, entries] of Object.entries(entriesByType)) {
|
|
6520
|
+
const ctMeta = {};
|
|
6521
|
+
for (const entry of entries) {
|
|
6522
|
+
const key = `${contentType}:${entry.identifier}`;
|
|
6523
|
+
if (entriesMeta?.[key]) {
|
|
6524
|
+
ctMeta[entry.identifier] = entriesMeta[key];
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6319
6527
|
const { filePath } = await writeEntries(contentDir, {
|
|
6320
6528
|
contentType,
|
|
6321
|
-
entries
|
|
6322
|
-
|
|
6323
|
-
data: e.data,
|
|
6324
|
-
status: e.status,
|
|
6325
|
-
hasUnpublishedChanges: e.hasUnpublishedChanges,
|
|
6326
|
-
slug: e.slug
|
|
6327
|
-
})),
|
|
6328
|
-
meta: { pulledAt, entries: {} },
|
|
6529
|
+
entries,
|
|
6530
|
+
meta: { pulledAt, entries: ctMeta },
|
|
6329
6531
|
pagination: { limit: entries.length, total: entries.length}
|
|
6330
6532
|
});
|
|
6331
6533
|
totalCount += entries.length;
|
|
@@ -6335,16 +6537,14 @@ async function writeAllEntries(contentDir, entriesByType, pulledAt) {
|
|
|
6335
6537
|
}
|
|
6336
6538
|
async function fetchAllContentPaginated(client, contentTypes, output) {
|
|
6337
6539
|
const allEntries = {};
|
|
6540
|
+
const allMeta = {};
|
|
6338
6541
|
for (const contentType of contentTypes) {
|
|
6339
6542
|
output.info(`Fetching ${contentType} entries...`);
|
|
6340
6543
|
const result = await pullEntriesWithPagination(client, contentType, output);
|
|
6341
|
-
allEntries[contentType] = result.entries.map(
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
hasUnpublishedChanges: e.hasUnpublishedChanges,
|
|
6346
|
-
slug: e.slug
|
|
6347
|
-
}));
|
|
6544
|
+
allEntries[contentType] = result.entries.map(mapEntryForOutput);
|
|
6545
|
+
for (const [id, meta] of Object.entries(result.meta.entries || {})) {
|
|
6546
|
+
allMeta[`${contentType}:${id}`] = meta;
|
|
6547
|
+
}
|
|
6348
6548
|
output.info(` ${result.entries.length} entries`);
|
|
6349
6549
|
}
|
|
6350
6550
|
output.info("Fetching pages...");
|
|
@@ -6358,10 +6558,10 @@ async function fetchAllContentPaginated(client, contentTypes, output) {
|
|
|
6358
6558
|
pages: pagesResult.pages,
|
|
6359
6559
|
navigation: navigationResult.menus,
|
|
6360
6560
|
settings: settingsResult.settings,
|
|
6361
|
-
meta: { pulledAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
6561
|
+
meta: { pulledAt: (/* @__PURE__ */ new Date()).toISOString(), entries: allMeta }
|
|
6362
6562
|
};
|
|
6363
6563
|
}
|
|
6364
|
-
var pullCommand = new commander.Command("pull").description("Pull content from CMS").argument("[scope]", "What to pull: entries, pages, navigation, settings, or all (default)").argument("[type]", 'Content type (when scope is "entries")').option("--output <dir>", "Output directory", "./content").addHelpText("after", `
|
|
6564
|
+
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)").addHelpText("after", `
|
|
6365
6565
|
Examples:
|
|
6366
6566
|
$ riverbankcms pull # Pull all content
|
|
6367
6567
|
$ riverbankcms pull --remote # Pull from production
|
|
@@ -6376,6 +6576,24 @@ Examples:
|
|
|
6376
6576
|
async (scope, type, options, command) => {
|
|
6377
6577
|
const { output, client } = createCommandContext(command);
|
|
6378
6578
|
const contentDir = path9__namespace.resolve(options.output ?? "./content");
|
|
6579
|
+
if (await contentDirExists(contentDir) && !options.force && !options.yes) {
|
|
6580
|
+
if (!process.stdin.isTTY) {
|
|
6581
|
+
output.error("Content directory already exists and --yes not specified", {
|
|
6582
|
+
suggestion: "Use --yes or --force to overwrite in non-interactive mode"
|
|
6583
|
+
});
|
|
6584
|
+
return;
|
|
6585
|
+
}
|
|
6586
|
+
const response = await prompts__default.default({
|
|
6587
|
+
type: "confirm",
|
|
6588
|
+
name: "overwrite",
|
|
6589
|
+
message: `Content directory '${path9__namespace.basename(contentDir)}' already exists. Overwrite?`,
|
|
6590
|
+
initial: false
|
|
6591
|
+
});
|
|
6592
|
+
if (!response.overwrite) {
|
|
6593
|
+
output.info("Aborted.");
|
|
6594
|
+
return;
|
|
6595
|
+
}
|
|
6596
|
+
}
|
|
6379
6597
|
const pullScope = scope?.toLowerCase() ?? "all";
|
|
6380
6598
|
switch (pullScope) {
|
|
6381
6599
|
case "entries": {
|
|
@@ -6400,7 +6618,8 @@ Examples:
|
|
|
6400
6618
|
const { totalCount, files } = await writeAllEntries(
|
|
6401
6619
|
contentDir,
|
|
6402
6620
|
result.entries,
|
|
6403
|
-
result.meta.pulledAt
|
|
6621
|
+
result.meta.pulledAt,
|
|
6622
|
+
result.meta.entries
|
|
6404
6623
|
);
|
|
6405
6624
|
output.success(`Pulled ${totalCount} entries across ${Object.keys(result.entries).length} content types`, {
|
|
6406
6625
|
files
|
|
@@ -6411,15 +6630,15 @@ Examples:
|
|
|
6411
6630
|
case "pages": {
|
|
6412
6631
|
output.info("Pulling pages");
|
|
6413
6632
|
const result = await client.pull.pages();
|
|
6414
|
-
const
|
|
6415
|
-
output.success(`Pulled ${result.pages.length} pages`, { files });
|
|
6633
|
+
const { filePaths, metaPath } = await writePages(contentDir, result);
|
|
6634
|
+
output.success(`Pulled ${result.pages.length} pages`, { files: filePaths, meta: metaPath });
|
|
6416
6635
|
break;
|
|
6417
6636
|
}
|
|
6418
6637
|
case "navigation": {
|
|
6419
6638
|
output.info("Pulling navigation menus");
|
|
6420
6639
|
const result = await client.pull.navigation();
|
|
6421
|
-
const filePath = await writeNavigation(contentDir, result);
|
|
6422
|
-
output.success(`Pulled ${result.menus.length} navigation menus`, { file: filePath });
|
|
6640
|
+
const { filePath, metaPath } = await writeNavigation(contentDir, result);
|
|
6641
|
+
output.success(`Pulled ${result.menus.length} navigation menus`, { file: filePath, meta: metaPath });
|
|
6423
6642
|
break;
|
|
6424
6643
|
}
|
|
6425
6644
|
case "settings": {
|
|
@@ -6442,7 +6661,8 @@ Examples:
|
|
|
6442
6661
|
const { totalCount: totalEntries } = await writeAllEntries(
|
|
6443
6662
|
contentDir,
|
|
6444
6663
|
result.entries,
|
|
6445
|
-
result.meta.pulledAt
|
|
6664
|
+
result.meta.pulledAt,
|
|
6665
|
+
result.meta.entries
|
|
6446
6666
|
);
|
|
6447
6667
|
await writePages(contentDir, { pages: result.pages, meta: result.meta });
|
|
6448
6668
|
await writeNavigation(contentDir, { menus: result.navigation, meta: result.meta });
|
|
@@ -6489,100 +6709,6 @@ async function loadCliConfig(configPath) {
|
|
|
6489
6709
|
throw error;
|
|
6490
6710
|
}
|
|
6491
6711
|
}
|
|
6492
|
-
async function fileExists(filePath) {
|
|
6493
|
-
try {
|
|
6494
|
-
await fs5__namespace.access(filePath);
|
|
6495
|
-
return true;
|
|
6496
|
-
} catch {
|
|
6497
|
-
return false;
|
|
6498
|
-
}
|
|
6499
|
-
}
|
|
6500
|
-
async function readJsonFile(filePath) {
|
|
6501
|
-
const content = await fs5__namespace.readFile(filePath, "utf-8");
|
|
6502
|
-
return JSON.parse(content);
|
|
6503
|
-
}
|
|
6504
|
-
async function listFiles(dirPath, extension) {
|
|
6505
|
-
try {
|
|
6506
|
-
const files = await fs5__namespace.readdir(dirPath);
|
|
6507
|
-
return files.filter((f) => f.endsWith(extension)).map((f) => path9__namespace.join(dirPath, f));
|
|
6508
|
-
} catch {
|
|
6509
|
-
return [];
|
|
6510
|
-
}
|
|
6511
|
-
}
|
|
6512
|
-
async function readEntries(contentDir, contentType) {
|
|
6513
|
-
const entriesDir = path9__namespace.join(contentDir, "entries");
|
|
6514
|
-
const result = /* @__PURE__ */ new Map();
|
|
6515
|
-
{
|
|
6516
|
-
const files = await listFiles(entriesDir, ".json");
|
|
6517
|
-
for (const filePath of files) {
|
|
6518
|
-
try {
|
|
6519
|
-
const file = await readJsonFile(filePath);
|
|
6520
|
-
result.set(file.contentType, file.entries);
|
|
6521
|
-
} catch (error) {
|
|
6522
|
-
console.warn(`Warning: Could not parse ${filePath}:`, error);
|
|
6523
|
-
}
|
|
6524
|
-
}
|
|
6525
|
-
}
|
|
6526
|
-
return result;
|
|
6527
|
-
}
|
|
6528
|
-
async function readPages(contentDir) {
|
|
6529
|
-
const pagesDir = path9__namespace.join(contentDir, "pages");
|
|
6530
|
-
const pages = [];
|
|
6531
|
-
const files = await listFiles(pagesDir, ".json");
|
|
6532
|
-
for (const filePath of files) {
|
|
6533
|
-
try {
|
|
6534
|
-
const page = await readJsonFile(filePath);
|
|
6535
|
-
pages.push(page);
|
|
6536
|
-
} catch (error) {
|
|
6537
|
-
console.warn(`Warning: Could not parse ${filePath}:`, error);
|
|
6538
|
-
}
|
|
6539
|
-
}
|
|
6540
|
-
return pages;
|
|
6541
|
-
}
|
|
6542
|
-
async function readNavigation(contentDir) {
|
|
6543
|
-
const filePath = path9__namespace.join(contentDir, "navigation.json");
|
|
6544
|
-
if (!await fileExists(filePath)) {
|
|
6545
|
-
return null;
|
|
6546
|
-
}
|
|
6547
|
-
return readJsonFile(filePath);
|
|
6548
|
-
}
|
|
6549
|
-
async function readSettings(contentDir) {
|
|
6550
|
-
const filePath = path9__namespace.join(contentDir, "settings.json");
|
|
6551
|
-
if (!await fileExists(filePath)) {
|
|
6552
|
-
return null;
|
|
6553
|
-
}
|
|
6554
|
-
return readJsonFile(filePath);
|
|
6555
|
-
}
|
|
6556
|
-
async function readAllContent(contentDir) {
|
|
6557
|
-
const [entries, pages, navigation, settings] = await Promise.all([
|
|
6558
|
-
readEntries(contentDir),
|
|
6559
|
-
readPages(contentDir),
|
|
6560
|
-
readNavigation(contentDir),
|
|
6561
|
-
readSettings(contentDir)
|
|
6562
|
-
]);
|
|
6563
|
-
return { entries, pages, navigation, settings };
|
|
6564
|
-
}
|
|
6565
|
-
async function contentDirExists(contentDir) {
|
|
6566
|
-
return fileExists(contentDir);
|
|
6567
|
-
}
|
|
6568
|
-
async function getContentSummary(contentDir) {
|
|
6569
|
-
const content = await readAllContent(contentDir);
|
|
6570
|
-
const entryTypes = Array.from(content.entries.keys());
|
|
6571
|
-
let totalEntries = 0;
|
|
6572
|
-
for (const entries of content.entries.values()) {
|
|
6573
|
-
totalEntries += entries.length;
|
|
6574
|
-
}
|
|
6575
|
-
return {
|
|
6576
|
-
hasEntries: content.entries.size > 0,
|
|
6577
|
-
entryTypes,
|
|
6578
|
-
totalEntries,
|
|
6579
|
-
hasPages: content.pages.length > 0,
|
|
6580
|
-
pageCount: content.pages.length,
|
|
6581
|
-
hasNavigation: content.navigation !== null && content.navigation.menus.length > 0,
|
|
6582
|
-
menuCount: content.navigation?.menus.length ?? 0,
|
|
6583
|
-
hasSettings: content.settings !== null
|
|
6584
|
-
};
|
|
6585
|
-
}
|
|
6586
6712
|
|
|
6587
6713
|
// src/cli/sync/mapper.ts
|
|
6588
6714
|
function stripNavigationItemIds(items) {
|
|
@@ -7365,7 +7491,48 @@ function reportSyncResults(output, result, hasErrors) {
|
|
|
7365
7491
|
}
|
|
7366
7492
|
output.info(formatSyncResult(result));
|
|
7367
7493
|
}
|
|
7368
|
-
|
|
7494
|
+
function checkForStaleContent(localContent, localMeta, pagesMeta, navigationMeta, remoteContent) {
|
|
7495
|
+
const staleItems = [];
|
|
7496
|
+
for (const [contentType, localEntries] of localContent.entries) {
|
|
7497
|
+
const meta = localMeta.get(contentType);
|
|
7498
|
+
if (!meta) continue;
|
|
7499
|
+
remoteContent.entries[contentType]?.forEach((remoteEntry) => {
|
|
7500
|
+
const localEntry = localEntries.find((e) => e.identifier === remoteEntry.identifier);
|
|
7501
|
+
if (localEntry) {
|
|
7502
|
+
const entryMeta = meta.entries[remoteEntry.identifier];
|
|
7503
|
+
const localBaseTime = entryMeta?.updatedAt;
|
|
7504
|
+
const remoteTime = remoteContent.meta.entries?.[`${contentType}:${remoteEntry.identifier}`]?.updatedAt;
|
|
7505
|
+
if (localBaseTime && remoteTime && new Date(remoteTime) > new Date(localBaseTime)) {
|
|
7506
|
+
staleItems.push(`Entry: ${contentType}/${remoteEntry.identifier} (Remote updated: ${remoteTime})`);
|
|
7507
|
+
}
|
|
7508
|
+
}
|
|
7509
|
+
});
|
|
7510
|
+
}
|
|
7511
|
+
if (pagesMeta) {
|
|
7512
|
+
localContent.pages.forEach((localPage) => {
|
|
7513
|
+
const remotePage = remoteContent.pages.find((p) => p.identifier === localPage.identifier);
|
|
7514
|
+
const localBaseTime = pagesMeta.pages[localPage.identifier]?.updatedAt;
|
|
7515
|
+
if (remotePage && localBaseTime) {
|
|
7516
|
+
if (new Date(remotePage.updatedAt) > new Date(localBaseTime)) {
|
|
7517
|
+
staleItems.push(`Page: ${localPage.identifier} (Remote updated: ${remotePage.updatedAt})`);
|
|
7518
|
+
}
|
|
7519
|
+
}
|
|
7520
|
+
});
|
|
7521
|
+
}
|
|
7522
|
+
if (navigationMeta) {
|
|
7523
|
+
localContent.navigation?.menus.forEach((localMenu) => {
|
|
7524
|
+
const remoteMenu = remoteContent.navigation.find((m) => m.name === localMenu.name);
|
|
7525
|
+
const localBaseTime = navigationMeta.menus[localMenu.name]?.updatedAt;
|
|
7526
|
+
if (remoteMenu && localBaseTime) {
|
|
7527
|
+
if (new Date(remoteMenu.updatedAt) > new Date(localBaseTime)) {
|
|
7528
|
+
staleItems.push(`Navigation: ${localMenu.name} (Remote updated: ${remoteMenu.updatedAt})`);
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
});
|
|
7532
|
+
}
|
|
7533
|
+
return staleItems;
|
|
7534
|
+
}
|
|
7535
|
+
var pushCommand = new commander.Command("push").description("Push content to CMS").argument("[scope]", "What to push: entries, pages, navigation, or all (default)").argument("[type]", 'Content type (when scope is "entries")').option("--content-dir <dir>", "Content directory (overrides config)").option("--dry-run", "Show what would be pushed without making changes").option("--yes", "Skip confirmation prompt (required for --remote)").option("--force", "Push even if remote content is newer (skip stale check)").option("--allow-truncated", "Push even if remote content was truncated (may cause incomplete sync)").option("--json-diff [mode]", "Output JSON diff (summary or full)", "summary").addHelpText("after", `
|
|
7369
7536
|
Examples:
|
|
7370
7537
|
$ riverbankcms push # Push all content
|
|
7371
7538
|
$ riverbankcms push --dry-run # Preview changes
|
|
@@ -7384,9 +7551,9 @@ Sync Behavior:
|
|
|
7384
7551
|
- sync.contentTarget: 'draft' (default) or 'publish'
|
|
7385
7552
|
|
|
7386
7553
|
Safety:
|
|
7387
|
-
-
|
|
7554
|
+
- Stale detection: aborts if remote content is newer than last pull (use --force to override)
|
|
7555
|
+
- Truncation: aborts if remote has >100 entries per type (use --allow-truncated to override)
|
|
7388
7556
|
- Remote environment (--remote): defaults to dry-run, requires --yes to execute
|
|
7389
|
-
- Use --dry-run to preview changes before pushing
|
|
7390
7557
|
`).action(async (scope, type, options, command) => {
|
|
7391
7558
|
const { output, isRemote, globalOpts } = getOutputContext(command);
|
|
7392
7559
|
let dryRun = options.dryRun ?? false;
|
|
@@ -7420,24 +7587,42 @@ Safety:
|
|
|
7420
7587
|
});
|
|
7421
7588
|
output.info("Reading local content...");
|
|
7422
7589
|
const localContent = await readAllContent(contentDir);
|
|
7590
|
+
const pushScope = scope?.toLowerCase() ?? "all";
|
|
7591
|
+
const filteredLocal = filterLocalContent(localContent, pushScope, type);
|
|
7423
7592
|
output.info("Fetching remote content for comparison...");
|
|
7424
7593
|
const remoteContent = await client.pull.all();
|
|
7425
7594
|
if (remoteContent.meta.truncated) {
|
|
7426
7595
|
output.warn("Warning: Remote content was truncated due to size limits.");
|
|
7427
7596
|
output.warn(
|
|
7428
|
-
remoteContent.meta.truncationMessage ?? "Some content types have more than
|
|
7597
|
+
remoteContent.meta.truncationMessage ?? "Some content types have more than 100 entries. Results may be incomplete."
|
|
7429
7598
|
);
|
|
7430
|
-
if (!options.
|
|
7599
|
+
if (!options.allowTruncated) {
|
|
7600
|
+
output.error(
|
|
7601
|
+
"Push aborted due to truncated remote content.",
|
|
7602
|
+
{ suggestion: "Use --allow-truncated to push anyway, or push specific content types: riverbankcms push entries blog-post" }
|
|
7603
|
+
);
|
|
7604
|
+
return;
|
|
7605
|
+
}
|
|
7606
|
+
output.warn("Proceeding with push despite truncation (--allow-truncated flag used)");
|
|
7607
|
+
}
|
|
7608
|
+
if (!options.force) {
|
|
7609
|
+
output.info("Checking for stale content...");
|
|
7610
|
+
const [localMeta, pagesMeta, navigationMeta] = await Promise.all([
|
|
7611
|
+
readAllMeta(contentDir),
|
|
7612
|
+
readPagesMeta(contentDir),
|
|
7613
|
+
readNavigationMeta(contentDir)
|
|
7614
|
+
]);
|
|
7615
|
+
const staleItems = checkForStaleContent(filteredLocal, localMeta, pagesMeta, navigationMeta, remoteContent);
|
|
7616
|
+
if (staleItems.length > 0) {
|
|
7617
|
+
output.warn("WARNING: The following remote content has changed since you last pulled:");
|
|
7618
|
+
staleItems.forEach((item) => output.warn(` - ${item}`));
|
|
7431
7619
|
output.error(
|
|
7432
|
-
"
|
|
7433
|
-
{ suggestion: "
|
|
7620
|
+
"Push aborted to prevent overwriting newer content.",
|
|
7621
|
+
{ suggestion: 'Run "riverbankcms pull" to update your local content, or use --force to overwrite.' }
|
|
7434
7622
|
);
|
|
7435
7623
|
return;
|
|
7436
7624
|
}
|
|
7437
|
-
output.warn("Proceeding with push despite truncation (--force flag used)");
|
|
7438
7625
|
}
|
|
7439
|
-
const pushScope = scope?.toLowerCase() ?? "all";
|
|
7440
|
-
const filteredLocal = filterLocalContent(localContent, pushScope, type);
|
|
7441
7626
|
output.info("Calculating changes...");
|
|
7442
7627
|
const diff = calculateDiff(filteredLocal, remoteContent, {
|
|
7443
7628
|
existingEntries: cliConfig.sync.existingEntries
|
|
@@ -7478,7 +7663,7 @@ var formatEntryRow = (entry) => [
|
|
|
7478
7663
|
entry.identifier,
|
|
7479
7664
|
entry.status,
|
|
7480
7665
|
entry.hasUnpublishedChanges ? "Yes" : "No",
|
|
7481
|
-
entry.slug,
|
|
7666
|
+
entry.slug ?? "-",
|
|
7482
7667
|
formatDateShort(entry.updatedAt)
|
|
7483
7668
|
];
|
|
7484
7669
|
var upsertCommand = new commander.Command("upsert").description("Create or update an entry").argument("<type>", "Content type").argument("<identifier>", "Entry identifier").option("--data <json>", "Entry data as JSON string").option("--file <path>", "Path to JSON file with entry data").option("--slug <slug>", "Entry slug (defaults to identifier)").option("--title <title>", "Entry title").action(
|
|
@@ -7850,9 +8035,9 @@ function resolveOutputMode(options) {
|
|
|
7850
8035
|
return "terminal";
|
|
7851
8036
|
}
|
|
7852
8037
|
async function writePreviewHtmlFile(html) {
|
|
7853
|
-
const dir = await
|
|
8038
|
+
const dir = await fs3__namespace.mkdtemp(path9__namespace.join(os__namespace.tmpdir(), "riverbank-preview-"));
|
|
7854
8039
|
const filePath = path9__namespace.join(dir, "index.html");
|
|
7855
|
-
await
|
|
8040
|
+
await fs3__namespace.writeFile(filePath, html, "utf-8");
|
|
7856
8041
|
return filePath;
|
|
7857
8042
|
}
|
|
7858
8043
|
async function openPreviewFile(filePath) {
|
|
@@ -7946,6 +8131,8 @@ function buildSchemaTemplate(state) {
|
|
|
7946
8131
|
"",
|
|
7947
8132
|
"This document captures the content model and SDK configuration for this site.",
|
|
7948
8133
|
"The generated section is updated by `riverbankcms init-docs`.",
|
|
8134
|
+
"",
|
|
8135
|
+
renderConfigGuideSection(),
|
|
7949
8136
|
""
|
|
7950
8137
|
].join("\n");
|
|
7951
8138
|
const generated = buildSchemaGeneratedSection(state);
|
|
@@ -8049,6 +8236,132 @@ function renderContentTypesSection(config3) {
|
|
|
8049
8236
|
}
|
|
8050
8237
|
return lines.join("\n");
|
|
8051
8238
|
}
|
|
8239
|
+
function renderConfigGuideSection() {
|
|
8240
|
+
return [
|
|
8241
|
+
"## riverbank.config.ts Guide",
|
|
8242
|
+
"",
|
|
8243
|
+
"Use `riverbank.config.ts` as the source of truth for the site schema, custom blocks, and CLI behavior.",
|
|
8244
|
+
"This section is static guidance for agents; the generated section below shows the live config.",
|
|
8245
|
+
"",
|
|
8246
|
+
"### Field Overrides (blockFieldOptions)",
|
|
8247
|
+
"",
|
|
8248
|
+
"Use `blockFieldOptions` to override the *options* for existing `select` fields.",
|
|
8249
|
+
"This only affects fields that use the `sdkSelect` UI widget (for example: the embed block layout picker).",
|
|
8250
|
+
"",
|
|
8251
|
+
"```ts",
|
|
8252
|
+
"export default defineConfig({",
|
|
8253
|
+
" siteId: 'your-site-id',",
|
|
8254
|
+
" blockFieldOptions: {",
|
|
8255
|
+
" 'block.embed': {",
|
|
8256
|
+
" layout: {",
|
|
8257
|
+
" options: [",
|
|
8258
|
+
" { value: 'showcase', label: 'Showcase Grid' },",
|
|
8259
|
+
" { value: 'list', label: 'Simple List' },",
|
|
8260
|
+
" ],",
|
|
8261
|
+
" },",
|
|
8262
|
+
" },",
|
|
8263
|
+
" 'custom.featured-posts': {",
|
|
8264
|
+
" style: {",
|
|
8265
|
+
" options: [",
|
|
8266
|
+
" { value: 'grid', label: 'Grid' },",
|
|
8267
|
+
" { value: 'carousel', label: 'Carousel' },",
|
|
8268
|
+
" ],",
|
|
8269
|
+
" },",
|
|
8270
|
+
" },",
|
|
8271
|
+
" },",
|
|
8272
|
+
"});",
|
|
8273
|
+
"```",
|
|
8274
|
+
"",
|
|
8275
|
+
"### Block Field Extensions (blockFieldExtensions)",
|
|
8276
|
+
"",
|
|
8277
|
+
"Use `blockFieldExtensions` to add *new* fields to built-in system blocks.",
|
|
8278
|
+
"Extended fields appear at the end of the block form and are available via `content` in `blockOverrides`.",
|
|
8279
|
+
"",
|
|
8280
|
+
"```ts",
|
|
8281
|
+
"export default defineConfig({",
|
|
8282
|
+
" siteId: 'your-site-id',",
|
|
8283
|
+
" blockFieldExtensions: {",
|
|
8284
|
+
" 'block.hero': {",
|
|
8285
|
+
" fields: [",
|
|
8286
|
+
" { id: 'videoBackground', type: 'media', label: 'Video Background', mediaKinds: ['video'] },",
|
|
8287
|
+
" { id: 'overlayOpacity', type: 'number', label: 'Overlay Opacity', defaultValue: 50 },",
|
|
8288
|
+
" ],",
|
|
8289
|
+
" },",
|
|
8290
|
+
" 'block.bodyText': {",
|
|
8291
|
+
" fields: [",
|
|
8292
|
+
" { id: 'layout', type: 'select', label: 'Layout', defaultValue: 'default',",
|
|
8293
|
+
" options: [",
|
|
8294
|
+
" { value: 'default', label: 'Default' },",
|
|
8295
|
+
" { value: 'wide', label: 'Wide' },",
|
|
8296
|
+
" ],",
|
|
8297
|
+
" },",
|
|
8298
|
+
" ],",
|
|
8299
|
+
" },",
|
|
8300
|
+
" },",
|
|
8301
|
+
"});",
|
|
8302
|
+
"```",
|
|
8303
|
+
"",
|
|
8304
|
+
"Rules:",
|
|
8305
|
+
"- Only system blocks (`block.*`) can be extended.",
|
|
8306
|
+
"- Extended field IDs must not collide with existing block fields.",
|
|
8307
|
+
"- If `required: true`, you must set a `defaultValue` to avoid breaking existing blocks.",
|
|
8308
|
+
"",
|
|
8309
|
+
"### Custom Blocks (customBlocks)",
|
|
8310
|
+
"",
|
|
8311
|
+
"Use `customBlocks` to define new block types with their own fields.",
|
|
8312
|
+
"Custom blocks must be rendered via `blockOverrides` in the SDK site.",
|
|
8313
|
+
"",
|
|
8314
|
+
"```ts",
|
|
8315
|
+
"export default defineConfig({",
|
|
8316
|
+
" siteId: 'your-site-id',",
|
|
8317
|
+
" customBlocks: [",
|
|
8318
|
+
" {",
|
|
8319
|
+
" id: 'custom.team-member',",
|
|
8320
|
+
" title: 'Team Member',",
|
|
8321
|
+
" titleSource: 'name',",
|
|
8322
|
+
" description: 'Profile card with bio and photo',",
|
|
8323
|
+
" category: 'content',",
|
|
8324
|
+
" icon: 'User',",
|
|
8325
|
+
" fields: [",
|
|
8326
|
+
" { id: 'name', type: 'text', label: 'Name', required: true },",
|
|
8327
|
+
" { id: 'role', type: 'text', label: 'Role' },",
|
|
8328
|
+
" { id: 'photo', type: 'media', label: 'Photo', mediaKinds: ['image'] },",
|
|
8329
|
+
" { id: 'bio', type: 'richText', label: 'Bio' },",
|
|
8330
|
+
" ],",
|
|
8331
|
+
" },",
|
|
8332
|
+
" ],",
|
|
8333
|
+
"});",
|
|
8334
|
+
"```",
|
|
8335
|
+
"",
|
|
8336
|
+
"### Field Types (for customBlocks and blockFieldExtensions)",
|
|
8337
|
+
"",
|
|
8338
|
+
"All field definitions use the same `FieldDefinition` format as system blocks.",
|
|
8339
|
+
"Supported `type` values:",
|
|
8340
|
+
"",
|
|
8341
|
+
"- `text`: Single or multiline text",
|
|
8342
|
+
"- `richText`: Rich text editor (markdown or HTML)",
|
|
8343
|
+
"- `media`: Image or video upload",
|
|
8344
|
+
"- `boolean`: Toggle/checkbox",
|
|
8345
|
+
"- `number`: Numeric input",
|
|
8346
|
+
"- `date`: Date picker",
|
|
8347
|
+
"- `time`: Time picker",
|
|
8348
|
+
"- `datetime`: Date + time picker",
|
|
8349
|
+
"- `slug`: Slug with optional source field",
|
|
8350
|
+
"- `url`: URL input (optionally allow relative)",
|
|
8351
|
+
"- `link`: Link picker",
|
|
8352
|
+
"- `select`: Single or multi-select options",
|
|
8353
|
+
"- `reference`: Reference another entry type",
|
|
8354
|
+
"- `repeater`: Repeatable list of fields",
|
|
8355
|
+
"- `group`: Group of fields",
|
|
8356
|
+
"- `modal`: Fields inside a modal dialog",
|
|
8357
|
+
"- `tabGroup`: Group fields by tabs",
|
|
8358
|
+
"- `presetOrCustom`: Preset list with custom fallback",
|
|
8359
|
+
"- `contentTypeSelect`: Content type picker (all/routable/nonRoutable)",
|
|
8360
|
+
"- `entryPicker`: Pick a content entry",
|
|
8361
|
+
"",
|
|
8362
|
+
"Tip: For `repeater`, define either `schema.fields` (monomorphic) or `polymorphic: true` with `itemTypes`."
|
|
8363
|
+
].join("\n");
|
|
8364
|
+
}
|
|
8052
8365
|
function renderCustomBlocksSection(customBlocks) {
|
|
8053
8366
|
const lines = ["## Custom Blocks", ""];
|
|
8054
8367
|
if (customBlocks.length === 0) {
|
|
@@ -8196,24 +8509,24 @@ async function loadConfig(configPath, output) {
|
|
|
8196
8509
|
}
|
|
8197
8510
|
}
|
|
8198
8511
|
async function ensureDir2(dirPath) {
|
|
8199
|
-
await
|
|
8512
|
+
await fs3__namespace.mkdir(dirPath, { recursive: true });
|
|
8200
8513
|
}
|
|
8201
8514
|
async function writeFileIfMissing(filePath, contents) {
|
|
8202
8515
|
try {
|
|
8203
|
-
await
|
|
8516
|
+
await fs3__namespace.access(filePath);
|
|
8204
8517
|
} catch {
|
|
8205
|
-
await
|
|
8518
|
+
await fs3__namespace.writeFile(filePath, contents, "utf-8");
|
|
8206
8519
|
}
|
|
8207
8520
|
}
|
|
8208
8521
|
async function upsertGeneratedDoc(filePath, template, generatedSection) {
|
|
8209
8522
|
const markers = getGeneratedMarkers();
|
|
8210
8523
|
const contents = await readFileOrTemplate(filePath, template);
|
|
8211
8524
|
const updated = replaceMarkedSection(contents, markers.start, markers.end, generatedSection);
|
|
8212
|
-
await
|
|
8525
|
+
await fs3__namespace.writeFile(filePath, updated, "utf-8");
|
|
8213
8526
|
}
|
|
8214
8527
|
async function readFileOrTemplate(filePath, template) {
|
|
8215
8528
|
try {
|
|
8216
|
-
return await
|
|
8529
|
+
return await fs3__namespace.readFile(filePath, "utf-8");
|
|
8217
8530
|
} catch {
|
|
8218
8531
|
return template;
|
|
8219
8532
|
}
|
|
@@ -8240,7 +8553,7 @@ async function upsertAgentsSection(filePath) {
|
|
|
8240
8553
|
const content = await readFileOrTemplate(filePath, "# AGENTS.md\n");
|
|
8241
8554
|
const section2 = agentsSectionTemplate();
|
|
8242
8555
|
const updated = replaceMarkedSection(content, AGENTS_START, AGENTS_END, section2);
|
|
8243
|
-
await
|
|
8556
|
+
await fs3__namespace.writeFile(filePath, updated, "utf-8");
|
|
8244
8557
|
}
|
|
8245
8558
|
function cliReferenceTemplate() {
|
|
8246
8559
|
return [
|
|
@@ -8350,6 +8663,51 @@ var initDocsCommand = new commander.Command("init-docs").description("Scaffold a
|
|
|
8350
8663
|
await runInitDocs(options, command);
|
|
8351
8664
|
})
|
|
8352
8665
|
);
|
|
8666
|
+
var backfillCommand = new commander.Command("backfill").description("Backfill identifiers for pages, blocks, and entries that don't have one").addHelpText("after", `
|
|
8667
|
+
This command generates stable identifiers for content created before the SDK was available.
|
|
8668
|
+
Identifiers are generated from titles (slugified) and are idempotent - running this
|
|
8669
|
+
command multiple times won't overwrite existing identifiers.
|
|
8670
|
+
|
|
8671
|
+
Examples:
|
|
8672
|
+
$ riverbankcms identifiers backfill
|
|
8673
|
+
$ riverbankcms identifiers backfill --remote
|
|
8674
|
+
`).action(
|
|
8675
|
+
withErrorHandling(async (_options, command) => {
|
|
8676
|
+
const { output, client } = createCommandContext(command);
|
|
8677
|
+
output.info("Backfilling identifiers for pages, blocks, and entries...");
|
|
8678
|
+
const result = await client.identifiers.backfill();
|
|
8679
|
+
const totalUpdated = result.pages.pagesUpdated + result.blocks.total + result.entries.entriesUpdated;
|
|
8680
|
+
if (totalUpdated === 0) {
|
|
8681
|
+
output.success("All content already has identifiers - nothing to backfill");
|
|
8682
|
+
return;
|
|
8683
|
+
}
|
|
8684
|
+
output.success("Backfill complete", {
|
|
8685
|
+
pages: {
|
|
8686
|
+
updated: result.pages.pagesUpdated,
|
|
8687
|
+
identifiers: result.pages.identifiersAssigned
|
|
8688
|
+
},
|
|
8689
|
+
blocks: {
|
|
8690
|
+
updated: result.blocks.total,
|
|
8691
|
+
byPage: Object.keys(result.blocks.byPage).length > 0 ? result.blocks.byPage : void 0
|
|
8692
|
+
},
|
|
8693
|
+
entries: {
|
|
8694
|
+
updated: result.entries.entriesUpdated,
|
|
8695
|
+
identifiers: result.entries.identifiersAssigned
|
|
8696
|
+
}
|
|
8697
|
+
});
|
|
8698
|
+
})
|
|
8699
|
+
);
|
|
8700
|
+
var identifiersCommand = new commander.Command("identifiers").description("Manage SDK identifiers").addHelpText("after", `
|
|
8701
|
+
Identifiers are stable, human-readable names used to reference content in the SDK.
|
|
8702
|
+
They are automatically generated when content is created via the SDK, but older
|
|
8703
|
+
content may need backfilling.
|
|
8704
|
+
|
|
8705
|
+
Commands:
|
|
8706
|
+
backfill Generate identifiers for content that doesn't have one
|
|
8707
|
+
|
|
8708
|
+
Examples:
|
|
8709
|
+
$ riverbankcms identifiers backfill
|
|
8710
|
+
`).addCommand(backfillCommand);
|
|
8353
8711
|
|
|
8354
8712
|
// src/cli/index.ts
|
|
8355
8713
|
dotenv.config({ path: ".env.local" });
|
|
@@ -8387,6 +8745,7 @@ program.addCommand(navigationCommand);
|
|
|
8387
8745
|
program.addCommand(deleteCommand);
|
|
8388
8746
|
program.addCommand(previewCommand);
|
|
8389
8747
|
program.addCommand(initDocsCommand);
|
|
8748
|
+
program.addCommand(identifiersCommand);
|
|
8390
8749
|
program.parse();
|
|
8391
8750
|
//# sourceMappingURL=index.js.map
|
|
8392
8751
|
//# sourceMappingURL=index.js.map
|