@programinglive/dev-workflow-mcp-server 1.7.10 → 1.7.11

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/.env.example ADDED
@@ -0,0 +1,11 @@
1
+ # Database Configuration
2
+ # Supported types: sqlite, mysql, postgres
3
+ DEV_WORKFLOW_DB_TYPE=sqlite
4
+
5
+ # Connection URLs
6
+ # MySQL Example: mysql://user:password@localhost:3306/database
7
+ # Postgres Example: postgres://user:password@localhost:5432/database
8
+ DEV_WORKFLOW_DB_URL=
9
+
10
+ # Optional: Override user ID
11
+ # DEV_WORKFLOW_USER_ID=my-custom-user-id
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.7.11](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.10...v1.7.11) (2025-12-16)
6
+
7
+
8
+ ### šŸ“ Documentation
9
+
10
+ * fix version status in PRD ([fa7912b](https://github.com/programinglive/dev-workflow-mcp-server/commit/fa7912ba3354a88ce943fbc9a0f34f4bd1936e34))
11
+
12
+
13
+ ### ✨ Features
14
+
15
+ * implement .env support and fix mysql date persistence ([096797c](https://github.com/programinglive/dev-workflow-mcp-server/commit/096797c689813f44106a027cfa36e90d3da4bfb4))
16
+
17
+
18
+ ### āœ… Tests
19
+
20
+ * comprehensive DB adapter tests, env support, and warning fixes ([44814ff](https://github.com/programinglive/dev-workflow-mcp-server/commit/44814ffcc0a1efbc8e89948de22c3b196834b390))
21
+
5
22
  ### [1.7.10](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.9...v1.7.10) (2025-12-16)
6
23
 
7
24
 
package/README.md CHANGED
@@ -183,9 +183,23 @@ See [Antigravity Getting Started](./docs/antigravity/GETTING-STARTED.md) for det
183
183
 
184
184
  ## āš™ļø Configuration
185
185
 
186
- ### Database Backend
186
+ ### Database Setup
187
187
 
188
- By default, the server uses a local **SQLite** database stored in `.state/dev-workflow.db`. You can switch to **MySQL** or **PostgreSQL** for centralized storage using environment variables.
188
+ The server supports **SQLite** (default), **MySQL**, and **PostgreSQL**.
189
+
190
+ #### Configuration via `.env`
191
+ 1. Copy `.env.example` to `.env`:
192
+ ```bash
193
+ cp .env.example .env
194
+ ```
195
+ 2. Edit `.env` with your settings:
196
+ ```ini
197
+ DEV_WORKFLOW_DB_TYPE=mysql
198
+ DEV_WORKFLOW_DB_URL=mysql://user:pass@localhost:3306/db
199
+ ```
200
+
201
+ #### Configuration via Environment Variables
202
+ Alternatively, export variables directly:.
189
203
 
190
204
  | Variable | Description | Default |
191
205
  |----------|-------------|---------|
@@ -252,6 +266,33 @@ Add the environment variable in your Windsurf MCP settings:
252
266
  ```
253
267
 
254
268
 
269
+
270
+ ### āœ… Testing
271
+
272
+ The project uses Node.js native test runner (`node --test`).
273
+
274
+ ```bash
275
+ npm test
276
+ ```
277
+
278
+ This runs:
279
+ - Unit tests for workflow logic
280
+ - Integration tests for `.env` configuration
281
+ - DB Adapter tests (SQLite always; MySQL/Postgres if configured)
282
+
283
+ #### Testing Database Adapters
284
+ To verify MySQL or PostgreSQL adapters, run tests with the environment variable set:
285
+
286
+ ```bash
287
+ # Test MySQL
288
+ export DEV_WORKFLOW_DB_URL="mysql://root:pass@localhost:3306/test_db"
289
+ node --test tests/db-adapters.test.js
290
+
291
+ # Test PostgreSQL
292
+ export DEV_WORKFLOW_DB_URL="postgres://postgres:pass@localhost:5432/test_db"
293
+ node --test tests/db-adapters.test.js
294
+ ```
295
+
255
296
  ### Scripts
256
297
 
257
298
  - `npm run build` - Bundle the source into `dist/index.mjs` for distribution
@@ -75,6 +75,13 @@ export class MysqlAdapter extends DbAdapter {
75
75
  }
76
76
  }
77
77
 
78
+ formatDate(date) {
79
+ // Ensure we have a string in 'YYYY-MM-DD HH:mm:ss' format
80
+ const d = date ? new Date(date) : new Date();
81
+ // slice(0, 19) gives YYYY-MM-DDTHH:mm:ss, then replace T with space
82
+ return d.toISOString().slice(0, 19).replace('T', ' ');
83
+ }
84
+
78
85
  async insertHistoryEntry(userId, projectPath, entry) {
79
86
  await this.connect();
80
87
  const normalizedProjectPath = projectPath || '';
@@ -98,7 +105,7 @@ export class MysqlAdapter extends DbAdapter {
98
105
  entry.taskDescription,
99
106
  entry.taskType,
100
107
  entry.commitMessage,
101
- entry.timestamp || new Date().toISOString(),
108
+ this.formatDate(entry.timestamp),
102
109
  entry.forced ? 1 : 0,
103
110
  entry.forceReason || null,
104
111
  entry.dropped ? 1 : 0,
@@ -150,9 +157,9 @@ export class MysqlAdapter extends DbAdapter {
150
157
  normalizedProjectPath,
151
158
  rows.length,
152
159
  JSON.stringify(taskTypes),
153
- lastActive,
160
+ this.formatDate(lastActive),
154
161
  JSON.stringify(recentTasks),
155
- new Date().toISOString()
162
+ this.formatDate(new Date())
156
163
  ]);
157
164
  }
158
165
 
@@ -270,7 +277,7 @@ export class MysqlAdapter extends DbAdapter {
270
277
  userId,
271
278
  normalizedProjectPath,
272
279
  JSON.stringify(state),
273
- new Date().toISOString()
280
+ this.formatDate(new Date())
274
281
  ]);
275
282
  }
276
283
 
@@ -4,7 +4,7 @@
4
4
  - **Product**: Development Workflow MCP Server
5
5
  - **Prepared by**: Cascade (AI assistant)
6
6
  - **Date**: 2025-12-16
7
- - **Status**: Live v1.6+
7
+ - **Status**: Live v1.7.10
8
8
 
9
9
  ## 2. Problem Statement
10
10
  Developers need structured guidance to maintain disciplined workflows across projects. Existing tooling lacks enforcement of sequential best practices (testing, documentation, release hygiene) and offers limited visibility into historical task execution.
@@ -38,6 +38,10 @@ Developers need structured guidance to maintain disciplined workflows across pro
38
38
  - Guard release commands to ensure prerequisites are satisfied.
39
39
  - Support `run_full_workflow` for scripted execution of all steps with validation.
40
40
  - Web dashboard displays version dynamically from `package.json` via `/api/version` endpoint to stay in sync after releases.
41
+ - [x] **Consolidated CI/CD**: Unified release and publish workflows.
42
+ - [x] **Multi-Database Support**: SQLite, MySQL, and PostgreSQL adapters implemented and verified.
43
+ - [x] **Environment Configuration**: `.env` file support added.
44
+ - [x] **Comprehensive Testing**: Automated tests for DB adapters and environment loading.
41
45
  - **Multi-Database Support**: Abstract the storage layer to support:
42
46
  - SQLite (default/local)
43
47
  - MySQL
@@ -4,6 +4,7 @@ Authoritative history of user-visible changes for the Development Workflow MCP S
4
4
 
5
5
  | Version | Date | Notes |
6
6
  |---------|------|-------|
7
+ | 1.7.10 | 2025-12-16 | update release notes and finalize ci config (92ac78a) |
7
8
  | 1.7.9 | 2025-12-16 | **ci:** apply official npm trusted publishing config (9256583) |
8
9
  | 1.7.8 | 2025-12-16 | **ci:** remove all token config for pure OIDC publishing (d4759cc) |
9
10
  | 1.7.7 | 2025-12-16 | **ci:** remove duplicates and apply robust trusted publishing config (eedb661) |
@@ -31,6 +32,14 @@ Authoritative history of user-visible changes for the Development Workflow MCP S
31
32
 
32
33
 
33
34
 
35
+
36
+ ## 1.7.10 – 🧹 Chores
37
+
38
+ Released on **2025-12-16**.
39
+
40
+ - update release notes and finalize ci config (92ac78a)
41
+ - update PRD and release notes for v1.7.9 (024b3c2)
42
+
34
43
  ## 1.7.9 – šŸ› Bug Fixes
35
44
 
36
45
  Released on **2025-12-16**.
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import "dotenv/config";
4
+
3
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
7
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@programinglive/dev-workflow-mcp-server",
3
- "version": "1.7.10",
3
+ "version": "1.7.11",
4
4
  "description": "MCP server to enforce development workflow discipline",
5
5
  "homepage": "https://devworkflow.programinglive.com/",
6
6
  "mcpName": "io.github.programinglive/dev-workflow-mcp-server",
@@ -17,7 +17,7 @@
17
17
  "dev": "node --watch index.js",
18
18
  "start": "node index.js",
19
19
  "local": "node index.js",
20
- "test": "node --test",
20
+ "test": "node --no-deprecation --test",
21
21
  "check": "node --check index.js",
22
22
  "prepare": "husky",
23
23
  "postinstall": "node install.js",
@@ -43,6 +43,7 @@
43
43
  "dependencies": {
44
44
  "@modelcontextprotocol/sdk": "^1.0.0",
45
45
  "better-sqlite3": "^11.8.0",
46
+ "dotenv": "^17.2.3",
46
47
  "esbuild": "^0.25.0",
47
48
  "get-port": "^7.1.0",
48
49
  "marked": "^12.0.2",
@@ -0,0 +1,108 @@
1
+ import "dotenv/config";
2
+ import test from "node:test";
3
+ import assert from "node:assert/strict";
4
+ import path from "node:path";
5
+ import fs from "node:fs/promises";
6
+ import os from "node:os";
7
+
8
+ import { SqliteAdapter } from "../db/adapters/sqlite.js";
9
+ import { MysqlAdapter } from "../db/adapters/mysql.js";
10
+ import { PostgresAdapter } from "../db/adapters/postgres.js";
11
+
12
+
13
+ // Helper to run common adapter tests
14
+ async function runGenericAdapterTests(t, adapter, testUserId, testProject) {
15
+ await t.test("connects and migrates", async () => {
16
+ await adapter.connect();
17
+ // No error means success
18
+ });
19
+
20
+ await t.test("inserts and retrieves history", async () => {
21
+ const entry = {
22
+ taskDescription: "Test Task",
23
+ taskType: "feature",
24
+ commitMessage: "feat: test",
25
+ timestamp: new Date().toISOString(),
26
+ };
27
+ await adapter.insertHistoryEntry(testUserId, testProject, entry);
28
+
29
+ const history = await adapter.getHistoryForUser(testUserId, testProject, { pageSize: 1 });
30
+ assert.equal(history.entries.length, 1);
31
+ assert.equal(history.entries[0].task_description, "Test Task");
32
+ });
33
+
34
+ await t.test("saves and retrieves state", async () => {
35
+ const state = { phase: "testing", count: 123 };
36
+ await adapter.saveState(testUserId, testProject, state);
37
+ const retrieved = await adapter.getState(testUserId, testProject);
38
+ assert.deepEqual(retrieved, state);
39
+ });
40
+
41
+ await t.test("history summary aggregation", async () => {
42
+ // already inserted one entry
43
+ const summary = await adapter.getHistorySummary(testUserId, testProject, { frequency: 'daily' });
44
+ assert.ok(summary.length > 0);
45
+ });
46
+
47
+ await t.test("project summary update", async () => {
48
+ const summary = await adapter.getSummaryForUser(testUserId, testProject);
49
+ assert.ok(summary);
50
+ assert.ok(summary.totalTasks >= 1);
51
+ });
52
+ }
53
+
54
+ test("Database Adapters", async (t) => {
55
+ // 1. SQLite (Always Test)
56
+ await t.test("SqliteAdapter", async (t) => {
57
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "sqlite-test-"));
58
+ const dbPath = path.join(tmpDir, "test.db");
59
+
60
+ // Convert to file URI for connection string if needed, or just pass path
61
+ // The adapter expects a connection URL for SQLite usually: file:///path/to/db or just path?
62
+ // Looking at sqlite.js: uri: this.config.connectionUrl.replace("sqlite://", "")
63
+ // So we invoke it with sqlite://<path>
64
+ const connectionUrl = `sqlite://${dbPath}`;
65
+
66
+ const adapter = new SqliteAdapter({ connectionUrl });
67
+
68
+ try {
69
+ await runGenericAdapterTests(t, adapter, "user-sqlite", tmpDir);
70
+ } finally {
71
+ if (adapter.db) adapter.db.close();
72
+ await fs.rm(tmpDir, { recursive: true, force: true });
73
+ }
74
+ });
75
+
76
+ // 2. MySQL (Conditional)
77
+ // Check specific test variable OR generic one if it maps to mysql
78
+ const mysqlUrl = process.env.TEST_MYSQL_URL ||
79
+ (process.env.DEV_WORKFLOW_DB_URL && process.env.DEV_WORKFLOW_DB_URL.startsWith("mysql") ? process.env.DEV_WORKFLOW_DB_URL : null);
80
+
81
+ await t.test("MysqlAdapter", { skip: !mysqlUrl }, async (t) => {
82
+ const adapter = new MysqlAdapter({ connectionUrl: mysqlUrl });
83
+ try {
84
+ // We use a random user ID to avoid clashing with verification script data
85
+ // although verification cleaned up mostly.
86
+ const testId = `test-mysql-${Date.now()}`;
87
+ const testProj = process.cwd();
88
+ await runGenericAdapterTests(t, adapter, testId, testProj);
89
+ } finally {
90
+ if (adapter.pool) await adapter.pool.end();
91
+ }
92
+ });
93
+
94
+ // 3. PostgreSQL (Conditional)
95
+ const pgUrl = process.env.TEST_POSTGRES_URL ||
96
+ (process.env.DEV_WORKFLOW_DB_URL && process.env.DEV_WORKFLOW_DB_URL.startsWith("postgres") ? process.env.DEV_WORKFLOW_DB_URL : null);
97
+
98
+ await t.test("PostgresAdapter", { skip: !pgUrl }, async (t) => {
99
+ const adapter = new PostgresAdapter({ connectionUrl: pgUrl });
100
+ try {
101
+ const testId = `test-pg-${Date.now()}`;
102
+ const testProj = process.cwd();
103
+ await runGenericAdapterTests(t, adapter, testId, testProj);
104
+ } finally {
105
+ if (adapter.pool) await adapter.pool.end();
106
+ }
107
+ });
108
+ });
@@ -0,0 +1,46 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import path from "node:path";
4
+ import fs from "node:fs/promises";
5
+ import os from "node:os";
6
+ import { exec } from "node:child_process";
7
+ import { promisify } from "node:util";
8
+ import { pathToFileURL } from "node:url";
9
+
10
+ const execAsync = promisify(exec);
11
+
12
+ test("Environment Configuration", async (t) => {
13
+ await t.test("loads variables from .env file", async () => {
14
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "env-test-"));
15
+ const originalCwd = process.cwd();
16
+ const indexJsPath = path.resolve(originalCwd, "index.js");
17
+ const indexUrl = pathToFileURL(indexJsPath).href;
18
+
19
+ try {
20
+ // Create a .env file
21
+ await fs.writeFile(path.join(tmpDir, ".env"), "TEST_ENV_VAR=loaded_successfully");
22
+
23
+ // Verify node can run and load it via index.js import
24
+ // We import index.js to trigger the 'dotenv/config' import.
25
+ // Since index.js starts a server if run directly, we just import it.
26
+ // However, we need to run a script in the NEW CWD.
27
+
28
+ const script = `
29
+ import '${indexUrl}';
30
+ console.log("VAR:" + process.env.TEST_ENV_VAR);
31
+ process.exit(0);
32
+ `;
33
+
34
+ const scriptPath = path.join(tmpDir, "check_env.js");
35
+ await fs.writeFile(scriptPath, script);
36
+
37
+ // Execute in tmpDir
38
+ const { stdout } = await execAsync(`node check_env.js`, { cwd: tmpDir });
39
+
40
+ assert.ok(stdout.includes("VAR:loaded_successfully"), "Output should contain loaded variable");
41
+
42
+ } finally {
43
+ await fs.rm(tmpDir, { recursive: true, force: true });
44
+ }
45
+ });
46
+ });
@@ -14,6 +14,13 @@ import { constants as fsConstants } from "node:fs";
14
14
  import { tmpdir } from "node:os";
