@nuasite/cms 0.27.0 → 0.29.0

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.
@@ -1,134 +0,0 @@
1
- /**
2
- * Vite SSR module cache invalidation + content-sync coordination.
3
- *
4
- * Astro's content layer chain (chokidar → glob loader → syncData → data store
5
- * → fs.watch → invalidateModule) is racy and unreliable under several conditions:
6
- *
7
- * - Native fs.watch on Linux dies after the first atomic rename of the watched
8
- * file (Astro writes data-store.json via writeFile-tmp + rename).
9
- * - Vite's bundled chokidar 3.6.0 misses the same atomic-write events.
10
- * - `invalidateModule(astro:data-layer-content)` alone does not propagate up
11
- * the import graph, so route modules that already cached `getCollection`
12
- * references keep returning stale data.
13
- *
14
- * This module exposes two things:
15
- *
16
- * - `invalidateContentCache(server)` — walks the SSR module graph from
17
- * `astro:data-layer-content` upward and invalidates every transitive
18
- * importer, then broadcasts `full-reload` to the client.
19
- * - `notifyContentStoreUpdated` / `awaitNextContentStoreUpdate` — a shared
20
- * rendezvous between the fs.watch plugin (which observes data-store.json
21
- * writes) and the CMS API middleware (which needs to hold the HTTP
22
- * response until the store is fresh). Keeps invalidation on a single path.
23
- */
24
-
25
- interface SsrModuleNode {
26
- id: string | null
27
- importers: Set<SsrModuleNode>
28
- }
29
-
30
- interface SsrModuleGraph {
31
- getModuleById(id: string): SsrModuleNode | undefined
32
- invalidateModule(
33
- mod: SsrModuleNode,
34
- seen?: Set<SsrModuleNode>,
35
- timestamp?: number,
36
- isHmr?: boolean,
37
- ): void
38
- }
39
-
40
- interface SsrEnvironment {
41
- moduleGraph: SsrModuleGraph
42
- hot: { send: (event: string, data?: unknown) => void }
43
- }
44
-
45
- interface ClientEnvironment {
46
- hot: { send: (payload: { type: string; path: string }) => void }
47
- }
48
-
49
- export interface ViteServerLike {
50
- environments: { ssr: SsrEnvironment; client: ClientEnvironment }
51
- }
52
-
53
- // Astro exposes the content data store as a virtual module whose resolved id
54
- // is `\0astro:data-layer-content` (see astro/dist/content/consts.js). Earlier
55
- // versions of this file used `\0astro:data-store`, which does not exist and
56
- // silently reduced `invalidateContentCache` to a no-op full-reload broadcast.
57
- const DATA_STORE_VIRTUAL_ID = '\0astro:data-layer-content'
58
-
59
- /**
60
- * Invalidate the SSR `astro:data-layer-content` virtual module and every
61
- * module that (transitively) imports it. After this returns, the next request
62
- * that imports any of these modules will re-execute and read fresh content.
63
- *
64
- * Also broadcasts `full-reload` so any connected browser refreshes.
65
- */
66
- export function invalidateContentCache(server: ViteServerLike): void {
67
- const ssr = server.environments.ssr
68
- const dataStoreMod = ssr.moduleGraph.getModuleById(DATA_STORE_VIRTUAL_ID)
69
- if (dataStoreMod) {
70
- const seen = new Set<SsrModuleNode>()
71
- const ts = Date.now()
72
- const walk = (mod: SsrModuleNode) => {
73
- if (seen.has(mod)) return
74
- seen.add(mod)
75
- ssr.moduleGraph.invalidateModule(mod, seen, ts, true)
76
- for (const importer of mod.importers) {
77
- walk(importer)
78
- }
79
- }
80
- walk(dataStoreMod)
81
- }
82
- ssr.hot.send('astro:content-changed', {})
83
- server.environments.client.hot.send({ type: 'full-reload', path: '*' })
84
- }
85
-
86
- // ---------------------------------------------------------------------------
87
- // Content-sync rendezvous
88
- // ---------------------------------------------------------------------------
89
- //
90
- // The CMS API middleware writes a content file and then needs to hold the HTTP
91
- // response until Astro has actually re-synced the data store — otherwise the
92
- // browser reloads into a stale render. The fs.watch plugin is the component
93
- // that observes the data-store.json write, so it is also the component that
94
- // resolves these waiters.
95
-
96
- type StoreUpdateResolver = () => void
97
- const pendingStoreUpdateWaiters = new Set<StoreUpdateResolver>()
98
-
99
- /**
100
- * Called by the data-store fs.watch plugin after it has invalidated the SSR
101
- * module cache in response to a data-store.json write. Wakes every middleware
102
- * caller currently parked in `awaitNextContentStoreUpdate`.
103
- */
104
- export function notifyContentStoreUpdated(): void {
105
- if (pendingStoreUpdateWaiters.size === 0) return
106
- const resolvers = Array.from(pendingStoreUpdateWaiters)
107
- pendingStoreUpdateWaiters.clear()
108
- for (const resolve of resolvers) resolve()
109
- }
110
-
111
- /**
112
- * Park until the next data-store.json write has been fully processed (store
113
- * reloaded on disk, SSR module graph invalidated). Resolves with `true` on
114
- * success or `false` if the timeout elapses first — callers should treat
115
- * timeout as "best-effort, proceed anyway".
116
- *
117
- * The timeout fallback exists because some edits legitimately do not change
118
- * the data store (e.g. whitespace-only edits are skipped by Astro's atomic
119
- * write comparator), in which case no fs.watch event will ever fire.
120
- */
121
- export function awaitNextContentStoreUpdate(timeoutMs: number): Promise<boolean> {
122
- return new Promise((resolve) => {
123
- const resolver = () => {
124
- clearTimeout(timer)
125
- pendingStoreUpdateWaiters.delete(resolver)
126
- resolve(true)
127
- }
128
- const timer = setTimeout(() => {
129
- pendingStoreUpdateWaiters.delete(resolver)
130
- resolve(false)
131
- }, timeoutMs)
132
- pendingStoreUpdateWaiters.add(resolver)
133
- })
134
- }