feature/workspace-domain
Dax Raad 2026-01-27 17:43:20 -05:00
parent cd174d8cba
commit 19a41ab297
6 changed files with 143 additions and 57 deletions

View File

@ -62,14 +62,13 @@ CREATE TABLE `session` (
--> statement-breakpoint
CREATE TABLE `todo` (
`session_id` text NOT NULL,
`id` text NOT NULL,
`content` text NOT NULL,
`status` text NOT NULL,
`priority` text NOT NULL,
`position` integer NOT NULL,
`time_created` integer NOT NULL,
`time_updated` integer NOT NULL,
CONSTRAINT `todo_pk` PRIMARY KEY(`session_id`, `id`),
CONSTRAINT `todo_pk` PRIMARY KEY(`session_id`, `position`),
CONSTRAINT `fk_todo_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
);
--> statement-breakpoint
@ -88,4 +87,4 @@ CREATE INDEX `part_message_idx` ON `part` (`message_id`);--> statement-breakpoin
CREATE INDEX `part_session_idx` ON `part` (`session_id`);--> statement-breakpoint
CREATE INDEX `session_project_idx` ON `session` (`project_id`);--> statement-breakpoint
CREATE INDEX `session_parent_idx` ON `session` (`parent_id`);--> statement-breakpoint
CREATE INDEX `todo_session_idx` ON `todo` (`session_id`);
CREATE INDEX `todo_session_idx` ON `todo` (`session_id`);

View File

@ -1,7 +1,7 @@
{
"version": "7",
"dialect": "sqlite",
"id": "0e365b40-39c4-447f-9729-9714d865d8ff",
"id": "068758ed-a97a-46f6-8a59-6c639ae7c20c",
"prevIds": [
"00000000-0000-0000-0000-000000000000"
],
@ -160,7 +160,17 @@
"autoincrement": false,
"default": null,
"generated": null,
"name": "created_at",
"name": "time_created",
"entityType": "columns",
"table": "message"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_updated",
"entityType": "columns",
"table": "message"
},
@ -204,6 +214,26 @@
"entityType": "columns",
"table": "part"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_created",
"entityType": "columns",
"table": "part"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_updated",
"entityType": "columns",
"table": "part"
},
{
"type": "text",
"notNull": true,
@ -224,6 +254,26 @@
"entityType": "columns",
"table": "permission"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_created",
"entityType": "columns",
"table": "permission"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_updated",
"entityType": "columns",
"table": "permission"
},
{
"type": "text",
"notNull": true,
@ -360,37 +410,7 @@
"autoincrement": false,
"default": null,
"generated": null,
"name": "revert_message_id",
"entityType": "columns",
"table": "session"
},
{
"type": "text",
"notNull": false,
"autoincrement": false,
"default": null,
"generated": null,
"name": "revert_part_id",
"entityType": "columns",
"table": "session"
},
{
"type": "text",
"notNull": false,
"autoincrement": false,
"default": null,
"generated": null,
"name": "revert_snapshot",
"entityType": "columns",
"table": "session"
},
{
"type": "text",
"notNull": false,
"autoincrement": false,
"default": null,
"generated": null,
"name": "revert_diff",
"name": "revert",
"entityType": "columns",
"table": "session"
},
@ -454,16 +474,6 @@
"entityType": "columns",
"table": "todo"
},
{
"type": "text",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "id",
"entityType": "columns",
"table": "todo"
},
{
"type": "text",
"notNull": true,
@ -504,6 +514,26 @@
"entityType": "columns",
"table": "todo"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_created",
"entityType": "columns",
"table": "todo"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_updated",
"entityType": "columns",
"table": "todo"
},
{
"type": "text",
"notNull": false,
@ -544,6 +574,26 @@
"entityType": "columns",
"table": "session_share"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_created",
"entityType": "columns",
"table": "session_share"
},
{
"type": "integer",
"notNull": true,
"autoincrement": false,
"default": null,
"generated": null,
"name": "time_updated",
"entityType": "columns",
"table": "session_share"
},
{
"columns": [
"session_id"
@ -637,7 +687,7 @@
{
"columns": [
"session_id",
"id"
"position"
],
"nameExplicit": false,
"name": "todo_pk",

View File

@ -67,14 +67,16 @@ export const TodoTable = sqliteTable(
session_id: text()
.notNull()
.references(() => SessionTable.id, { onDelete: "cascade" }),
id: text().notNull(),
content: text().notNull(),
status: text().notNull(),
priority: text().notNull(),
position: integer().notNull(),
...Timestamps,
},
(table) => [primaryKey({ columns: [table.session_id, table.id] }), index("todo_session_idx").on(table.session_id)],
(table) => [
primaryKey({ columns: [table.session_id, table.position] }),
index("todo_session_idx").on(table.session_id),
],
)
export const PermissionTable = sqliteTable("permission", {

View File

@ -10,7 +10,6 @@ export namespace Todo {
content: z.string().describe("Brief description of the task"),
status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
priority: z.string().describe("Priority level of the task: high, medium, low"),
id: z.string().describe("Unique identifier for the todo item"),
})
.meta({ ref: "Todo" })
export type Info = z.infer<typeof Info>
@ -33,7 +32,6 @@ export namespace Todo {
.values(
input.todos.map((todo, position) => ({
session_id: input.sessionID,
id: todo.id,
content: todo.content,
status: todo.status,
priority: todo.priority,
@ -50,7 +48,6 @@ export namespace Todo {
db.select().from(TodoTable).where(eq(TodoTable.session_id, sessionID)).orderBy(asc(TodoTable.position)).all(),
)
return rows.map((row) => ({
id: row.id,
content: row.content,
status: row.status,
priority: row.priority,

View File

@ -235,14 +235,15 @@ export namespace JsonMigration {
}
for (let position = 0; position < data.length; position++) {
const todo = data[position]
if (!todo?.id || !todo?.content || !todo?.status || !todo?.priority) continue
if (!todo?.content || !todo?.status || !todo?.priority) continue
values.push({
session_id: sessionID,
id: todo.id,
content: todo.content,
status: todo.status,
priority: todo.priority,
position,
time_created: Date.now(),
time_updated: Date.now(),
})
}
}

View File

@ -282,17 +282,54 @@ describe("JSON to SQLite migration", () => {
expect(stats?.todos).toBe(2)
const db = drizzle({ client: sqlite })
const todos = db.select().from(TodoTable).all()
const todos = db.select().from(TodoTable).orderBy(TodoTable.position).all()
expect(todos.length).toBe(2)
expect(todos[0].id).toBe("todo_1")
expect(todos[0].content).toBe("First todo")
expect(todos[0].status).toBe("pending")
expect(todos[0].priority).toBe("high")
expect(todos[0].position).toBe(0)
expect(todos[1].id).toBe("todo_2")
expect(todos[1].content).toBe("Second todo")
expect(todos[1].position).toBe(1)
})
test("todos are ordered by position", async () => {
await Bun.write(
path.join(storageDir, "project", "proj_test123abc.json"),
JSON.stringify({
id: "proj_test123abc",
worktree: "/",
time: { created: Date.now(), updated: Date.now() },
sandboxes: [],
}),
)
await Bun.write(
path.join(storageDir, "session", "proj_test123abc", "ses_test456def.json"),
JSON.stringify({ ...fixtures.session }),
)
await Bun.write(
path.join(storageDir, "todo", "ses_test456def.json"),
JSON.stringify([
{ content: "Third", status: "pending", priority: "low" },
{ content: "First", status: "pending", priority: "high" },
{ content: "Second", status: "in_progress", priority: "medium" },
]),
)
await JsonMigration.run(sqlite)
const db = drizzle({ client: sqlite })
const todos = db.select().from(TodoTable).orderBy(TodoTable.position).all()
expect(todos.length).toBe(3)
expect(todos[0].content).toBe("Third")
expect(todos[0].position).toBe(0)
expect(todos[1].content).toBe("First")
expect(todos[1].position).toBe(1)
expect(todos[2].content).toBe("Second")
expect(todos[2].position).toBe(2)
})
test("migrates permissions", async () => {
// First create the project
await Bun.write(