mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-04-17 15:20:58 +00:00
106 lines
4.7 KiB
Markdown
106 lines
4.7 KiB
Markdown
# Phase 01 — D1 Setup
|
|
|
|
**Priority:** P0 (blocker for 02/03/04)
|
|
**Status:** Complete
|
|
|
|
## Overview
|
|
|
|
Wire Cloudflare D1 into the framework: binding, per-module migrations, `SqlStore` factory mirroring the `KVStore` shape, Miniflare-backed tests.
|
|
|
|
## Key Insights
|
|
|
|
- D1 is SQLite at the edge; prepared statements + `db.prepare().bind().all()/first()/run()`.
|
|
- `vitest-pool-workers` (or plain Miniflare) exposes D1 in tests without real Cloudflare calls.
|
|
- Per-module table prefixing mirrors the existing KV prefixing — module authors never touch raw `env.DB`.
|
|
|
|
## Requirements
|
|
|
|
**Functional**
|
|
- Module init receives `sql` alongside `db` in `init({ db, sql, env })`.
|
|
- `sql.prepare(query, ...binds)` / `sql.run(query, ...binds)` / `sql.all(query, ...binds)` / `sql.first(query, ...binds)`.
|
|
- Table names referenced in queries are left literal — authors write `trading_trades` directly (prefix is convention, not rewriting).
|
|
- `sql.tablePrefix` exposed for authors who want to interpolate.
|
|
- Migrations auto-discovered from `src/modules/*/migrations/*.sql`, applied via `wrangler d1 migrations apply` in deploy script.
|
|
|
|
**Non-functional**
|
|
- Zero overhead when a module does not use SQL.
|
|
- Tests run fully offline against Miniflare.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
src/db/
|
|
├── kv-store-interface.js # existing
|
|
├── cf-kv-store.js # existing
|
|
├── create-store.js # existing (KV)
|
|
├── sql-store-interface.js # NEW — JSDoc typedef
|
|
├── cf-sql-store.js # NEW — wraps env.DB
|
|
└── create-sql-store.js # NEW — factory, sets tablePrefix = `${moduleName}_`
|
|
```
|
|
|
|
`createSqlStore(moduleName, env)` returns an object exposing `prepare`, `run`, `all`, `first`, `batch`, `tablePrefix`.
|
|
|
|
## Related Code Files
|
|
|
|
**Create**
|
|
- `src/db/sql-store-interface.js`
|
|
- `src/db/cf-sql-store.js`
|
|
- `src/db/create-sql-store.js`
|
|
- `tests/db/create-sql-store.test.js`
|
|
- `tests/fakes/fake-d1.js` (Miniflare D1 helper for tests)
|
|
|
|
**Modify**
|
|
- `src/modules/registry.js` — pass `sql` into `init({ db, sql, env })`
|
|
- `src/modules/dispatcher.js` — same
|
|
- `wrangler.toml` — add `[[d1_databases]]` block, `migrations_dir` optional
|
|
- `package.json` — add `db:migrate` script: `wrangler d1 migrations apply miti99bot-db --remote`; chain into `deploy`
|
|
- `scripts/register.js` — no change needed, but verify no breakage
|
|
|
|
## Implementation Steps
|
|
|
|
1. Create D1 database: `npx wrangler d1 create miti99bot-db`. Record UUID in `wrangler.toml`.
|
|
2. Author `sql-store-interface.js` with JSDoc `@typedef` for `SqlStore`.
|
|
3. Implement `cf-sql-store.js` — thin wrapper around `env.DB.prepare()`.
|
|
4. Implement `create-sql-store.js` — returns wrapper + `tablePrefix`.
|
|
5. Update `registry.js` + `dispatcher.js` to pass `sql` into module `init` + command handler contexts (via `ctx.sql`? **decision below**).
|
|
6. Add migration discovery: walk `src/modules/*/migrations/` at deploy time, consolidate into a central `migrations/` or use wrangler's default per-dir.
|
|
7. Wire `db:migrate` into `npm run deploy`: `wrangler deploy && npm run db:migrate && npm run register`.
|
|
8. Add `fake-d1.js` using `@miniflare/d1` or `better-sqlite3`-backed fake.
|
|
9. Tests: `create-sql-store.test.js` verifying prefix exposure + basic CRUD.
|
|
|
|
## Open Decisions
|
|
|
|
- **Command handler access to `sql`:** expose via `ctx.sql` (grammY context extension in dispatcher) or require modules to close over `sql` captured in `init`? Lean **close over in init** — matches how `db` is currently used.
|
|
- **Migration runner:** wrangler's native `d1 migrations apply` requires a single `migrations_dir`. Options:
|
|
- (a) consolidate all per-module SQL into root `migrations/` at build time via a prebuild script.
|
|
- (b) custom runner script that applies each `src/modules/*/migrations/*.sql` in order.
|
|
- **Lean (b)** — keeps per-module locality.
|
|
|
|
## Todo List
|
|
|
|
- [x] Create D1 database + update `wrangler.toml`
|
|
- [x] `sql-store-interface.js` with typedefs
|
|
- [x] `cf-sql-store.js` implementation
|
|
- [x] `create-sql-store.js` factory
|
|
- [x] Update `registry.js` init signature
|
|
- [x] Update `dispatcher.js` to pass `sql` (no change needed — delegates to buildRegistry)
|
|
- [x] Write custom migration runner at `scripts/migrate.js`
|
|
- [x] Wire into `npm run deploy`
|
|
- [x] `fake-d1.js` test helper
|
|
- [x] Unit tests for `create-sql-store`
|
|
|
|
## Success Criteria
|
|
|
|
- A module can define `init({ sql }) => sql.run("INSERT INTO mymod_foo VALUES (?)", "x")` and it works in dev + test + prod.
|
|
- `npm test` green.
|
|
- No regression in existing KV-only modules.
|
|
|
|
## Risks
|
|
|
|
- Wrangler migration tooling may not support per-module layout → fallback to custom runner.
|
|
- D1 read-after-write consistency in eventually-consistent replicas — document for module authors.
|
|
|
|
## Next Steps
|
|
|
|
- Phase 02 can start once `sql` threads through `init`.
|