@gamflaz/opencode-todolist 1.0.12 → 1.1.0
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/icons/glyph.d.ts +77 -0
- package/dist/icons/glyph.js +103 -0
- package/dist/icons/glyphs.d.ts +51 -0
- package/dist/icons/glyphs.js +56 -0
- package/dist/index.d.ts +21 -2
- package/dist/index.js +41 -3
- package/dist/schemas/todo.d.ts +76 -58
- package/dist/schemas/todo.js +73 -18
- package/dist/storage/db.d.ts +71 -0
- package/dist/storage/db.js +59 -0
- package/dist/storage/migrate-json.d.ts +82 -0
- package/dist/storage/migrate-json.js +278 -0
- package/dist/storage/schema.d.ts +775 -0
- package/dist/storage/schema.js +108 -0
- package/dist/storage/todo-store.d.ts +104 -56
- package/dist/storage/todo-store.js +178 -297
- package/dist/todos/hint.d.ts +13 -2
- package/dist/todos/hint.js +16 -19
- package/dist/todos/todo-tree.d.ts +16 -3
- package/dist/todos/todo-tree.js +22 -4
- package/dist/todos/update.js +2 -2
- package/dist/tui/todo-panel.js +73 -65
- package/drizzle/20261014050406_eminent_zaran/migration.sql +49 -0
- package/drizzle/20261014050406_eminent_zaran/snapshot.json +470 -0
- package/package.json +7 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TodoStatus } from "../schemas/status.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TodoChildren, TodoRootItem } from "../schemas/todo";
|
|
3
3
|
export interface TodoProgress {
|
|
4
4
|
completed: number;
|
|
5
5
|
total: number;
|
|
@@ -34,7 +34,7 @@ export declare class TodoTree {
|
|
|
34
34
|
private static readonly BAR_UNFILLED;
|
|
35
35
|
constructor(todos: readonly TodoRootItem[]);
|
|
36
36
|
static isOpenStatus(status: TodoStatus): boolean;
|
|
37
|
-
static calculateProgress(children?: readonly
|
|
37
|
+
static calculateProgress(children?: readonly TodoChildren[]): TodoProgress | null;
|
|
38
38
|
static renderProgressBar(percent: number, width?: number): {
|
|
39
39
|
filled: string;
|
|
40
40
|
unfilled: string;
|
|
@@ -56,7 +56,20 @@ export declare class TodoTree {
|
|
|
56
56
|
* parent whose children are all completed.
|
|
57
57
|
*/
|
|
58
58
|
private normalize;
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Renders the tree.
|
|
61
|
+
*
|
|
62
|
+
* `options.visibleIds` narrows which rows are *printed* without touching the
|
|
63
|
+
* data, so a root keeps its real `(completed/total)` label even when only one
|
|
64
|
+
* subtask is listed. A scoped read needs exactly that: the root line shows
|
|
65
|
+
* `(2/2)` while a single subtask row is displayed.
|
|
66
|
+
*
|
|
67
|
+
* A root line is printed only when its own id is visible; a child line only
|
|
68
|
+
* when the child's id is visible. Omitting `visibleIds` prints everything.
|
|
69
|
+
*/
|
|
70
|
+
render(options?: {
|
|
71
|
+
visibleIds?: readonly number[];
|
|
72
|
+
}): string;
|
|
60
73
|
private static icon;
|
|
61
74
|
/** Content with strike-through when completed, prefixed with its id unless it already starts with it. */
|
|
62
75
|
private static renderLabel;
|
package/dist/todos/todo-tree.js
CHANGED
|
@@ -159,15 +159,33 @@ export class TodoTree {
|
|
|
159
159
|
return { valid: true, data: normalized };
|
|
160
160
|
}
|
|
161
161
|
// Rendering
|
|
162
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Renders the tree.
|
|
164
|
+
*
|
|
165
|
+
* `options.visibleIds` narrows which rows are *printed* without touching the
|
|
166
|
+
* data, so a root keeps its real `(completed/total)` label even when only one
|
|
167
|
+
* subtask is listed. A scoped read needs exactly that: the root line shows
|
|
168
|
+
* `(2/2)` while a single subtask row is displayed.
|
|
169
|
+
*
|
|
170
|
+
* A root line is printed only when its own id is visible; a child line only
|
|
171
|
+
* when the child's id is visible. Omitting `visibleIds` prints everything.
|
|
172
|
+
*/
|
|
173
|
+
render(options) {
|
|
163
174
|
const lines = [];
|
|
175
|
+
const visible = options?.visibleIds === undefined
|
|
176
|
+
? undefined
|
|
177
|
+
: new Set(options.visibleIds);
|
|
164
178
|
for (const parent of this.todos) {
|
|
165
179
|
const progress = TodoTree.calculateProgress(parent.children);
|
|
166
180
|
const progressText = progress ? ` (${progress.label})` : "";
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
181
|
+
if (!visible || visible.has(parent.id)) {
|
|
182
|
+
lines.push(`${TodoTree.icon(parent.status)} ${TodoTree.renderLabel(parent, 0)}${progressText}`);
|
|
183
|
+
if (parent.note)
|
|
184
|
+
lines.push(` ↳ note: ${parent.note}`);
|
|
185
|
+
}
|
|
170
186
|
for (const child of parent.children ?? []) {
|
|
187
|
+
if (visible && !visible.has(child.id))
|
|
188
|
+
continue;
|
|
171
189
|
lines.push(` ${TodoTree.icon(child.status)} ${TodoTree.renderLabel(child, 1)}`);
|
|
172
190
|
if (child.note)
|
|
173
191
|
lines.push(` ↳ note: ${child.note}`);
|
package/dist/todos/update.js
CHANGED
|
@@ -74,7 +74,7 @@ export function assertSequentialScope(todos, input) {
|
|
|
74
74
|
const targetRootIndex = todos.indexOf(targetRoot);
|
|
75
75
|
if (targetRootIndex !== firstOpen && !hasError(targetRoot)) {
|
|
76
76
|
const blocking = todos[firstOpen];
|
|
77
|
-
throw new Error(`
|
|
77
|
+
throw new Error(`root ${targetRoot.id} is out of order: finish root ${blocking.id} "${blocking.content}" first (or cancel it). Only a root in 'error' may be fixed early.`);
|
|
78
78
|
}
|
|
79
79
|
const target = targetRoot.id === input.id
|
|
80
80
|
? targetRoot
|
|
@@ -89,7 +89,7 @@ export function assertSequentialScope(todos, input) {
|
|
|
89
89
|
if (input.status === "in_progress") {
|
|
90
90
|
const running = activeRoot(todos);
|
|
91
91
|
if (running && running.id !== targetRoot.id) {
|
|
92
|
-
throw new Error(`
|
|
92
|
+
throw new Error(`root ${running.id} is still in_progress. Finish it (or cancel it) before starting ${targetRoot.id}; an error root may be fixed early only when nothing else is running.`);
|
|
93
93
|
}
|
|
94
94
|
const busyChild = targetRoot.children?.find((child) => child.status === "in_progress" && child.id !== input.id);
|
|
95
95
|
if (busyChild) {
|
package/dist/tui/todo-panel.js
CHANGED
|
@@ -11,11 +11,10 @@ import { mergeProps as _$mergeProps } from "@opentui/solid";
|
|
|
11
11
|
import { createElement as _$createElement } from "@opentui/solid";
|
|
12
12
|
import { mkdirSync, watch } from "node:fs";
|
|
13
13
|
import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { arrowDownSvgIcon, arrowRightSvgIcon, continuePromptSvgIcon, removeSvgIcon } from "../icons/svg-icons.js";
|
|
14
|
+
import { Glyph } from "../icons/glyph.js";
|
|
15
|
+
import { ARROW_DOWN, ARROW_RIGHT, CLEAR, CONTINUE } from "../icons/glyphs.js";
|
|
17
16
|
import { StatusLabel } from "../schemas/status.js";
|
|
18
|
-
import {
|
|
17
|
+
import { getDataVersion, getTodosDirectory, readTodosSync, writeTodos } from "../storage/todo-store.js";
|
|
19
18
|
import { getTodoDisplaySummary, hasOpenTodos } from "../todos/display.js";
|
|
20
19
|
import { TodoTree } from "../todos/todo-tree.js";
|
|
21
20
|
import { ERROR_PREFIX, ErrorRowWidth, RowWidth } from "./layout.js";
|
|
@@ -31,6 +30,15 @@ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧",
|
|
|
31
30
|
const SPINNER_INTERVAL_MS = 100;
|
|
32
31
|
const WATCH_DEBOUNCE_MS = 50;
|
|
33
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Safety-net interval for cross-process change detection.
|
|
35
|
+
*
|
|
36
|
+
* `fs.watch` on the database usually fires first; this poll only exists for
|
|
37
|
+
* the cases where the filesystem event is missed or arrives late. One PRAGMA
|
|
38
|
+
* per second is negligible next to rendering the panel.
|
|
39
|
+
*/
|
|
40
|
+
const DATA_VERSION_POLL_MS = 1000;
|
|
41
|
+
|
|
34
42
|
/**
|
|
35
43
|
* Maximum number of todo rows the panel shows before it starts scrolling.
|
|
36
44
|
* Keeping the panel compact protects the composer; scrolling guarantees no
|
|
@@ -337,47 +345,69 @@ function createHover() {
|
|
|
337
345
|
}
|
|
338
346
|
|
|
339
347
|
/**
|
|
340
|
-
*
|
|
341
|
-
*
|
|
348
|
+
* Notifies when the todo data changes, from either process.
|
|
349
|
+
*
|
|
350
|
+
* All sessions now live in one SQLite file, so there is no longer a per-session
|
|
351
|
+
* folder to watch. Two complementary signals are used:
|
|
352
|
+
*
|
|
353
|
+
* 1. `fs.watch` on the database file, debounced. Low latency, but SQLite writes
|
|
354
|
+
* to the WAL first, so the event can arrive before the reader sees the new
|
|
355
|
+
* rows, and some platforms are inconsistent about reporting it at all.
|
|
356
|
+
* 2. A poll of `PRAGMA data_version`, which changes exactly when *another*
|
|
357
|
+
* connection commits. This is the safety net: if the filesystem event is
|
|
358
|
+
* missed, the poll still notices within one interval.
|
|
359
|
+
*
|
|
360
|
+
* The poll is skipped entirely while the document is idle and nothing moved, so
|
|
361
|
+
* the steady-state cost is one cheap PRAGMA per interval.
|
|
342
362
|
*/
|
|
343
|
-
function watchSessionTodos(
|
|
363
|
+
function watchSessionTodos(onChange, readDataVersion) {
|
|
344
364
|
let timer;
|
|
365
|
+
let interval;
|
|
345
366
|
let watcher;
|
|
346
|
-
const hashed = createMemo(() => {
|
|
347
|
-
const sid = sessionID();
|
|
348
|
-
if (!sid) return "";
|
|
349
|
-
try {
|
|
350
|
-
return hashSessionID(sid);
|
|
351
|
-
} catch {
|
|
352
|
-
return "";
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
367
|
const notify = () => {
|
|
356
368
|
clearTimeout(timer);
|
|
357
369
|
timer = setTimeout(onChange, WATCH_DEBOUNCE_MS);
|
|
358
370
|
};
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
371
|
+
|
|
372
|
+
// Seed with the current value so the first poll compares against reality
|
|
373
|
+
// instead of firing a spurious refresh.
|
|
374
|
+
let lastVersion = Number.NaN;
|
|
375
|
+
const poll = () => {
|
|
376
|
+
try {
|
|
377
|
+
const version = readDataVersion();
|
|
378
|
+
if (Number.isNaN(lastVersion)) {
|
|
379
|
+
lastVersion = version;
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (version !== lastVersion) {
|
|
383
|
+
lastVersion = version;
|
|
384
|
+
notify();
|
|
385
|
+
}
|
|
386
|
+
} catch {
|
|
387
|
+
// The database may be mid-migration; the next tick retries.
|
|
388
|
+
}
|
|
364
389
|
};
|
|
365
390
|
try {
|
|
366
391
|
const baseDir = getTodosDirectory();
|
|
367
392
|
mkdirSync(baseDir, {
|
|
368
393
|
recursive: true
|
|
369
394
|
});
|
|
395
|
+
// Watching the directory rather than the file itself: SQLite may replace
|
|
396
|
+
// the file on checkpoint, which would detach a file-level watcher.
|
|
370
397
|
watcher = watch(baseDir, {
|
|
371
|
-
recursive: true,
|
|
372
398
|
persistent: false
|
|
373
399
|
}, (_, filename) => {
|
|
374
|
-
|
|
400
|
+
const name = filename ? String(filename) : "";
|
|
401
|
+
if (!name || name.startsWith("todos.db")) notify();
|
|
375
402
|
});
|
|
376
403
|
} catch (err) {
|
|
377
404
|
console.error("[todos] watcher initialization failed:", err);
|
|
378
405
|
}
|
|
406
|
+
interval = setInterval(poll, DATA_VERSION_POLL_MS);
|
|
407
|
+
poll();
|
|
379
408
|
onCleanup(() => {
|
|
380
409
|
clearTimeout(timer);
|
|
410
|
+
if (interval) clearInterval(interval);
|
|
381
411
|
watcher?.close();
|
|
382
412
|
});
|
|
383
413
|
}
|
|
@@ -395,12 +425,6 @@ export function TodoProgress(props) {
|
|
|
395
425
|
const clearHover = createHover();
|
|
396
426
|
const continueHover = createHover();
|
|
397
427
|
|
|
398
|
-
// Image capability
|
|
399
|
-
const [imageSupported, setImageSupported] = createSignal(supportsImageRendering(context.renderer?.capabilities));
|
|
400
|
-
const onCapabilities = caps => setImageSupported(supportsImageRendering(caps));
|
|
401
|
-
context.renderer.on("capabilities", onCapabilities);
|
|
402
|
-
onCleanup(() => context.renderer.off("capabilities", onCapabilities));
|
|
403
|
-
|
|
404
428
|
// Running state
|
|
405
429
|
const readRunning = () => {
|
|
406
430
|
const sid = sessionID();
|
|
@@ -443,7 +467,7 @@ export function TodoProgress(props) {
|
|
|
443
467
|
});
|
|
444
468
|
|
|
445
469
|
// Todo data
|
|
446
|
-
watchSessionTodos(
|
|
470
|
+
watchSessionTodos(() => setRefresh(v => v + 1), getDataVersion);
|
|
447
471
|
const summary = createMemo(() => {
|
|
448
472
|
refresh();
|
|
449
473
|
const sid = sessionID();
|
|
@@ -570,18 +594,13 @@ export function TodoProgress(props) {
|
|
|
570
594
|
_$setProp(_el$18, "width", 2);
|
|
571
595
|
_$setProp(_el$18, "height", 1);
|
|
572
596
|
_$setProp(_el$18, "marginRight", 1);
|
|
597
|
+
_$setProp(_el$18, "marginLeft", 1);
|
|
573
598
|
_$setProp(_el$18, "alignItems", "flex-start");
|
|
574
|
-
_$insert(_el$18, _$createComponent(
|
|
575
|
-
|
|
576
|
-
return isCollapsed() ? arrowRightSvgIcon : arrowDownSvgIcon;
|
|
577
|
-
},
|
|
599
|
+
_$insert(_el$18, _$createComponent(Glyph, {
|
|
600
|
+
glyph: () => isCollapsed() ? ARROW_RIGHT : ARROW_DOWN,
|
|
578
601
|
get color() {
|
|
579
602
|
return theme().text.muted;
|
|
580
603
|
},
|
|
581
|
-
get fallback() {
|
|
582
|
-
return isCollapsed() ? "▶" : "▼";
|
|
583
|
-
},
|
|
584
|
-
width: 2,
|
|
585
604
|
height: 1
|
|
586
605
|
}));
|
|
587
606
|
_$insertNode(_el$19, _el$20);
|
|
@@ -642,14 +661,11 @@ export function TodoProgress(props) {
|
|
|
642
661
|
void continueTodos();
|
|
643
662
|
}
|
|
644
663
|
});
|
|
645
|
-
_$insert(_el$24, _$createComponent(
|
|
646
|
-
|
|
664
|
+
_$insert(_el$24, _$createComponent(Glyph, {
|
|
665
|
+
glyph: () => CONTINUE,
|
|
647
666
|
get color() {
|
|
648
667
|
return hoverColor(continueHover.hovered(), theme().text.action.primary.base);
|
|
649
|
-
}
|
|
650
|
-
fallback: "\u21BB",
|
|
651
|
-
width: 2,
|
|
652
|
-
height: 1
|
|
668
|
+
}
|
|
653
669
|
}), _el$25);
|
|
654
670
|
_$insertNode(_el$25, _$createTextNode(`Continue`));
|
|
655
671
|
_$setProp(_el$25, "wrapMode", "none");
|
|
@@ -669,6 +685,7 @@ export function TodoProgress(props) {
|
|
|
669
685
|
return _el$24;
|
|
670
686
|
}
|
|
671
687
|
}), _el$27);
|
|
688
|
+
_$setProp(_el$27, "marginRight", 1.5);
|
|
672
689
|
_$setProp(_el$27, "flexDirection", "row");
|
|
673
690
|
_$setProp(_el$27, "onMouseDown", e => {
|
|
674
691
|
if (e.button === 0) {
|
|
@@ -676,14 +693,11 @@ export function TodoProgress(props) {
|
|
|
676
693
|
void clearTodos();
|
|
677
694
|
}
|
|
678
695
|
});
|
|
679
|
-
_$insert(_el$27, _$createComponent(
|
|
696
|
+
_$insert(_el$27, _$createComponent(Glyph, {
|
|
697
|
+
glyph: () => CLEAR,
|
|
680
698
|
get color() {
|
|
681
699
|
return hoverColor(clearHover.hovered(), theme().text.feedback.error.base);
|
|
682
|
-
}
|
|
683
|
-
source: removeSvgIcon,
|
|
684
|
-
fallback: "\u2716",
|
|
685
|
-
height: 1,
|
|
686
|
-
width: 2
|
|
700
|
+
}
|
|
687
701
|
}));
|
|
688
702
|
_$insert(_el$15, _$createComponent(Show, {
|
|
689
703
|
get when() {
|
|
@@ -709,32 +723,26 @@ export function TodoProgress(props) {
|
|
|
709
723
|
}), null);
|
|
710
724
|
_$effect(_p$ => {
|
|
711
725
|
var _v$11 = theme().border.base,
|
|
712
|
-
_v$12 =
|
|
713
|
-
_v$13 = {
|
|
726
|
+
_v$12 = {
|
|
714
727
|
fg: theme().text.base
|
|
715
728
|
},
|
|
716
|
-
_v$
|
|
729
|
+
_v$13 = {
|
|
717
730
|
fg: theme().text.muted
|
|
718
731
|
},
|
|
719
|
-
_v$
|
|
720
|
-
_v$
|
|
721
|
-
_v$17 = clearHover.onMouseOut;
|
|
732
|
+
_v$14 = clearHover.onMouseOver,
|
|
733
|
+
_v$15 = clearHover.onMouseOut;
|
|
722
734
|
_v$11 !== _p$.e && (_p$.e = _$setProp(_el$15, "borderColor", _v$11, _p$.e));
|
|
723
|
-
_v$12 !== _p$.t && (_p$.t = _$setProp(_el$
|
|
724
|
-
_v$13 !== _p$.a && (_p$.a = _$setProp(_el$
|
|
725
|
-
_v$14 !== _p$.o && (_p$.o = _$setProp(_el$
|
|
726
|
-
_v$15 !== _p$.i && (_p$.i = _$setProp(_el$27, "
|
|
727
|
-
_v$16 !== _p$.n && (_p$.n = _$setProp(_el$27, "onMouseOver", _v$16, _p$.n));
|
|
728
|
-
_v$17 !== _p$.s && (_p$.s = _$setProp(_el$27, "onMouseOut", _v$17, _p$.s));
|
|
735
|
+
_v$12 !== _p$.t && (_p$.t = _$setProp(_el$21, "style", _v$12, _p$.t));
|
|
736
|
+
_v$13 !== _p$.a && (_p$.a = _$setProp(_el$23, "style", _v$13, _p$.a));
|
|
737
|
+
_v$14 !== _p$.o && (_p$.o = _$setProp(_el$27, "onMouseOver", _v$14, _p$.o));
|
|
738
|
+
_v$15 !== _p$.i && (_p$.i = _$setProp(_el$27, "onMouseOut", _v$15, _p$.i));
|
|
729
739
|
return _p$;
|
|
730
740
|
}, {
|
|
731
741
|
e: undefined,
|
|
732
742
|
t: undefined,
|
|
733
743
|
a: undefined,
|
|
734
744
|
o: undefined,
|
|
735
|
-
i: undefined
|
|
736
|
-
n: undefined,
|
|
737
|
-
s: undefined
|
|
745
|
+
i: undefined
|
|
738
746
|
});
|
|
739
747
|
return _el$15;
|
|
740
748
|
})()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
CREATE TABLE `tag` (
|
|
2
|
+
`id` text PRIMARY KEY,
|
|
3
|
+
`name` text NOT NULL UNIQUE
|
|
4
|
+
);
|
|
5
|
+
--> statement-breakpoint
|
|
6
|
+
CREATE TABLE `todo_item` (
|
|
7
|
+
`session_key` text NOT NULL,
|
|
8
|
+
`id` text NOT NULL,
|
|
9
|
+
`parent_id` text,
|
|
10
|
+
`content` text NOT NULL,
|
|
11
|
+
`status` text NOT NULL,
|
|
12
|
+
`priority` text,
|
|
13
|
+
`note` text,
|
|
14
|
+
`position` integer NOT NULL,
|
|
15
|
+
CONSTRAINT `todo_item_pk` PRIMARY KEY(`session_key`, `id`),
|
|
16
|
+
CONSTRAINT `fk_todo_item_session_key_todo_session_session_key_fk` FOREIGN KEY (`session_key`) REFERENCES `todo_session`(`session_key`) ON DELETE CASCADE,
|
|
17
|
+
CONSTRAINT `fk_todo_item_session_key_parent_id_todo_item_session_key_id_fk` FOREIGN KEY (`session_key`,`parent_id`) REFERENCES `todo_item`(`session_key`,`id`) ON DELETE CASCADE
|
|
18
|
+
);
|
|
19
|
+
--> statement-breakpoint
|
|
20
|
+
CREATE TABLE `todo_item_dependency` (
|
|
21
|
+
`session_key` text NOT NULL,
|
|
22
|
+
`todo_id` text NOT NULL,
|
|
23
|
+
`depends_on_id` text NOT NULL,
|
|
24
|
+
CONSTRAINT `todo_item_dependency_pk` PRIMARY KEY(`session_key`, `todo_id`, `depends_on_id`),
|
|
25
|
+
CONSTRAINT `fk_todo_item_dependency_session_key_todo_id_todo_item_session_key_id_fk` FOREIGN KEY (`session_key`,`todo_id`) REFERENCES `todo_item`(`session_key`,`id`) ON DELETE CASCADE,
|
|
26
|
+
CONSTRAINT `fk_todo_item_dependency_session_key_depends_on_id_todo_item_session_key_id_fk` FOREIGN KEY (`session_key`,`depends_on_id`) REFERENCES `todo_item`(`session_key`,`id`) ON DELETE CASCADE
|
|
27
|
+
);
|
|
28
|
+
--> statement-breakpoint
|
|
29
|
+
CREATE TABLE `todo_item_tag` (
|
|
30
|
+
`session_key` text NOT NULL,
|
|
31
|
+
`todo_id` text NOT NULL,
|
|
32
|
+
`tag_id` text NOT NULL,
|
|
33
|
+
CONSTRAINT `todo_item_tag_pk` PRIMARY KEY(`session_key`, `todo_id`, `tag_id`),
|
|
34
|
+
CONSTRAINT `fk_todo_item_tag_session_key_todo_id_todo_item_session_key_id_fk` FOREIGN KEY (`session_key`,`todo_id`) REFERENCES `todo_item`(`session_key`,`id`) ON DELETE CASCADE,
|
|
35
|
+
CONSTRAINT `fk_todo_item_tag_tag_id_tag_id_fk` FOREIGN KEY (`tag_id`) REFERENCES `tag`(`id`) ON DELETE CASCADE
|
|
36
|
+
);
|
|
37
|
+
--> statement-breakpoint
|
|
38
|
+
CREATE TABLE `todo_session` (
|
|
39
|
+
`session_key` text PRIMARY KEY,
|
|
40
|
+
`revision` integer DEFAULT 0 NOT NULL,
|
|
41
|
+
`hint_revision` integer DEFAULT 0 NOT NULL,
|
|
42
|
+
`created_at` text NOT NULL,
|
|
43
|
+
`updated_at` text NOT NULL
|
|
44
|
+
);
|
|
45
|
+
--> statement-breakpoint
|
|
46
|
+
CREATE INDEX `todo_item_session_position_idx` ON `todo_item` (`session_key`,`position`);--> statement-breakpoint
|
|
47
|
+
CREATE UNIQUE INDEX `todo_item_dependency_pair_idx` ON `todo_item_dependency` (`session_key`,`todo_id`,`depends_on_id`);--> statement-breakpoint
|
|
48
|
+
CREATE INDEX `todo_item_dependency_depends_on_idx` ON `todo_item_dependency` (`session_key`,`depends_on_id`);--> statement-breakpoint
|
|
49
|
+
CREATE INDEX `todo_item_tag_tag_idx` ON `todo_item_tag` (`tag_id`);
|