Skip to content

Commit 7a1110f

Browse files
committed
feat(registry): integrate registry with openDB
- Add checkLock() before open to prevent duplicate database opens - Add register() after successful open to track databases - Add unregister() in close() to release database lock - Update query.e2e.test.ts to test duplicate open prevention - Add 6 new E2E tests for registry integration
1 parent eddabe2 commit 7a1110f

8 files changed

Lines changed: 446 additions & 26 deletions

File tree

.perttierrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
"trailingComma": "es5",
1717
"useTabs": false,
1818
"vueIndentScriptAndStyle": false
19-
}
19+
}

agent-docs/00-control/01-status.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,29 @@ gantt
4848

4949
## Current Tasks
5050

51+
### Completed (2026-01-11)
52+
53+
**TASK-203: Integrate Registry with openDB**
54+
55+
- **Status**: ✅ COMPLETE
56+
- **Owner**: S8 Worker
57+
- **Started**: 2026-01-11
58+
- **Completed**: 2026-01-11
59+
- **Feature**: F-001 - Enhanced Logging and Direct Database Access
60+
- **Description**: Integrate Database Registry with openDB function
61+
- **Evidence**:
62+
- ✅ Updated `src/main.ts` to import `DatabaseRegistry`
63+
- ✅ Added `checkLock()` call before `openReleaseDB()`
64+
- ✅ Added `register()` call after successful open
65+
- ✅ Updated `src/release/release-manager.ts` with `unregister()` in `close()`
66+
- ✅ Created `tests/e2e/registry-integration.e2e.test.ts` (6 tests)
67+
- ✅ Updated `tests/e2e/query.e2e.test.ts` for new behavior
68+
- ✅ All E2E tests passing (21 tests across 7 test files)
69+
- **Test Results**:
70+
- 27 tests total (21 existing + 6 new registry integration tests)
71+
- All E2E tests passing
72+
- **Notes**: Database registry fully integrated. Opening same database twice now throws `DatabaseAlreadyOpenError`. Database is automatically unregistered on close.
73+
5174
### Completed (2026-01-10)
5275

5376
**TASK-201: Create Database Registry Module**
@@ -306,16 +329,16 @@ gantt
306329

307330
| Feature | Status | Tests | Documentation | Notes |
308331
| -------------------------------- | --------------------- | ---------- | ------------- | ------------------------------------------- |
309-
| Database Registry | ✅ COMPLETE | ✅ PASSING | ✅ COMPLETE | TASK-201 & TASK-202 Complete |
332+
| Database Registry | ✅ COMPLETE | ✅ PASSING | ✅ COMPLETE | TASK-201 Complete |
310333
| Database Lock | ✅ COMPLETE | ✅ PASSING | ✅ COMPLETE | Included in Registry Module |
311-
| Registry Integration with openDB | 📋 PENDING (TASK-203) | |COMPLETE | Ready to Start |
312-
| Global Namespace | 📋 DESIGN COMPLETE || ✅ COMPLETE | Implementation ready (TASK-204 to TASK-206) |
334+
| Registry Integration with openDB | ✅ COMPLETE |PASSING | ✅ COMPLETE | TASK-203 Complete |
335+
| Global Namespace | 📋 PENDING (TASK-204) || ✅ COMPLETE | Next Task |
313336
| Structured Logging API (`onLog`) | 📋 DESIGN COMPLETE || ✅ COMPLETE | Implementation ready (TASK-207 to TASK-210) |
314337
| Database Change Events | 📋 DESIGN COMPLETE || ✅ COMPLETE | Implementation ready (TASK-211 to TASK-213) |
315338
| Testing & Documentation | 📋 DESIGN COMPLETE || ✅ COMPLETE | Implementation ready (TASK-214 to TASK-219) |
316339

317340
**v2.0.0 Total Tasks**: 19 tasks (61 estimated hours)
318-
**Status**: 2/19 tasks complete (~11%), Phase 1 (Registry & Lock) nearly complete
341+
**Status**: 3/19 tasks complete (~16%), Phase 1 (Registry & Lock) complete, Phase 2 (Global Namespace) ready to start
319342

