@anchrd/intel-api 0.27.0 → 0.28.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/adapters/cloudflare/cloudflare.js +34 -0
- package/dist/adapters/db/db-audit.d.ts +34 -0
- package/dist/adapters/db/db-audit.js +89 -0
- package/dist/adapters/db/db-boards.d.ts +6 -0
- package/dist/adapters/db/db-boards.js +201 -0
- package/dist/adapters/db/db.js +22 -2
- package/dist/audit/audit.d.ts +2 -0
- package/dist/audit/audit.js +72 -0
- package/dist/audit/audit.types.d.ts +22 -0
- package/dist/audit/audit.types.js +1 -0
- package/dist/boards/boards.d.ts +16 -0
- package/dist/boards/boards.js +166 -0
- package/dist/boards/boards.types.d.ts +51 -0
- package/dist/boards/boards.types.js +1 -0
- package/dist/http/http.js +67 -0
- package/dist/http/http.types.d.ts +4 -0
- package/dist/intel/intel.js +4 -0
- package/dist/intel/intel.types.d.ts +4 -0
- package/dist/mcp/mcp.js +72 -0
- package/dist/mcp/mcp.types.d.ts +4 -0
- package/dist/nodes/nodes.js +101 -9
- package/dist/nodes/nodes.types.d.ts +10 -0
- package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
- package/migrations/0023_a_board_and_its_tasks.sql +116 -0
- package/package.json +2 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
-- Two more node kinds, `board` and `task`, and the two side tables that carry what a node row must
|
|
2
|
+
-- not (D66, anchrd/intel#376).
|
|
3
|
+
--
|
|
4
|
+
-- ⚠️ BOTH words in ONE rebuild. Adding them in two migrations would be the same risky operation
|
|
5
|
+
-- twice, and this one has gone wrong before: `0005` committed with every row of `node_links` gone.
|
|
6
|
+
--
|
|
7
|
+
-- The split this migration encodes: IDENTITY through `kind`, FIELDS through a side table.
|
|
8
|
+
-- * `kind` because the tree has to hide `task` on EVERY level query. As a kind that is a condition
|
|
9
|
+
-- on a column the query already carries; as a `document` plus a side-table row it would be a
|
|
10
|
+
-- join on the hottest path in the interface.
|
|
11
|
+
-- * the side table because `status`, `assignee` and the dates are FILTER columns. In the `nodes`
|
|
12
|
+
-- row they would stand empty for every other kind.
|
|
13
|
+
--
|
|
14
|
+
-- The recipe below is the one in `packages/api/CLAUDE.md`, *Rebuilding `nodes` is a recipe*. All
|
|
15
|
+
-- four points, and each is one a previous migration got wrong first:
|
|
16
|
+
-- 1. the new table is created under its FINAL name — never `ALTER TABLE … RENAME`, which rewrites
|
|
17
|
+
-- every `REFERENCES` clause and drags the children onto the table about to be dropped;
|
|
18
|
+
-- 2. `node_links` is carried out and back with `INSERT OR IGNORE`, because it is the one child
|
|
19
|
+
-- declared `ON DELETE CASCADE` and `DROP TABLE nodes` fires an implicit `DELETE FROM`;
|
|
20
|
+
-- 3. `PRAGMA defer_foreign_keys`, never `foreign_keys = OFF` — D1 ignores the latter over its HTTP
|
|
21
|
+
-- API while miniflare honours it, so a green test here would prove nothing about production;
|
|
22
|
+
-- 4. the columns are named on BOTH sides of `INSERT … SELECT`, so a future reordering cannot turn
|
|
23
|
+
-- every title into an owner id and still commit.
|
|
24
|
+
PRAGMA defer_foreign_keys = TRUE;
|
|
25
|
+
|
|
26
|
+
CREATE TABLE nodes_carry AS SELECT * FROM nodes;
|
|
27
|
+
CREATE TABLE node_links_carry AS SELECT * FROM node_links;
|
|
28
|
+
|
|
29
|
+
DROP TABLE nodes;
|
|
30
|
+
|
|
31
|
+
CREATE TABLE nodes (
|
|
32
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
33
|
+
parent_id TEXT REFERENCES nodes(id),
|
|
34
|
+
kind TEXT NOT NULL CHECK (kind IN ('folder', 'document', 'attachment', 'table', 'board', 'task')),
|
|
35
|
+
title TEXT NOT NULL CHECK (length(title) BETWEEN 1 AND 240),
|
|
36
|
+
description TEXT CHECK (description IS NULL OR length(description) <= 2000),
|
|
37
|
+
owner_id TEXT NOT NULL,
|
|
38
|
+
current_version_id TEXT,
|
|
39
|
+
created_at TEXT NOT NULL,
|
|
40
|
+
updated_at TEXT NOT NULL,
|
|
41
|
+
archived_at TEXT
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
INSERT INTO nodes (
|
|
45
|
+
id, parent_id, kind, title, description, owner_id,
|
|
46
|
+
current_version_id, created_at, updated_at, archived_at
|
|
47
|
+
)
|
|
48
|
+
SELECT
|
|
49
|
+
id, parent_id, kind, title, description, owner_id,
|
|
50
|
+
current_version_id, created_at, updated_at, archived_at
|
|
51
|
+
FROM nodes_carry;
|
|
52
|
+
|
|
53
|
+
INSERT OR IGNORE INTO node_links SELECT * FROM node_links_carry;
|
|
54
|
+
|
|
55
|
+
DROP TABLE nodes_carry;
|
|
56
|
+
DROP TABLE node_links_carry;
|
|
57
|
+
|
|
58
|
+
CREATE INDEX nodes_parent_idx ON nodes(parent_id, archived_at, title);
|
|
59
|
+
CREATE INDEX nodes_owner_idx ON nodes(owner_id, archived_at);
|
|
60
|
+
|
|
61
|
+
-- The board's own configuration: which columns it has and in what order. It is one row per board
|
|
62
|
+
-- node, and it exists because a list of statuses is not a node property — every other kind would
|
|
63
|
+
-- carry it empty.
|
|
64
|
+
--
|
|
65
|
+
-- ⚠️ `ON DELETE CASCADE` here and on `board_tasks` below, and that is deliberate in a repository
|
|
66
|
+
-- whose rule is *refuse rather than cascade*. The rule protects a CONTAINER from taking its contents
|
|
67
|
+
-- down with it; these two rows are not contents but the node's own fields, split off only because
|
|
68
|
+
-- SQLite has no per-kind columns. A board whose row here outlived it would be a configuration for a
|
|
69
|
+
-- board that does not exist.
|
|
70
|
+
CREATE TABLE boards (
|
|
71
|
+
node_id TEXT PRIMARY KEY NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
|
72
|
+
statuses_json TEXT NOT NULL DEFAULT '[]',
|
|
73
|
+
created_at TEXT NOT NULL,
|
|
74
|
+
updated_at TEXT NOT NULL
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
-- What a task carries beyond a node. Every column here is one somebody filters or sorts by — that
|
|
78
|
+
-- is the whole reason they are not in the body.
|
|
79
|
+
--
|
|
80
|
+
-- ⚠️ `position` is REAL, not INTEGER. Dragging a card between two neighbours then needs no rewrite
|
|
81
|
+
-- of the whole column: the new value is the midpoint of the two. With integers every drop would
|
|
82
|
+
-- renumber the rows below it, and two people dropping at once would fight over rows neither touched.
|
|
83
|
+
CREATE TABLE board_tasks (
|
|
84
|
+
node_id TEXT PRIMARY KEY NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
|
85
|
+
board_id TEXT NOT NULL REFERENCES nodes(id),
|
|
86
|
+
status TEXT NOT NULL,
|
|
87
|
+
assignee_id TEXT,
|
|
88
|
+
labels_json TEXT NOT NULL DEFAULT '[]',
|
|
89
|
+
start_date TEXT,
|
|
90
|
+
due_date TEXT,
|
|
91
|
+
-- "waits for", as a COLUMN and not as a sentence in the body. The DoD of #376 says it in one
|
|
92
|
+
-- line: relations are columns the text points at, never the other way round. A dependency living
|
|
93
|
+
-- in prose cannot be asked "what is blocked right now", and `node_links` cannot answer it either
|
|
94
|
+
-- — that table says two nodes are related, not that one waits for the other.
|
|
95
|
+
--
|
|
96
|
+
-- ⚠️ It points at `nodes`, not at `board_tasks`. A task may wait for something that is not a
|
|
97
|
+
-- task: a document that has to be approved first, a board that has to be finished. Narrowing it
|
|
98
|
+
-- to the side table would decide today that only tasks can block, and nothing asked for that.
|
|
99
|
+
depends_on TEXT REFERENCES nodes(id),
|
|
100
|
+
position REAL NOT NULL DEFAULT 0,
|
|
101
|
+
created_at TEXT NOT NULL,
|
|
102
|
+
updated_at TEXT NOT NULL
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
-- The board view reads one column at a time, ordered. Both columns of the sort are in the index, so
|
|
106
|
+
-- the read needs no sorting pass — the same reason `audit_events_feed_idx` carries its tie-break
|
|
107
|
+
-- (anchrd/intel#620).
|
|
108
|
+
CREATE INDEX board_tasks_column_idx ON board_tasks(board_id, status, position);
|
|
109
|
+
|
|
110
|
+
-- "What is assigned to me, soonest first" — across every board, which is why `assignee_id` leads
|
|
111
|
+
-- and `board_id` does not appear.
|
|
112
|
+
CREATE INDEX board_tasks_assignee_idx ON board_tasks(assignee_id, due_date);
|
|
113
|
+
|
|
114
|
+
-- "What is blocked by this node" — the direction the interface asks in. Without it the question is
|
|
115
|
+
-- a scan of every task on every board.
|
|
116
|
+
CREATE INDEX board_tasks_depends_idx ON board_tasks(depends_on) WHERE depends_on IS NOT NULL;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anchrd/intel-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@anchrd/gate-sdk": "^0.21.0",
|
|
46
|
-
"@anchrd/intel-contract": "^0.
|
|
46
|
+
"@anchrd/intel-contract": "^0.23.0",
|
|
47
47
|
"@cfworker/json-schema": "^4.1.1",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
49
49
|
"fflate": "^0.8.3",
|