@stelstone/server 0.26.0 → 0.26.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stelstone/server",
3
- "version": "0.26.0",
3
+ "version": "0.26.2",
4
4
  "description": "Runtime-agnostic CMS server built on the Web Fetch API, with pluggable adapters for content, media, auth, and build.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -73,13 +73,41 @@ export function createGitHubContent({ token, owner, repo, branch, draftBranch, p
73
73
  // Lazily create the draft branch off the published branch. Checked once per
74
74
  // process; a 404 on the ref is the "first ever use" case, not an error.
75
75
  let draftBranchReady = !draftMode;
76
+ let indexesStaleAfterSync = false;
77
+ const refreshedAfterSync = new Set();
76
78
  async function ensureDraftBranch() {
77
79
  if (draftBranchReady) return;
78
80
  const ref = await apiGet(`/git/ref/heads/${draftBranch}`);
79
81
  if (!ref) {
80
82
  const base = await apiGet(`/git/ref/heads/${branch}`);
81
- if (!base) throw new Error(`Published branch "${branch}" does not exist`);
83
+ if (!base) throw new Error(`Published branch \"${branch}\" does not exist`);
82
84
  await apiPost(`/git/refs`, { ref: `refs/heads/${draftBranch}`, sha: base.object.sha });
85
+ } else {
86
+ // The draft branch was a snapshot of `branch` at creation — content
87
+ // pushed to the published branch OUTSIDE the CMS (a code deploy adding
88
+ // records) never reached it, so the admin could not even see those
89
+ // files. Found by dogfooding: stelstone.com's landing records landed
90
+ // on main from code and the admin listed an empty collection. Keep the
91
+ // draft current by merging the published branch in once per process.
92
+ try {
93
+ const merged = await apiPost(`/merges`, { base: draftBranch, head: branch });
94
+ // A real merge means files changed under us — the per-collection
95
+ // _index.json manifests may now be stale (the empty-index variant of
96
+ // this bit stelstone.com: an index bootstrapped before content
97
+ // existed served an empty list forever). Mark them suspect; the
98
+ // lazy strategy rebuilds on next list.
99
+ if (merged?.sha) indexesStaleAfterSync = true;
100
+ } catch (err) {
101
+ if (err?.upstreamStatus === 409) {
102
+ // Same record edited on both branches: the draft (the editor's
103
+ // in-progress work) wins by default. Say so; don't fail reads.
104
+ console.warn(
105
+ `[github-content] draft branch \"${draftBranch}\" conflicts with \"${branch}\" — keeping draft state`,
106
+ );
107
+ }
108
+ // 204 \"nothing to merge\" parses as empty JSON and lands here too —
109
+ // in every case reads must proceed.
110
+ }
83
111
  }
84
112
  draftBranchReady = true;
85
113
  }
@@ -206,8 +234,16 @@ export function createGitHubContent({ token, owner, repo, branch, draftBranch, p
206
234
  }
207
235
 
208
236
  // 2. strategy "index": try the pre-built manifest first (1 subrequest).
237
+ // After a draft-branch sync merged changes in, an existing manifest is
238
+ // suspect: skip it once per collection so the lazy path rebuilds it
239
+ // from the merged tree. ("build" sites rebuild via the CLI instead.)
240
+ const skipIndex =
241
+ indexesStaleAfterSync &&
242
+ listConfig.rebuild === "lazy" &&
243
+ !refreshedAfterSync.has(safeCollection);
209
244
  const idxPath = indexFilePath(safeCollection);
210
245
  try {
246
+ if (skipIndex) throw new Error("index suspect after sync");
211
247
  const data = await apiGet(`/contents/${idxPath}?ref=${workBranch}`);
212
248
  if (data && !Array.isArray(data)) {
213
249
  shaCache.set(idxPath, data.sha);
@@ -242,6 +278,7 @@ export function createGitHubContent({ token, owner, repo, branch, draftBranch, p
242
278
  // Persist for all future calls.
243
279
  try {
244
280
  await writeIndex(safeCollection, { entries: pages });
281
+ refreshedAfterSync.add(safeCollection);
245
282
  } catch (err) {
246
283
  console.warn(`[listPages] Could not write ${listConfig.indexFile}:`, err?.message);
247
284
  }
package/src/version.mjs CHANGED
@@ -5,4 +5,4 @@
5
5
  * require() and no import.meta.url, so reading the manifest at runtime yields
6
6
  * "unknown" there.
7
7
  */
8
- export const SERVER_VERSION = "0.26.0";
8
+ export const SERVER_VERSION = "0.26.2";