koguma 2.3.3 → 2.3.4

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/src/vite.js CHANGED
@@ -3,34 +3,31 @@ import { createHash } from "node:crypto";
3
3
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  function kogumaHMR(options = {}) {
6
- const HASH_FILENAME = "dbhash";
7
6
  const HASH_DIR = options.kogumaDir ?? ".koguma";
8
- const HASH_SUBPATH = `${HASH_DIR}/${HASH_FILENAME}`;
9
- const SQLITE_GLOB = `**/${HASH_DIR}/.wrangler/state/v3/d1/**/*.sqlite`;
7
+ const HASH_FILE = "dbhash";
8
+ const HASH_SUBPATH = `${HASH_DIR}/${HASH_FILE}`;
10
9
  return {
11
10
  name: "koguma-hmr",
12
11
  configureServer(server) {
13
12
  const root = server.config.root;
14
13
  const hashDir = join(root, HASH_DIR);
15
14
  const hashPath = join(root, HASH_SUBPATH);
16
- server.watcher.add(SQLITE_GLOB);
15
+ const wranglerDir = join(root, HASH_DIR, ".wrangler");
16
+ if (!existsSync(hashDir))
17
+ mkdirSync(hashDir, { recursive: true });
18
+ server.watcher.add(hashDir);
17
19
  server.watcher.on("change", (file) => {
18
- if (!file.includes(".koguma/.wrangler/state/v3/d1") || !file.endsWith(".sqlite"))
20
+ if (file.startsWith(wranglerDir) && file.endsWith(".sqlite")) {
21
+ try {
22
+ const hash = createHash("sha256").update(readFileSync(file)).digest("hex");
23
+ writeFileSync(hashPath, hash);
24
+ } catch {}
19
25
  return;
20
- try {
21
- const hash = createHash("sha256").update(readFileSync(file)).digest("hex");
22
- if (!existsSync(hashDir))
23
- mkdirSync(hashDir, { recursive: true });
24
- writeFileSync(hashPath, hash);
25
- } catch {}
26
+ }
27
+ if (file === hashPath) {
28
+ server.ws.send({ type: "full-reload" });
29
+ }
26
30
  });
27
- server.watcher.add(hashPath);
28
- },
29
- handleHotUpdate({ file, server }) {
30
- if (file.endsWith(HASH_FILENAME) && file.includes(HASH_DIR)) {
31
- server.ws.send({ type: "full-reload" });
32
- return [];
33
- }
34
31
  },
35
32
  config() {
36
33
  return {
package/src/vite.ts CHANGED
@@ -15,18 +15,6 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
15
15
  import { join } from 'node:path';
16
16
  import type { Plugin } from 'vite';
17
17
 
18
- /**
19
- * Watches the wrangler local D1 SQLite file and triggers a full browser reload
20
- * when content actually changes. Rather than watching the binary SQLite directly
21
- * (which churns 3-4× per operation due to WAL writes), it writes a SHA-256 hash
22
- * of the DB to `.koguma/dbhash` and watches that text file as the reload trigger.
23
- *
24
- * Also suppresses Vite HMR for the `content/` directory, since those edits sync
25
- * through koguma's own file watcher → D1 → API, not through the Vite module graph.
26
- *
27
- * Pair with the `server.proxy` pointing `/api` to `http://localhost:8787` so the
28
- * browser refetches fresh content on each reload.
29
- */
30
18
  export interface KogumaHMROptions {
31
19
  /**
32
20
  * Path to the .koguma directory, relative to the Vite project root.
@@ -36,13 +24,18 @@ export interface KogumaHMROptions {
36
24
  kogumaDir?: string;
37
25
  }
38
26
 
27
+ /**
28
+ * Watches the wrangler local D1 SQLite file and triggers a full browser reload
29
+ * when content changes. Uses a SHA-256 hash file in .koguma/dbhash to debounce
30
+ * the multiple WAL writes that SQLite generates per D1 operation.
31
+ *
32
+ * Also suppresses Vite HMR for content/ since those changes sync through
33
+ * koguma's own pipeline → D1 → API, not through the Vite module graph.
34
+ */
39
35
  export function kogumaHMR(options: KogumaHMROptions = {}): Plugin {
40
- const HASH_FILENAME = 'dbhash';
41
36
  const HASH_DIR = options.kogumaDir ?? '.koguma';
42
- const HASH_SUBPATH = `${HASH_DIR}/${HASH_FILENAME}`;
43
-
44
- // Glob passed to chokidar — must NOT go through path.resolve or the ** breaks
45
- const SQLITE_GLOB = `**/${HASH_DIR}/.wrangler/state/v3/d1/**/*.sqlite`;
37
+ const HASH_FILE = 'dbhash';
38
+ const HASH_SUBPATH = `${HASH_DIR}/${HASH_FILE}`;
46
39
 
47
40
  return {
48
41
  name: 'koguma-hmr',
@@ -51,38 +44,37 @@ export function kogumaHMR(options: KogumaHMROptions = {}): Plugin {
51
44
  const root = server.config.root;
52
45
  const hashDir = join(root, HASH_DIR);
53
46
  const hashPath = join(root, HASH_SUBPATH);
47
+ const wranglerDir = join(root, HASH_DIR, '.wrangler');
48
+
49
+ // Ensure .koguma exists so chokidar can watch it
50
+ if (!existsSync(hashDir)) mkdirSync(hashDir, { recursive: true });
54
51
 
55
- // Add the wrangler SQLite glob directly (no resolve globs need literal **)
56
- server.watcher.add(SQLITE_GLOB);
52
+ // Watch .koguma/ directory covers both the SQLite files inside
53
+ // .wrangler/ and the dbhash file we write ourselves.
54
+ server.watcher.add(hashDir);
57
55
 
58
56
  server.watcher.on('change', (file: string) => {
59
- if (
60
- !file.includes('.koguma/.wrangler/state/v3/d1') ||
61
- !file.endsWith('.sqlite')
62
- )
57
+ // SQLite changed → compute hash and write dbhash file.
58
+ // The next change event for dbhash will trigger the reload.
59
+ if (file.startsWith(wranglerDir) && file.endsWith('.sqlite')) {
60
+ try {
61
+ const hash = createHash('sha256')
62
+ .update(readFileSync(file))
63
+ .digest('hex');
64
+ writeFileSync(hashPath, hash);
65
+ } catch {
66
+ // SQLite may be locked mid-write — next write will succeed
67
+ }
63
68
  return;
64
- try {
65
- const hash = createHash('sha256')
66
- .update(readFileSync(file))
67
- .digest('hex');
68
- if (!existsSync(hashDir)) mkdirSync(hashDir, { recursive: true });
69
- writeFileSync(hashPath, hash);
70
- } catch {
71
- // SQLite may be locked mid-write — the next write will succeed
72
69
  }
73
- });
74
70
 
75
- // Watch the hash file Vite does NOT auto-reload non-module files,
76
- // so handleHotUpdate below is required to send the full-reload signal.
77
- server.watcher.add(hashPath);
78
- },
79
-
80
- handleHotUpdate({ file, server }) {
81
- // When the hash file changes, the DB has new content → reload the page
82
- if (file.endsWith(HASH_FILENAME) && file.includes(HASH_DIR)) {
83
- server.ws.send({ type: 'full-reload' });
84
- return [];
85
- }
71
+ // dbhash changed new content is in D1 reload the page.
72
+ // We send directly from here rather than via handleHotUpdate because
73
+ // handleHotUpdate only fires for files in Vite's module graph.
74
+ if (file === hashPath) {
75
+ server.ws.send({ type: 'full-reload' });
76
+ }
77
+ });
86
78
  },
87
79
 
88
80
  config() {