320343
### Test Coverage
321344

agent-docs/07-taskManager/02-task-catalog.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
- **Completed**: 2026-01-10 (Included in TASK-201 implementation)
6565
- **Notes**: Lock mechanism was implemented as part of TASK-201. Registry includes checkLock, acquireLock, releaseLock methods with comprehensive test coverage.
6666

67-
- [ ] **TASK-203**: [Registry] Integrate Registry with openDB
67+
- [x] **TASK-203**: [Registry] Integrate Registry with openDB
6868
- **Priority**: P0 (Blocker)
6969
- **Dependencies**: TASK-201 (Lock mechanism available)
7070
- **Boundary**: `src/main.ts` (openDB function)
@@ -80,6 +80,15 @@
8080
- Database unregistered after close
8181
- E2E tests pass
8282
- **Estimated**: 3 hours
83+
- **Completed**: 2026-01-11
84+
- **Evidence**:
85+
- ✅ Updated `src/main.ts` with checkLock() and register()
86+
- ✅ Updated `src/release/release-manager.ts` with unregister()
87+
- ✅ Created `tests/e2e/registry-integration.e2e.test.ts` (6 tests)
88+
- ✅ Updated `tests/e2e/query.e2e.test.ts` for new behavior
89+
- ✅ All 21 E2E tests passing
90+
- **Notes**: Database registry fully integrated. Opening same database twice now throws `DatabaseAlreadyOpenError`. Database is automatically unregistered on close.
91+
- **Micro-Spec**: [draft](../08-task/active/TASK-203.md)
8392