15
15
  import path from "node:path";
16
16
  import { fileURLToPath, pathToFileURL } from "node:url";
17
+
18
+ // Isolate these tests from the real database configuration
19
+ delete process.env.DEV_WORKFLOW_DB_TYPE;
20
+ delete process.env.DEV_WORKFLOW_DB_URL;
21
+ delete process.env.TEST_MYSQL_URL;
22
+ delete process.env.TEST_POSTGRES_URL;
23
+
17
24
  import {
18
25
  WorkflowState,
19
26
  getNextStep,
@@ -50,7 +57,9 @@ test("resolveStateFile prefers current working directory when multiple project m
50
57
  const sourceModulePath = fileURLToPath(new URL("../workflow-state.js", import.meta.url));
51
58
  await copyFile(sourceModulePath, path.join(moduleDir, "workflow-state.js"));
52
59
 
53
- await writeFile(path.join(moduleDir, "package.json"), JSON.stringify({ name: "module" }));
60
+ await writeFile(path.join(moduleDir, "package.json"), JSON.stringify({ name: "module", type: "module" }));
61
+ await mkdir(path.join(moduleDir, "db"), { recursive: true });
62
+ await writeFile(path.join(moduleDir, "db", "index.js"), 'export function getDbAdapter() { return { connect: async () => {}, saveState: async () => {}, getState: async () => null }; } export async function insertHistoryEntry() {} export async function updateSummaryForUser() {} export async function saveState() {} export async function getState() {} export async function getSummaryForUser() { return { totalTasks: 0, taskTypes: {} }; } export async function getHistoryForUser() { return { entries: [], total: 0 }; }');
54
63
  await writeFile(path.join(projectA, "package.json"), JSON.stringify({ name: "project-a" }));
55
64
  await writeFile(path.join(projectB, "package.json"), JSON.stringify({ name: "project-b" }));
56
65
 
@@ -101,6 +110,9 @@ test("WorkflowState mirrors state file to compatibility locations", async () =>
101
110
  await mkdir(path.join(tempRoot, "dist"), { recursive: true });
102
111
  await mkdir(stateDir, { recursive: true });
103
112
  await copyFile(sourceModulePath, path.join(moduleDir, "workflow-state.js"));
113
+ await writeFile(path.join(moduleDir, "package.json"), JSON.stringify({ name: "module", type: "module" }));
114
+ await mkdir(path.join(moduleDir, "db"), { recursive: true });
115
+ await writeFile(path.join(moduleDir, "db", "index.js"), 'export function getDbAdapter() { return { connect: async () => {}, saveState: async () => {}, getState: async () => null }; } export async function insertHistoryEntry() {} export async function updateSummaryForUser() {} export async function saveState() {} export async function getState() {} export async function getSummaryForUser() { return { totalTasks: 0, taskTypes: {} }; } export async function getHistoryForUser() { return { entries: [], total: 0 }; }');
104
116
  await writeFile(path.join(projectRoot, "package.json"), JSON.stringify({ name: "temp" }));
105
117
 
106
118
  const stateFile = path.join(stateDir, "workflow-state.json");
@@ -129,6 +141,9 @@ test("WorkflowState avoids filesystem root when no project markers found", async
129
141
 
130
142
  const sourceModulePath = fileURLToPath(new URL("../workflow-state.js", import.meta.url));
131
143
  await copyFile(sourceModulePath, path.join(moduleDir, "workflow-state.js"));
144
+ await writeFile(path.join(moduleDir, "package.json"), JSON.stringify({ name: "module", type: "module" }));
145
+ await mkdir(path.join(moduleDir, "db"), { recursive: true });
146
+ await writeFile(path.join(moduleDir, "db", "index.js"), 'export function getDbAdapter() { return { connect: async () => {}, saveState: async () => {}, getState: async () => null }; } export async function insertHistoryEntry() {} export async function updateSummaryForUser() {} export async function saveState() {} export async function getState() {} export async function getSummaryForUser() { return { totalTasks: 0, taskTypes: {} }; } export async function getHistoryForUser() { return { entries: [], total: 0 }; }');
132
147
 
133
148
  const moduleUrl = pathToFileURL(path.join(moduleDir, "workflow-state.js"));
134
149
  const { WorkflowState: TempWorkflowState } = await import(moduleUrl.href);
@@ -0,0 +1,92 @@
1
+
2
+ import { MysqlAdapter } from "./db/adapters/mysql.js";
3
+ import { resolve } from "path";
4
+
5
+ const CONNECTION_URL = process.env.DEV_WORKFLOW_DB_URL;
6
+
7
+ if (!CONNECTION_URL) {
8
+ console.error("āŒ Error: DEV_WORKFLOW_DB_URL environment variable is required.");
9
+ console.error("Usage: DEV_WORKFLOW_DB_URL='mysql://user:password@host:port/database' node verify-db-mysql.js");
10
+ process.exit(1);
11
+ }
12
+
13
+ const TEST_USER = "test-verification-user";
14
+ const TEST_PROJECT = resolve(process.cwd());
15
+
16
+ async function runVerification() {
17
+ console.log("šŸ” Starting MySQL Verification...");
18
+ console.log(` User ID: ${TEST_USER}`);
19
+ console.log(` Project: ${TEST_PROJECT}`);
20
+ console.log(` URL: ${CONNECTION_URL.replace(/:[^:@]+@/, ":****@")}`);
21
+
22
+ const adapter = new MysqlAdapter({
23
+ connectionUrl: CONNECTION_URL
24
+ });
25
+
26
+ try {
27
+ // 1. Connect & Migrate
28
+ console.log("\n1. Connecting and Migrating...");
29
+ await adapter.connect();
30
+ console.log(" āœ… Connected to MySQL");
31
+
32
+ // 2. Clear previous test data (optional, but good for cleanliness)
33
+ // We won't strictly clear to avoid wiping valid data if user reuses IDs,
34
+ // but the test user should be unique enough.
35
+
36
+ // 3. Insert History
37
+ console.log("\n3. Testing History Insertion...");
38
+ const entry = {
39
+ taskDescription: "Verification Task",
40
+ taskType: "test",
41
+ timestamp: new Date().toISOString(),
42
+ commitMessage: "test commit"
43
+ };
44
+ await adapter.insertHistoryEntry(TEST_USER, TEST_PROJECT, entry);
45
+ console.log(" āœ… Inserted history entry");
46
+
47
+ // 4. Verify History Retrieval
48
+ console.log("\n4. Verifying History Retrieval...");
49
+ const history = await adapter.getHistoryForUser(TEST_USER, TEST_PROJECT, { pageSize: 1 });
50
+ const savedEntry = history.entries[0];
51
+
52
+ if (savedEntry && savedEntry.task_description === entry.taskDescription) {
53
+ console.log(" āœ… Retrieved history entry matches");
54
+ } else {
55
+ console.error(" āŒ Retrieved entry mismatch:", savedEntry);
56
+ process.exit(1);
57
+ }
58
+
59
+ // 5. Test State Persistence
60
+ console.log("\n5. Testing State Persistence...");
61
+ const state = {
62
+ currentPhase: "verification",
63
+ verified: true,
64
+ random: Math.random()
65
+ };
66
+ await adapter.saveState(TEST_USER, TEST_PROJECT, state);
67
+ console.log(" āœ… Saved state");
68
+
69
+ // 6. Verify State Retrieval
70
+ console.log("\n6. Verifying State Retrieval...");
71
+ const retrievedState = await adapter.getState(TEST_USER, TEST_PROJECT);
72
+ if (retrievedState && retrievedState.random === state.random) {
73
+ console.log(" āœ… Retrieved state matches");
74
+ } else {
75
+ console.error(" āŒ Retrieved state mismatch:", retrievedState);
76
+ process.exit(1);
77
+ }
78
+
79
+ console.log("\nšŸŽ‰ MySQL Verification SUCCESS!");
80
+ process.exit(0);
81
+
82
+ } catch (error) {
83
+ console.error("\nāŒ Verification FAILED:", error);
84
+ process.exit(1);
85
+ } finally {
86
+ if (adapter.pool) {
87
+ await adapter.pool.end();
88
+ }
89
+ }
90
+ }
91
+
92
+ runVerification();