@omg-dev/server 0.4.35 → 0.4.37

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/index.mjs CHANGED
@@ -831,6 +831,7 @@ var VibesAuthRequiredError = class extends Error {
831
831
  function openDb(path) {
832
832
  const bun = new Database(path, { create: true });
833
833
  bun.exec("PRAGMA journal_mode=WAL;");
834
+ bun.exec("PRAGMA busy_timeout=5000;");
834
835
  function now() {
835
836
  return (/* @__PURE__ */ new Date()).toISOString();
836
837
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omg-dev/server",
3
- "version": "0.4.35",
3
+ "version": "0.4.37",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -14,9 +14,9 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@restatedev/restate-sdk": "1.14.5",
17
- "@omg-dev/auth": "0.4.35",
18
- "@omg-dev/schema": "0.4.35",
19
- "@omg-dev/stream": "0.4.35",
17
+ "@omg-dev/auth": "0.4.37",
18
+ "@omg-dev/schema": "0.4.37",
19
+ "@omg-dev/stream": "0.4.37",
20
20
  "web-push": "^3.6.7"
21
21
  },
22
22
  "scripts": {
package/src/db.ts CHANGED
@@ -68,6 +68,13 @@ export function openDb(path: string): VibesDb {
68
68
  const bun = new Database(path, { create: true })
69
69
  // Enable WAL mode for better concurrency
70
70
  bun.exec("PRAGMA journal_mode=WAL;")
71
+ // Litestream is a second SQLite process, not a passive file reader. Its
72
+ // periodic checkpoint briefly owns SQLite's write lock. A connection with
73
+ // the default zero busy timeout turns that valid handoff into SQLITE_BUSY,
74
+ // which made unrelated control-plane writes fail at the checkpoint boundary.
75
+ // Put all writers through SQLite's process-safe lock queue instead of trying
76
+ // to coordinate selected JavaScript call sites inside this process.
77
+ bun.exec("PRAGMA busy_timeout=5000;")
71
78
 
72
79
  function now(): string {
73
80
  return new Date().toISOString()
@@ -0,0 +1,63 @@
1
+ import { afterEach, describe, expect, it } from "bun:test"
2
+ import fs from "node:fs"
3
+ import os from "node:os"
4
+ import path from "node:path"
5
+ import { openDb, type VibesDb } from "../db.ts"
6
+
7
+ const dbPaths: string[] = []
8
+
9
+ function tmpDbPath(): string {
10
+ const dbPath = path.join(os.tmpdir(), `vibes-contention-${crypto.randomUUID()}.db`)
11
+ dbPaths.push(dbPath)
12
+ return dbPath
13
+ }
14
+
15
+ afterEach(() => {
16
+ for (const dbPath of dbPaths.splice(0)) {
17
+ for (const suffix of ["", "-shm", "-wal"]) {
18
+ try {
19
+ fs.unlinkSync(dbPath + suffix)
20
+ } catch {}
21
+ }
22
+ }
23
+ })
24
+
25
+ describe("SQLite writer coordination", () => {
26
+ it("waits for a second process to release Litestream's write lock", async () => {
27
+ const dbPath = tmpDbPath()
28
+ const app: VibesDb = openDb(dbPath)
29
+ app.raw().exec("CREATE TABLE writes (value TEXT)")
30
+
31
+ const locker = Bun.spawn(
32
+ [
33
+ process.execPath,
34
+ "-e",
35
+ `
36
+ import { Database } from "bun:sqlite";
37
+ const db = new Database(process.argv[1]);
38
+ db.exec("BEGIN IMMEDIATE");
39
+ console.log("locked");
40
+ await Bun.sleep(150);
41
+ db.exec("COMMIT");
42
+ db.close();
43
+ `,
44
+ dbPath,
45
+ ],
46
+ { stdout: "pipe", stderr: "pipe" },
47
+ )
48
+
49
+ const reader = locker.stdout.getReader()
50
+ const firstOutput = await reader.read()
51
+ expect(new TextDecoder().decode(firstOutput.value)).toContain("locked")
52
+
53
+ expect(() =>
54
+ app.raw().prepare("INSERT INTO writes (value) VALUES (?)").run("saved"),
55
+ ).not.toThrow()
56
+ expect(await locker.exited).toBe(0)
57
+ expect(
58
+ app.raw().prepare("SELECT value FROM writes").get() as { value: string },
59
+ ).toEqual({ value: "saved" })
60
+
61
+ app.close()
62
+ })
63
+ })