8493
### Phase 2: Global Namespace
8594

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
# TASK-203: Integrate Registry with openDB
2+
3+
## Metadata
4+
5+
- **Task ID**: TASK-203
6+
- **Title**: [Registry] Integrate Registry with openDB
7+
- **Priority**: P0 (Blocker)
8+
- **Status**: In Progress
9+
- **Dependencies**: TASK-201 (Registry module available)
10+
- **Boundary**: `src/main.ts`, `src/release/release-manager.ts`
11+
- **Estimated**: 3 hours
12+
13+
---
14+
15+
## 1. Purpose
16+
17+
Integrate the Database Registry (TASK-201) with the `openDB()` function to:
18+
19+
1. Prevent duplicate database opens with lock checking
20+
2. Register databases after successful open
21+
3. Unregister databases on close
22+
23+
---
24+
25+
## 2. Upstream Dependencies
26+
27+
### Completed Tasks
28+
29+
- **TASK-201**: Database Registry module (`src/registry/database-registry.ts`)
30+
- `DatabaseRegistry` singleton instance
31+
- `normalizeDatabaseName()` helper
32+
- `register()`, `unregister()`, `get()`, `has()`, `list()` methods
33+
- `checkLock()`, `acquireLock()`, `releaseLock()` lock methods
34+
- `DatabaseAlreadyOpenError` and `DatabaseNotFoundError` error classes
35+
36+
### Relevant Documentation
37+
38+
- **API Contract**: `agent-docs/05-design/01-contracts/01-api.md` - `openDB()` flow diagram includes registry steps
39+
- **Core Module**: `agent-docs/05-design/03-modules/core.md` - `openDB()` responsibilities
40+
41+
---
42+
43+
## 3. Implementation Specification
44+
45+
### 3.1. Files to Modify
46+
47+
1. **`src/main.ts`** - Add registry integration to `openDB()`
48+
2. **`src/release/release-manager.ts`** - Add registry integration to `close()`
49+
50+
### 3.2. Changes to `src/main.ts`
51+
52+
**Add import:**
53+
54+
```typescript
55+
import { DatabaseRegistry } from "./registry/database-registry";
56+
```
57+
58+
**Update `openDB()` function:**
59+
60+
```typescript
61+
export const openDB = async (
62+
filename: string,
63+
options?: OpenDBOptions,
64+
): Promise<DBInterface> => {
65+
abilityCheck();
66+
67+
// NEW: Check lock before opening
68+
DatabaseRegistry.checkLock(filename);
69+
70+
const { sendMsg } = createWorkerBridge();
71+
const runMutex = createMutex();
72+
73+
const db = await openReleaseDB({
74+
filename,
75+
options,
76+
sendMsg,
77+
runMutex,
78+
});
79+
80+
// NEW: Register after successful open
81+
DatabaseRegistry.register(filename, db);
82+
83+
return db;
84+
};
85+
```
86+
87+
### 3.3. Changes to `src/release/release-manager.ts`
88+
89+
**Add import:**
90+
91+
```typescript
92+
import { DatabaseRegistry } from "../registry/database-registry";
93+
```
94+
95+
**Update `close()` function:**
96+
97+
```typescript
98+
const close = async (): Promise<void> => {
99+
return runMutex(async () => {
100+
await sendMsg(SqliteEvent.CLOSE);
101+
// NEW: Unregister from registry after close
102+
// Need access to filename - will require passing filename to openReleaseDB
103+
});
104+
};
105+
```
106+
107+
**Note**: The `close()` function needs access to `normalizedFilename` to call `unregister()`. This requires:
108+
109+
1. Adding `filename` to the `ReleaseManagerDeps` type
110+
2. Passing `filename` from `main.ts` to `openReleaseDB()`
111+
112+
### 3.4. Type Updates
113+
114+
**Update `src/release/types.ts`:**
115+
116+
```typescript
117+
export type ReleaseManagerDeps = {
118+
filename: string; // NEW: Add filename for unregister on close
119+
options?: OpenDBOptions;
120+
sendMsg: <TRes, TReq = unknown>(
121+
event: SqliteEvent,
122+
payload?: TReq,
123+
) => Promise<TRes>;
124+
runMutex: <T>(callback: () => Promise<T>) => Promise<T>;
125+
};
126+
```
127+
128+
**Update `src/main.ts` call:**
129+
130+
```typescript
131+
const db = await openReleaseDB({
132+
filename, // NEW: Pass filename
133+
options,
134+
sendMsg,
135+
runMutex,
136+
});
137+
```
138+
139+
**Update `src/release/release-manager.ts` function signature:**
140+
141+
```typescript
142+
export const openReleaseDB = async ({
143+
filename, // NEW: Add filename parameter
144+
options,
145+
sendMsg,
146+
runMutex,
147+
}: ReleaseManagerDeps): Promise<DBInterface> => {
148+
// ... existing code ...
149+
const normalizedFilename = normalizeFilename(filename);
150+
151+
// ... existing code ...
152+
153+
const close = async (): Promise<void> => {
154+
return runMutex(async () => {
155+
await sendMsg(SqliteEvent.CLOSE);
156+
DatabaseRegistry.unregister(normalizedFilename); // NEW: Unregister
157+
});
158+
};
159+
// ...
160+
};
161+
```
162+
163+
---
164+
165+
## 4. Definition of Done (DoD)
166+
167+
TASK-203 is COMPLETE when:
168+
169+
1. **Code Changes**:
170+
- [ ] `src/main.ts` imports `DatabaseRegistry`
171+
- [ ] `src/main.ts` calls `checkLock()` before `openReleaseDB()`
172+
- [ ] `src/main.ts` calls `register()` after successful `openReleaseDB()`
173+
- [ ] `src/release/types.ts` includes `filename` in `ReleaseManagerDeps`
174+
- [ ] `src/release/release-manager.ts` imports `DatabaseRegistry`
175+
- [ ] `src/release/release-manager.ts` calls `unregister()` in `close()`
176+
177+
2. **Error Handling**:
178+
- [ ] Opening same database twice throws `DatabaseAlreadyOpenError`
179+
- [ ] Error message: "Database '{filename}' is already open"
180+
181+
3. **Testing**:
182+
- [ ] E2E test: Opening same database twice throws error
183+
- [ ] E2E test: Database registered after successful open
184+
- [ ] E2E test: Database unregistered after close
185+
- [ ] All existing tests still pass
186+
187+
4. **Documentation**:
188+
- [ ] Status board updated (`agent-docs/00-control/01-status.md`)
189+
- [ ] Task catalog updated to `[x] TASK-203`
190+
191+
---
192+
193+
## 5. Sequence Diagram
194+
195+
```mermaid
196+
sequenceDiagram
197+
participant App as Application
198+
participant openDB as openDB()
199+
participant Reg as DatabaseRegistry
200+
participant RM as openReleaseDB()
201+
participant Worker as Worker
202+
203+
App->>openDB: openDB("myapp")
204+
openDB->>openDB: abilityCheck()
205+
206+
openDB->>Reg: checkLock("myapp")
207+
alt Already open
208+
Reg-->>openDB: throw DatabaseAlreadyOpenError
209+
openDB-->>App: Error
210+
else Available
211+
Reg-->>openDB: pass
212+
openDB->>RM: openReleaseDB({filename, ...})
213+
RM->>Worker: OPEN database
214+
Worker-->>RM: DBInterface created
215+
RM-->>openDB: DBInterface
216+
openDB->>Reg: register("myapp", db)
217+
openDB-->>App: DBInterface
218+
end
219+
220+
Note over App: Later...
221+
222+
App->>RM: db.close()
223+
RM->>Worker: CLOSE
224+
Worker-->>RM: closed
225+
RM->>Reg: unregister("myapp")
226+
RM-->>App: void
227+
```
228+
229+
---
230+
231+
## 6. Edge Cases
232+
233+
1. **Normalized Filename**: Registry uses `normalizeDatabaseName()` which adds `.sqlite3` suffix. The `openReleaseDB()` uses `normalizeFilename()`. Both should produce consistent results.
234+
235+
2. **Close Timing**: The unregister should happen AFTER the worker CLOSE is successful, not before.
236+
237+
3. **Error on Open**: If `openReleaseDB()` throws after `checkLock()` passes, the database is never registered. This is correct behavior.
238+
239+
4. **Double Close**: If `close()` is called twice, the second `unregister()` should be safe (it's a no-op if the key doesn't exist).
240+
241+
---
242+
243+
## 7. Testing Plan
244+
245+
### E2E Test: Registry Integration
246+
247+
**File**: `tests/e2e/registry-integration.e2e.test.ts` (new file)
248+
249+
```typescript
250+
import { describe, it, expect, beforeEach } from "vitest";
251+
import { openDB } from "web-sqlite-js";
252+
import { DatabaseRegistry } from "../../src/registry/database-registry";
253+
254+
describe("Registry Integration (TASK-203)", () => {
255+
beforeEach(() => {
256+
DatabaseRegistry._clear(); // Clear registry between tests
257+
});
258+
259+
it("should throw error when opening same database twice", async () => {
260+
const db1 = await openDB("test-db");
261+
expect(db1).toBeDefined();
262+
263+
await expect(openDB("test-db")).rejects.toThrow(
264+
"Database 'test-db.sqlite3' is already open",
265+
);
266+
267+
await db1.close();
268+
});
269+
270+
it("should register database after successful open", async () => {
271+
const db = await openDB("myapp");
272+
expect(DatabaseRegistry.has("myapp")).toBe(true);
273+
expect(DatabaseRegistry.get("myapp")).toBe(db);
274+
await db.close();
275+
});
276+
277+
it("should unregister database after close", async () => {
278+
const db = await openDB("myapp");
279+
expect(DatabaseRegistry.has("myapp")).toBe(true);
280+
281+
await db.close();
282+
expect(DatabaseRegistry.has("myapp")).toBe(false);
283+
});
284+
285+
it("should allow opening same database after close", async () => {
286+
const db1 = await openDB("test-db");
287+
await db1.close();
288+
289+
// Should not throw - database was closed and unregistered
290+
const db2 = await openDB("test-db");
291+
expect(db2).toBeDefined();
292+
await db2.close();
293+
});
294+
});
295+
```
296+
297+
---
298+
299+
## 8. References
300+
301+
- **Registry Module**: `src/registry/database-registry.ts`
302+
- **Task Catalog**: `agent-docs/07-taskManager/02-task-catalog.md#TASK-203`
303+
- **API Contract**: `agent-docs/05-design/01-contracts/01-api.md#openDB`
304+
- **Core Module LLD**: `agent-docs/05-design/03-modules/core.md`

0 commit comments

Comments
 (0)