@drawnagency/primitives 0.1.107 → 0.1.108
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/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/hooks/useEditorPublish.d.ts +2 -0
- package/dist/hooks/useEditorPublish.d.ts.map +1 -1
- package/dist/lib/dexie.d.ts +25 -8
- package/dist/lib/dexie.d.ts.map +1 -1
- package/dist/lib/dexie.js +19 -17
- package/package.json +1 -1
- package/src/components/shell/EditorShell.tsx +64 -29
- package/src/hooks/useEditorPublish.ts +29 -4
- package/src/lib/dexie.ts +62 -28
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAejD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAejD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAuDtF,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AA+CnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA2BxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,2CA2+DP;AAwED;6EAC6E;AAC7E,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAK/B"}
|
|
@@ -52,6 +52,8 @@ interface PublishDeps {
|
|
|
52
52
|
sections: LoadedSection[];
|
|
53
53
|
index: SiteIndex;
|
|
54
54
|
savedIds: string[];
|
|
55
|
+
mediaManifest: MediaManifest;
|
|
56
|
+
documentManifest: DocumentManifest;
|
|
55
57
|
}) => Promise<void>;
|
|
56
58
|
/** Injected by EditorShell. Receives non-fatal server warnings from a
|
|
57
59
|
* successful save (e.g. the nav.json replacement was declined by the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAgDpF,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;;mFAM+E;IAC/E,SAAS,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE,KAAK,IAAI,CAAC;IACtG,aAAa,EAAE,aAAa,CAAC;IAC7B;;2EAEuE;IACvE,gBAAgB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAClD,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,iBAAiB,EAAE,SAAS,EAAE,CAAC;IAC/B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtF,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzE,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAI/B,cAAc,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACpC;;;;qDAIiD;IACjD,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC;;;;;sDAKkD;IAClD,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,aAAa,EAAE,CAAC;QAAC,KAAK,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/dexie.d.ts
CHANGED
|
@@ -65,11 +65,24 @@ export declare function getBranchShas(): Promise<{
|
|
|
65
65
|
mainSha: string | null;
|
|
66
66
|
} | null>;
|
|
67
67
|
export declare function getDeletedSections(): Promise<string[]>;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Write the content snapshot for one commit.
|
|
70
|
+
*
|
|
71
|
+
* `manifests` is REQUIRED, and that is the whole point: both sidecars belong to
|
|
72
|
+
* this sha, so every writer that advances the row must restate them. They used to
|
|
73
|
+
* be optional (the media one lived in a separate store entirely), and every
|
|
74
|
+
* save/publish that re-primed this row left them behind at an older sha — a later
|
|
75
|
+
* cache hit then served pre-upload media ("No Image" on images the published site
|
|
76
|
+
* rendered fine) and an empty document manifest ("File missing" on committed
|
|
77
|
+
* documents). Passing them positionally makes forgetting a compile error.
|
|
78
|
+
*/
|
|
79
|
+
export declare function cacheContent(sha: string, sections: LoadedSection[], index: SiteIndex, siteConfig: Record<string, unknown>, manifests: {
|
|
80
|
+
media: MediaManifest;
|
|
81
|
+
documents: DocumentManifest;
|
|
82
|
+
}, diff?: {
|
|
83
|
+
savedBranchSha?: string | null;
|
|
84
|
+
changedSectionIds?: string[];
|
|
85
|
+
mainIndex?: SiteIndex | null;
|
|
73
86
|
}): Promise<void>;
|
|
74
87
|
export declare function getCachedContent(): Promise<{
|
|
75
88
|
sha: string;
|
|
@@ -79,14 +92,18 @@ export declare function getCachedContent(): Promise<{
|
|
|
79
92
|
savedBranchSha: string | null;
|
|
80
93
|
changedSectionIds: string[];
|
|
81
94
|
mainIndex: SiteIndex | null;
|
|
82
|
-
|
|
95
|
+
/** Both are null ONLY for a row written before the sidecars moved onto it.
|
|
96
|
+
* Callers must treat null as an unusable cache and re-fetch — substituting an
|
|
97
|
+
* empty manifest would blank every image ("No Image") or every committed
|
|
98
|
+
* document ("File missing") on the page. An EMPTY manifest is a legitimate
|
|
99
|
+
* value and is not null. */
|
|
100
|
+
mediaManifest: MediaManifest | null;
|
|
101
|
+
documentManifest: DocumentManifest | null;
|
|
83
102
|
} | null>;
|
|
84
103
|
/** Invalidate only the cached content snapshot (e.g. after a publish removes the
|
|
85
104
|
* draft), forcing the next editor load to refetch rather than serve stale diff
|
|
86
105
|
* state. Narrower than discardSavedChanges, which also clears index/meta/config. */
|
|
87
106
|
export declare function clearContentCache(): Promise<void>;
|
|
88
|
-
export declare function persistMediaManifest(manifest: MediaManifest): Promise<void>;
|
|
89
|
-
export declare function getMediaManifest(): Promise<MediaManifest | null>;
|
|
90
107
|
export declare function addPendingMediaItem(item: MediaItem, localUrls?: Record<string, string>, blobs?: Record<string, Blob>): Promise<void>;
|
|
91
108
|
export declare function getPendingMediaItems(): Promise<MediaItem[]>;
|
|
92
109
|
export declare function getPendingMediaLocalUrls(id: string): Promise<Record<string, string> | null>;
|
package/dist/lib/dexie.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dexie.d.ts","sourceRoot":"","sources":["../../src/lib/dexie.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAe,UAAU,EAAQ,MAAM,wBAAwB,CAAC;AAEvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"dexie.d.ts","sourceRoot":"","sources":["../../src/lib/dexie.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/dexie.js
CHANGED
|
@@ -8,7 +8,6 @@ var EditorDatabase = class extends Dexie {
|
|
|
8
8
|
meta;
|
|
9
9
|
siteConfig;
|
|
10
10
|
contentCache;
|
|
11
|
-
mediaManifest;
|
|
12
11
|
pendingMedia;
|
|
13
12
|
pendingMediaDeletions;
|
|
14
13
|
preservedEdits;
|
|
@@ -114,6 +113,20 @@ var EditorDatabase = class extends Dexie {
|
|
|
114
113
|
preservedEdits: "id, sectionId",
|
|
115
114
|
pendingDocuments: "id"
|
|
116
115
|
});
|
|
116
|
+
this.version(11).stores({
|
|
117
|
+
sections: "sectionId",
|
|
118
|
+
siteIndex: "key",
|
|
119
|
+
meta: "key",
|
|
120
|
+
siteConfig: "key",
|
|
121
|
+
contentCache: "key",
|
|
122
|
+
mediaManifest: null,
|
|
123
|
+
pendingMedia: "id",
|
|
124
|
+
pendingMediaDeletions: "id",
|
|
125
|
+
preservedEdits: "id, sectionId",
|
|
126
|
+
pendingDocuments: "id"
|
|
127
|
+
}).upgrade(async (tx) => {
|
|
128
|
+
await tx.table("contentCache").clear();
|
|
129
|
+
});
|
|
117
130
|
}
|
|
118
131
|
};
|
|
119
132
|
var db = null;
|
|
@@ -310,7 +323,7 @@ async function getDeletedSections() {
|
|
|
310
323
|
const row = await getDb().siteIndex.get("current");
|
|
311
324
|
return row?.deletedSections ?? [];
|
|
312
325
|
}
|
|
313
|
-
async function cacheContent(sha, sections, index, siteConfig, diff) {
|
|
326
|
+
async function cacheContent(sha, sections, index, siteConfig, manifests, diff) {
|
|
314
327
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
315
328
|
await getDb().contentCache.put({
|
|
316
329
|
key: "current",
|
|
@@ -322,7 +335,8 @@ async function cacheContent(sha, sections, index, siteConfig, diff) {
|
|
|
322
335
|
savedBranchSha: diff?.savedBranchSha ?? null,
|
|
323
336
|
changedSectionIds: diff?.changedSectionIds ?? [],
|
|
324
337
|
mainIndex: diff?.mainIndex ?? null,
|
|
325
|
-
|
|
338
|
+
mediaManifest: manifests.media,
|
|
339
|
+
documentManifest: manifests.documents
|
|
326
340
|
});
|
|
327
341
|
}
|
|
328
342
|
async function getCachedContent() {
|
|
@@ -338,23 +352,13 @@ async function getCachedContent() {
|
|
|
338
352
|
// No stored mainIndex (e.g. legacy or post-publish cache) means "no draft",
|
|
339
353
|
// so the published index equals the current index.
|
|
340
354
|
mainIndex: row.mainIndex ?? row.index,
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
// heals on the next fresh load or save.
|
|
344
|
-
documentManifest: row.documentManifest ?? {}
|
|
355
|
+
mediaManifest: row.mediaManifest ?? null,
|
|
356
|
+
documentManifest: row.documentManifest ?? null
|
|
345
357
|
};
|
|
346
358
|
}
|
|
347
359
|
async function clearContentCache() {
|
|
348
360
|
await getDb().contentCache.clear();
|
|
349
361
|
}
|
|
350
|
-
async function persistMediaManifest(manifest) {
|
|
351
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
352
|
-
await getDb().mediaManifest.put({ key: "current", manifest, updatedAt: now });
|
|
353
|
-
}
|
|
354
|
-
async function getMediaManifest() {
|
|
355
|
-
const row = await getDb().mediaManifest.get("current");
|
|
356
|
-
return row?.manifest ?? null;
|
|
357
|
-
}
|
|
358
362
|
async function addPendingMediaItem(item, localUrls = {}, blobs = {}) {
|
|
359
363
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
360
364
|
await getDb().pendingMedia.put({ id: item.id, item, localUrls, blobs, updatedAt: now });
|
|
@@ -444,7 +448,6 @@ export {
|
|
|
444
448
|
getDeletedSections,
|
|
445
449
|
getDirtySectionRows,
|
|
446
450
|
getDirtySections,
|
|
447
|
-
getMediaManifest,
|
|
448
451
|
getPendingDocumentBlob,
|
|
449
452
|
getPendingDocuments,
|
|
450
453
|
getPendingMediaBlobs,
|
|
@@ -456,7 +459,6 @@ export {
|
|
|
456
459
|
listPreservedEdits,
|
|
457
460
|
markPendingMediaDeleted,
|
|
458
461
|
persistAll,
|
|
459
|
-
persistMediaManifest,
|
|
460
462
|
persistSiteConfig,
|
|
461
463
|
persistSiteIndex,
|
|
462
464
|
preserveEdit,
|
package/package.json
CHANGED
|
@@ -67,8 +67,6 @@ import {
|
|
|
67
67
|
discardLocalChanges,
|
|
68
68
|
getCachedContent,
|
|
69
69
|
cacheContent,
|
|
70
|
-
persistMediaManifest,
|
|
71
|
-
getMediaManifest,
|
|
72
70
|
getPendingMediaItems,
|
|
73
71
|
getPendingMediaBlobs,
|
|
74
72
|
getPendingMediaDeletions,
|
|
@@ -311,6 +309,13 @@ export default function EditorShell({
|
|
|
311
309
|
// immediate 409 retry assembles the saved manifest against the rebased copy.
|
|
312
310
|
const mediaManifestRef = useRef<MediaManifest>(mediaManifest);
|
|
313
311
|
useEffect(() => { mediaManifestRef.current = mediaManifest; }, [mediaManifest]);
|
|
312
|
+
// Same reason, for the document sidecar: the collab handlers cache it against a
|
|
313
|
+
// remote sha, and reading the state directly there would either capture a stale
|
|
314
|
+
// closure copy (writing an outdated sidecar for that sha — "File missing" on the
|
|
315
|
+
// next cache hit) or force documentManifest into their dep arrays, churning the
|
|
316
|
+
// callback identity the collab wiring subscribes with.
|
|
317
|
+
const documentManifestRef = useRef<DocumentManifest | null>(documentManifest);
|
|
318
|
+
useEffect(() => { documentManifestRef.current = documentManifest; }, [documentManifest]);
|
|
314
319
|
// Guards the debounced preview-image composition effect (below): skips
|
|
315
320
|
// recomposing on the very first run when a preview already exists, and
|
|
316
321
|
// dedupes concurrent runs against the same composition signature.
|
|
@@ -603,13 +608,22 @@ export default function EditorShell({
|
|
|
603
608
|
// snapshot, never the rebased result. Caching the rebase (local dirty
|
|
604
609
|
// overlays folded in) would poison the next recovery's diff: dirty
|
|
605
610
|
// sections would read as remote-unchanged (silent local-wins) and a
|
|
606
|
-
// locally-added section would read as "deleted remotely".
|
|
611
|
+
// locally-added section would read as "deleted remotely". The manifest
|
|
612
|
+
// follows the same rule: the remote one, not the rebased view.
|
|
607
613
|
await cacheContent(
|
|
608
614
|
remote.sha ?? remote.savedBranchSha ?? headSha,
|
|
609
615
|
remote.sections,
|
|
610
616
|
remote.index,
|
|
611
617
|
remote.siteConfig,
|
|
612
|
-
{
|
|
618
|
+
{
|
|
619
|
+
media: remote.mediaManifest ?? mediaManifestRef.current,
|
|
620
|
+
documents: remote.documentManifest ?? documentManifestRef.current ?? {},
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
savedBranchSha: remote.savedBranchSha,
|
|
624
|
+
changedSectionIds: remote.changedSectionIds,
|
|
625
|
+
mainIndex: remote.mainIndex,
|
|
626
|
+
},
|
|
613
627
|
);
|
|
614
628
|
return true;
|
|
615
629
|
} catch (err) {
|
|
@@ -698,13 +712,21 @@ export default function EditorShell({
|
|
|
698
712
|
// ...but the CLEAN fetched remote snapshot is what gets cached as the
|
|
699
713
|
// baseline (amendment 1: caching the rebased result would poison the next
|
|
700
714
|
// 409 recovery — dirty sections would read as remote-unchanged and a ghost
|
|
701
|
-
// race would never surface).
|
|
715
|
+
// race would never surface). Same for the manifest: remote, not rebased.
|
|
702
716
|
await cacheContent(
|
|
703
717
|
remote.sha ?? remote.savedBranchSha ?? headSha,
|
|
704
718
|
remote.sections,
|
|
705
719
|
remote.index,
|
|
706
720
|
remote.siteConfig,
|
|
707
|
-
{
|
|
721
|
+
{
|
|
722
|
+
media: remote.mediaManifest ?? mediaManifestRef.current,
|
|
723
|
+
documents: remote.documentManifest ?? documentManifestRef.current ?? {},
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
savedBranchSha: remote.savedBranchSha,
|
|
727
|
+
changedSectionIds: remote.changedSectionIds,
|
|
728
|
+
mainIndex: remote.mainIndex,
|
|
729
|
+
},
|
|
708
730
|
);
|
|
709
731
|
} catch (err) {
|
|
710
732
|
console.error("Remote saved-event sync failed:", err);
|
|
@@ -947,17 +969,24 @@ export default function EditorShell({
|
|
|
947
969
|
// just-saved sections now differ from main, so fold them into
|
|
948
970
|
// changedSectionIds (state + ref + cache stay consistent, and a reload that
|
|
949
971
|
// hits this cache shows the same badges the server would report).
|
|
950
|
-
onBaselineCommitted: async ({ sha, sections: baselineSections, index, savedIds }) => {
|
|
972
|
+
onBaselineCommitted: async ({ sha, sections: baselineSections, index, savedIds, mediaManifest: committedManifest, documentManifest: committedDocumentManifest }) => {
|
|
951
973
|
const changed = new Set(
|
|
952
974
|
[...changedSectionIdsRef.current, ...savedIds].filter((id) => id in index.sections),
|
|
953
975
|
);
|
|
954
976
|
changedSectionIdsRef.current = changed;
|
|
955
977
|
setChangedSectionIds(changed);
|
|
956
|
-
await cacheContent(
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
978
|
+
await cacheContent(
|
|
979
|
+
sha, baselineSections, index, (siteConfig ?? {}) as Record<string, unknown>,
|
|
980
|
+
// What the save actually committed — NOT the refs/state, which the
|
|
981
|
+
// onMediaPublished / onDocumentsPublished callbacks only refresh through a
|
|
982
|
+
// state effect and can still lag by a render at this point.
|
|
983
|
+
{ media: committedManifest, documents: committedDocumentManifest },
|
|
984
|
+
{
|
|
985
|
+
savedBranchSha: sha,
|
|
986
|
+
changedSectionIds: [...changed],
|
|
987
|
+
mainIndex: mainIndexRef.current,
|
|
988
|
+
},
|
|
989
|
+
);
|
|
961
990
|
},
|
|
962
991
|
savingRef: inFlightSaveRef,
|
|
963
992
|
getEditGeneration: persistence.currentGeneration,
|
|
@@ -1148,36 +1177,40 @@ export default function EditorShell({
|
|
|
1148
1177
|
// deliberately don't block every load on a freshness fetch to validate this.
|
|
1149
1178
|
const cacheKeySha = draftHeadSha ?? headSha;
|
|
1150
1179
|
|
|
1180
|
+
// A row missing either sidecar predates them moving onto it (v11); it is
|
|
1181
|
+
// not a usable snapshot — serving it would render every image as "No Image"
|
|
1182
|
+
// and every committed document as "File missing" — so treat it as a miss
|
|
1183
|
+
// and re-fetch. An EMPTY manifest is a legitimate value, not an absence.
|
|
1151
1184
|
const cached = await getCachedContent();
|
|
1152
|
-
if (cached && cached.sha === cacheKeySha) {
|
|
1185
|
+
if (cached && cached.sha === cacheKeySha && cached.mediaManifest && cached.documentManifest) {
|
|
1153
1186
|
loadedSections = cached.sections;
|
|
1154
1187
|
loadedIndex = cached.index;
|
|
1155
1188
|
loadedConfig = SiteConfigSchema.parse(cached.siteConfig);
|
|
1156
|
-
|
|
1157
|
-
if (savedManifest) loadedManifest = savedManifest;
|
|
1189
|
+
loadedManifest = cached.mediaManifest;
|
|
1158
1190
|
setSavedSha(cached.savedBranchSha ?? null);
|
|
1159
1191
|
setMainSha(headSha);
|
|
1160
1192
|
setChangedSectionIds(new Set(cached.changedSectionIds ?? []));
|
|
1161
1193
|
setMainIndex(cached.mainIndex ?? loadedIndex);
|
|
1162
1194
|
// Without this, a cache-hit load left documentManifest null forever —
|
|
1163
1195
|
// the siteDocuments memo's null-guard then never lifted and every
|
|
1164
|
-
// document row (committed or staged) read "File missing".
|
|
1165
|
-
|
|
1196
|
+
// document row (committed or staged) read "File missing". The row's copy
|
|
1197
|
+
// is authoritative: the hit gate above rejected a row without one.
|
|
1198
|
+
setDocumentManifest(cached.documentManifest);
|
|
1166
1199
|
} else {
|
|
1167
1200
|
const data = await fetchSavedContent();
|
|
1168
1201
|
loadedSections = data.sections;
|
|
1169
1202
|
loadedIndex = data.index;
|
|
1170
1203
|
loadedConfig = SiteConfigSchema.parse(data.siteConfig);
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1204
|
+
if (data.mediaManifest) loadedManifest = data.mediaManifest;
|
|
1205
|
+
await cacheContent(
|
|
1206
|
+
data.sha, data.sections, data.index, data.siteConfig,
|
|
1207
|
+
{ media: loadedManifest, documents: data.documentManifest ?? {} },
|
|
1208
|
+
{
|
|
1209
|
+
savedBranchSha: data.savedBranchSha,
|
|
1210
|
+
changedSectionIds: data.changedSectionIds,
|
|
1211
|
+
mainIndex: data.mainIndex,
|
|
1212
|
+
},
|
|
1213
|
+
)
|
|
1181
1214
|
setDocumentManifest(data.documentManifest ?? {});
|
|
1182
1215
|
setSavedSha(data.savedBranchSha);
|
|
1183
1216
|
setMainSha(headSha);
|
|
@@ -1343,9 +1376,11 @@ export default function EditorShell({
|
|
|
1343
1376
|
setSiteConfig(parsed.data);
|
|
1344
1377
|
applySiteConfigPreview(parsed.data);
|
|
1345
1378
|
}
|
|
1379
|
+
// Discard restores the cached snapshot, so the sidecars must come from that
|
|
1380
|
+
// same row — anything else could disagree with the sections we just set.
|
|
1381
|
+
if (cached.mediaManifest) setMediaManifest(cached.mediaManifest);
|
|
1382
|
+
if (cached.documentManifest) setDocumentManifest(cached.documentManifest);
|
|
1346
1383
|
}
|
|
1347
|
-
const savedManifest = await getMediaManifest();
|
|
1348
|
-
setMediaManifest(savedManifest ?? { images: {}, folders: {} });
|
|
1349
1384
|
persistence.clearConfigDirty();
|
|
1350
1385
|
// Discard wiped the Dexie siteIndex row — no unsaved structure remains.
|
|
1351
1386
|
// Tokenless: discard is unconditional, unlike the save-success clear.
|
|
@@ -98,7 +98,7 @@ interface PublishDeps {
|
|
|
98
98
|
* baseline that recoverConflict diffs against. Without this, the baseline
|
|
99
99
|
* dies at the first save (discardSavedChanges clears it) and later 409
|
|
100
100
|
* recoveries would silently merge local-wins. */
|
|
101
|
-
onBaselineCommitted?: (args: { sha: string; sections: LoadedSection[]; index: SiteIndex; savedIds: string[] }) => Promise<void>;
|
|
101
|
+
onBaselineCommitted?: (args: { sha: string; sections: LoadedSection[]; index: SiteIndex; savedIds: string[]; mediaManifest: MediaManifest; documentManifest: DocumentManifest }) => Promise<void>;
|
|
102
102
|
/** Injected by EditorShell. Receives non-fatal server warnings from a
|
|
103
103
|
* successful save (e.g. the nav.json replacement was declined by the
|
|
104
104
|
* degraded-nav guard) so the shell can surface them instead of the save
|
|
@@ -291,6 +291,15 @@ export function useEditorPublish({
|
|
|
291
291
|
uploadedItems: MediaItem[];
|
|
292
292
|
// The index actually sent (deletions applied) — the committed index state.
|
|
293
293
|
sentIndex: SiteIndex;
|
|
294
|
+
// The media manifest as committed by this save: the assembled manifest when
|
|
295
|
+
// media changed, otherwise the (unchanged) manifest we saved against. The
|
|
296
|
+
// content-cache re-prime writes it alongside the new sha, so the two can
|
|
297
|
+
// never drift.
|
|
298
|
+
committedManifest: MediaManifest;
|
|
299
|
+
// The document sidecar as committed by this save — the view we saved against,
|
|
300
|
+
// plus the entries this save committed, minus the ids it deleted. Same
|
|
301
|
+
// contract as committedManifest: it rides the cache write for this sha.
|
|
302
|
+
committedDocumentManifest: DocumentManifest;
|
|
294
303
|
committedDocumentIds: string[];
|
|
295
304
|
// The deleted ids the final request actually carried — empty when nothing
|
|
296
305
|
// was staged, and never populated on a failure (see the catch below).
|
|
@@ -499,6 +508,10 @@ export function useEditorPublish({
|
|
|
499
508
|
savedSections: dirty.map(({ sectionId, updatedAt, content }) => ({ sectionId, updatedAt, content })),
|
|
500
509
|
uploadedItems: [...uploadedItems, ...finalUploads.map((u) => u.item)],
|
|
501
510
|
sentIndex: siteIndex,
|
|
511
|
+
committedManifest: gathered.updatedManifest ?? args.mediaManifest,
|
|
512
|
+
// Read AFTER the final request: committedDocuments is complete and the
|
|
513
|
+
// deletions (which ride only that request) have landed.
|
|
514
|
+
committedDocumentManifest: documentManifestToSend(),
|
|
502
515
|
committedDocumentIds,
|
|
503
516
|
committedDocumentDeletions: args.pendingDocumentDeletions,
|
|
504
517
|
};
|
|
@@ -659,7 +672,7 @@ export function useEditorPublish({
|
|
|
659
672
|
return;
|
|
660
673
|
}
|
|
661
674
|
|
|
662
|
-
const { sha, savedSections, uploadedItems, sentIndex, committedDocumentIds, committedDocumentDeletions } = await saveWithConflictRecovery();
|
|
675
|
+
const { sha, savedSections, uploadedItems, sentIndex, committedManifest, committedDocumentManifest, committedDocumentIds, committedDocumentDeletions } = await saveWithConflictRecovery();
|
|
663
676
|
onShasUpdated(sha, null);
|
|
664
677
|
|
|
665
678
|
try {
|
|
@@ -681,6 +694,8 @@ export function useEditorPublish({
|
|
|
681
694
|
sections: baselineSections,
|
|
682
695
|
index: sentIndex,
|
|
683
696
|
savedIds: savedSections.map((s) => s.sectionId),
|
|
697
|
+
mediaManifest: committedManifest,
|
|
698
|
+
documentManifest: committedDocumentManifest,
|
|
684
699
|
});
|
|
685
700
|
}
|
|
686
701
|
finishSave(uploadedItems, savedSections.map((s) => s.sectionId));
|
|
@@ -750,10 +765,12 @@ export function useEditorPublish({
|
|
|
750
765
|
let savedSections: { sectionId: string; updatedAt: string; content: SectionContent }[] = [];
|
|
751
766
|
let uploadedItems: MediaItem[] = [];
|
|
752
767
|
let sentIndex: SiteIndex | null = null;
|
|
768
|
+
let committedManifest: MediaManifest | null = null;
|
|
769
|
+
let committedDocumentManifest: DocumentManifest | null = null;
|
|
753
770
|
let committedDocumentIds: string[] = [];
|
|
754
771
|
let committedDocumentDeletions: string[] = [];
|
|
755
772
|
if (hasLocalEdits) {
|
|
756
|
-
({ savedSections, uploadedItems, sentIndex, committedDocumentIds, committedDocumentDeletions } = await saveWithConflictRecovery());
|
|
773
|
+
({ savedSections, uploadedItems, sentIndex, committedManifest, committedDocumentManifest, committedDocumentIds, committedDocumentDeletions } = await saveWithConflictRecovery());
|
|
757
774
|
}
|
|
758
775
|
|
|
759
776
|
setPublishAction("publishing");
|
|
@@ -789,7 +806,15 @@ export function useEditorPublish({
|
|
|
789
806
|
savedRows: savedSections,
|
|
790
807
|
sentIndex: sentIndex!,
|
|
791
808
|
});
|
|
792
|
-
|
|
809
|
+
// Both sidecars ride the same write as the sha they belong to: publish
|
|
810
|
+
// deleted the draft, so main's head IS this sha and the next load hits
|
|
811
|
+
// this row. Omitting them here is what blanked freshly uploaded images
|
|
812
|
+
// ("No Image", while the published site was correct) and freshly
|
|
813
|
+
// committed documents ("File missing").
|
|
814
|
+
await cacheContent(sha, baselineSections, sentIndex!, siteConfig!, {
|
|
815
|
+
media: committedManifest!,
|
|
816
|
+
documents: committedDocumentManifest!,
|
|
817
|
+
});
|
|
793
818
|
finishSave(uploadedItems, savedSections.map((s) => s.sectionId));
|
|
794
819
|
if (committedDocumentIds.length > 0) onDocumentsPublished?.(committedDocumentIds);
|
|
795
820
|
if (committedDocumentDeletions.length > 0) onDocumentsDeleted?.(committedDocumentDeletions);
|
package/src/lib/dexie.ts
CHANGED
|
@@ -50,19 +50,21 @@ interface ContentCacheRow {
|
|
|
50
50
|
savedBranchSha?: string | null;
|
|
51
51
|
changedSectionIds?: string[];
|
|
52
52
|
mainIndex?: SiteIndex | null;
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
53
|
+
// Both sidecars AT THIS SHA. They live HERE and nowhere else, and cacheContent
|
|
54
|
+
// takes them as a required argument, because every way of storing them apart
|
|
55
|
+
// from the sha drifted: the media manifest was a separate singleton row and the
|
|
56
|
+
// document manifest was an optional extra, and both were written only by the
|
|
57
|
+
// cache-miss load path — so every save/publish that re-primed this row advanced
|
|
58
|
+
// the sha and left them behind. A later cache hit then restored pre-upload
|
|
59
|
+
// media (freshly uploaded images rendered "No Image" in the editor while the
|
|
60
|
+
// published site was correct) and an empty document manifest (committed
|
|
61
|
+
// documents read "File missing"). Optional in the TYPE only because Dexie rows
|
|
62
|
+
// are not schema-enforced; readers treat absence as a MISS (getCachedContent),
|
|
63
|
+
// never as "nothing here".
|
|
64
|
+
mediaManifest?: MediaManifest | null;
|
|
57
65
|
documentManifest?: DocumentManifest | null;
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
interface MediaManifestRow {
|
|
61
|
-
key: string;
|
|
62
|
-
manifest: MediaManifest;
|
|
63
|
-
updatedAt: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
68
|
interface PendingMediaRow {
|
|
67
69
|
id: string;
|
|
68
70
|
item: MediaItem;
|
|
@@ -101,7 +103,6 @@ class EditorDatabase extends Dexie {
|
|
|
101
103
|
meta!: Dexie.Table<MetaRow, string>;
|
|
102
104
|
siteConfig!: Dexie.Table<SiteConfigRow, string>;
|
|
103
105
|
contentCache!: Dexie.Table<ContentCacheRow, string>;
|
|
104
|
-
mediaManifest!: Dexie.Table<MediaManifestRow, string>;
|
|
105
106
|
pendingMedia!: Dexie.Table<PendingMediaRow, string>;
|
|
106
107
|
pendingMediaDeletions!: Dexie.Table<PendingMediaDeletionRow, string>;
|
|
107
108
|
preservedEdits!: Dexie.Table<PreservedEditRow, string>;
|
|
@@ -223,6 +224,28 @@ class EditorDatabase extends Dexie {
|
|
|
223
224
|
preservedEdits: "id, sectionId",
|
|
224
225
|
pendingDocuments: "id",
|
|
225
226
|
});
|
|
227
|
+
// v11: the media manifest moved onto the sha-keyed contentCache row and the
|
|
228
|
+
// standalone `mediaManifest` store is deleted. It was written by the
|
|
229
|
+
// cache-miss load path ONLY, so every save/publish that re-primed
|
|
230
|
+
// contentCache advanced the cached sha while leaving the manifest at its
|
|
231
|
+
// older value — and the next cache hit served that stale manifest, blanking
|
|
232
|
+
// every image uploaded since. Dropping the store makes the drift
|
|
233
|
+
// unrepresentable. contentCache is cleared because no pre-v11 row carries a
|
|
234
|
+
// manifest; the next load re-fetches and writes both together.
|
|
235
|
+
this.version(11).stores({
|
|
236
|
+
sections: "sectionId",
|
|
237
|
+
siteIndex: "key",
|
|
238
|
+
meta: "key",
|
|
239
|
+
siteConfig: "key",
|
|
240
|
+
contentCache: "key",
|
|
241
|
+
mediaManifest: null,
|
|
242
|
+
pendingMedia: "id",
|
|
243
|
+
pendingMediaDeletions: "id",
|
|
244
|
+
preservedEdits: "id, sectionId",
|
|
245
|
+
pendingDocuments: "id",
|
|
246
|
+
}).upgrade(async (tx) => {
|
|
247
|
+
await tx.table("contentCache").clear();
|
|
248
|
+
});
|
|
226
249
|
}
|
|
227
250
|
}
|
|
228
251
|
|
|
@@ -478,12 +501,28 @@ export async function getDeletedSections(): Promise<string[]> {
|
|
|
478
501
|
return row?.deletedSections ?? [];
|
|
479
502
|
}
|
|
480
503
|
|
|
504
|
+
/**
|
|
505
|
+
* Write the content snapshot for one commit.
|
|
506
|
+
*
|
|
507
|
+
* `manifests` is REQUIRED, and that is the whole point: both sidecars belong to
|
|
508
|
+
* this sha, so every writer that advances the row must restate them. They used to
|
|
509
|
+
* be optional (the media one lived in a separate store entirely), and every
|
|
510
|
+
* save/publish that re-primed this row left them behind at an older sha — a later
|
|
511
|
+
* cache hit then served pre-upload media ("No Image" on images the published site
|
|
512
|
+
* rendered fine) and an empty document manifest ("File missing" on committed
|
|
513
|
+
* documents). Passing them positionally makes forgetting a compile error.
|
|
514
|
+
*/
|
|
481
515
|
export async function cacheContent(
|
|
482
516
|
sha: string,
|
|
483
517
|
sections: LoadedSection[],
|
|
484
518
|
index: SiteIndex,
|
|
485
519
|
siteConfig: Record<string, unknown>,
|
|
486
|
-
|
|
520
|
+
manifests: { media: MediaManifest; documents: DocumentManifest },
|
|
521
|
+
diff?: {
|
|
522
|
+
savedBranchSha?: string | null;
|
|
523
|
+
changedSectionIds?: string[];
|
|
524
|
+
mainIndex?: SiteIndex | null;
|
|
525
|
+
},
|
|
487
526
|
): Promise<void> {
|
|
488
527
|
const now = new Date().toISOString();
|
|
489
528
|
await getDb().contentCache.put({
|
|
@@ -496,7 +535,8 @@ export async function cacheContent(
|
|
|
496
535
|
savedBranchSha: diff?.savedBranchSha ?? null,
|
|
497
536
|
changedSectionIds: diff?.changedSectionIds ?? [],
|
|
498
537
|
mainIndex: diff?.mainIndex ?? null,
|
|
499
|
-
|
|
538
|
+
mediaManifest: manifests.media,
|
|
539
|
+
documentManifest: manifests.documents,
|
|
500
540
|
});
|
|
501
541
|
}
|
|
502
542
|
|
|
@@ -508,7 +548,13 @@ export async function getCachedContent(): Promise<{
|
|
|
508
548
|
savedBranchSha: string | null;
|
|
509
549
|
changedSectionIds: string[];
|
|
510
550
|
mainIndex: SiteIndex | null;
|
|
511
|
-
|
|
551
|
+
/** Both are null ONLY for a row written before the sidecars moved onto it.
|
|
552
|
+
* Callers must treat null as an unusable cache and re-fetch — substituting an
|
|
553
|
+
* empty manifest would blank every image ("No Image") or every committed
|
|
554
|
+
* document ("File missing") on the page. An EMPTY manifest is a legitimate
|
|
555
|
+
* value and is not null. */
|
|
556
|
+
mediaManifest: MediaManifest | null;
|
|
557
|
+
documentManifest: DocumentManifest | null;
|
|
512
558
|
} | null> {
|
|
513
559
|
const row = await getDb().contentCache.get("current");
|
|
514
560
|
if (!row) return null;
|
|
@@ -522,10 +568,8 @@ export async function getCachedContent(): Promise<{
|
|
|
522
568
|
// No stored mainIndex (e.g. legacy or post-publish cache) means "no draft",
|
|
523
569
|
// so the published index equals the current index.
|
|
524
570
|
mainIndex: row.mainIndex ?? row.index,
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
// heals on the next fresh load or save.
|
|
528
|
-
documentManifest: row.documentManifest ?? {},
|
|
571
|
+
mediaManifest: row.mediaManifest ?? null,
|
|
572
|
+
documentManifest: row.documentManifest ?? null,
|
|
529
573
|
};
|
|
530
574
|
}
|
|
531
575
|
|
|
@@ -536,16 +580,6 @@ export async function clearContentCache(): Promise<void> {
|
|
|
536
580
|
await getDb().contentCache.clear();
|
|
537
581
|
}
|
|
538
582
|
|
|
539
|
-
export async function persistMediaManifest(manifest: MediaManifest): Promise<void> {
|
|
540
|
-
const now = new Date().toISOString();
|
|
541
|
-
await getDb().mediaManifest.put({ key: "current", manifest, updatedAt: now });
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
export async function getMediaManifest(): Promise<MediaManifest | null> {
|
|
545
|
-
const row = await getDb().mediaManifest.get("current");
|
|
546
|
-
return row?.manifest ?? null;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
583
|
export async function addPendingMediaItem(
|
|
550
584
|
item: MediaItem,
|
|
551
585
|
localUrls: Record<string, string> = {},
|