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