@lwelliott/cortex-cli 1.0.4 → 1.0.6
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/README.md +2 -2
- package/dist/cli/formatters/markdown.js +7 -7
- package/dist/cli/formatters/markdown.js.map +1 -1
- package/dist/cli/formatters/table.js +10 -10
- package/dist/cli/formatters/table.js.map +1 -1
- package/dist/cli/index.js +4 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/task.d.ts.map +1 -1
- package/dist/cli/task.js +32 -4
- package/dist/cli/task.js.map +1 -1
- package/dist/db/generated/schema-types.d.ts +8 -8
- package/dist/db/generated/schema-types.js +4 -4
- package/dist/db/generated/schema-types.js.map +1 -1
- package/dist/db/schema.d.ts +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +15 -1
- package/dist/db/schema.js.map +1 -1
- package/dist/models/relation.d.ts.map +1 -1
- package/dist/models/relation.js +81 -73
- package/dist/models/relation.js.map +1 -1
- package/dist/models/task.d.ts +4 -4
- package/dist/models/task.d.ts.map +1 -1
- package/dist/services/document-service.d.ts +3 -0
- package/dist/services/document-service.d.ts.map +1 -1
- package/dist/services/document-service.js +77 -76
- package/dist/services/document-service.js.map +1 -1
- package/dist/services/review-service.d.ts +47 -2
- package/dist/services/review-service.d.ts.map +1 -1
- package/dist/services/review-service.js +189 -27
- package/dist/services/review-service.js.map +1 -1
- package/dist/services/schema-migration-service.js +4 -4
- package/dist/services/schema-migration-service.js.map +1 -1
- package/dist/services/sprint.js +1 -1
- package/dist/services/task.d.ts +4 -4
- package/dist/services/task.d.ts.map +1 -1
- package/dist/services/task.js +18 -27
- package/dist/services/task.js.map +1 -1
- package/dist/utils/errors.d.ts +13 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +28 -4
- package/dist/utils/errors.js.map +1 -1
- package/package.json +1 -1
package/dist/db/schema.js
CHANGED
|
@@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|
|
23
23
|
id TEXT NOT NULL,
|
|
24
24
|
sprint_id TEXT NOT NULL REFERENCES sprints(id),
|
|
25
25
|
parent_id TEXT,
|
|
26
|
-
|
|
26
|
+
content TEXT,
|
|
27
27
|
task_type TEXT NOT NULL DEFAULT 'Feature'
|
|
28
28
|
CHECK (task_type IN ('Feature','Bug','Chore')),
|
|
29
29
|
priority TEXT NOT NULL DEFAULT 'P2'
|
|
@@ -786,6 +786,20 @@ exports.MIGRATIONS = [
|
|
|
786
786
|
CREATE INDEX IF NOT EXISTS idx_tasks_sprint_id_id ON tasks(sprint_id, id);
|
|
787
787
|
`,
|
|
788
788
|
},
|
|
789
|
+
{
|
|
790
|
+
version: 14,
|
|
791
|
+
description: 'SAD-0040: Rename tasks.description→content column (add content, copy data, drop description)',
|
|
792
|
+
sql: `
|
|
793
|
+
-- Step 1: Add content column if it doesn't exist (idempotent via migration runner error handling)
|
|
794
|
+
ALTER TABLE tasks ADD COLUMN content TEXT;
|
|
795
|
+
|
|
796
|
+
-- Step 2: Copy description data to content where content is still null
|
|
797
|
+
UPDATE tasks SET content = description WHERE description IS NOT NULL AND content IS NULL;
|
|
798
|
+
|
|
799
|
+
-- Step 3: Drop description column
|
|
800
|
+
ALTER TABLE tasks DROP COLUMN description;
|
|
801
|
+
`,
|
|
802
|
+
},
|
|
789
803
|
];
|
|
790
804
|
/** Current latest migration version */
|
|
791
805
|
exports.LATEST_VERSION = exports.MIGRATIONS[exports.MIGRATIONS.length - 1].version;
|
package/dist/db/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":";AAAA,2DAA2D;AAC3D,4DAA4D;;;AAS5D,2DAA2D;AAC9C,QAAA,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuNjC,CAAC;AAEF;;;;GAIG;AACU,QAAA,UAAU,GAAgB;IACrC;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,iCAAiC;QAC9C,GAAG,EAAE,0BAAkB;KACxB;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,0DAA0D;QACvE,GAAG,EAAE;;;KAGJ;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,sEAAsE;QACnF,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqCJ;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,sDAAsD;QACnE,GAAG,EAAE;;;;;;;;;;;;;;;KAeJ;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,kGAAkG;QAC/G,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiDJ;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,4FAA4F;QACzG,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqGJ;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,gFAAgF;QAC7F,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;KAyBJ;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,+FAA+F;QAC5G,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAwDJ;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,oFAAoF;QACjG,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiEJ;KACF;IACD;QACE,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,2EAA2E;QACxF,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkCJ;KACF;IACD;QACE,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,6EAA6E;QAC1F,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAuCJ;KACF;IACD;QACE,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,wFAAwF;QACrG,GAAG,EAAE;;;;;;;;;;;;KAYJ;KACF;IACD;QACE,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,iFAAiF;QAC9F,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2DJ;KACF;CACF,CAAC;AAEF,uCAAuC;AAC1B,QAAA,cAAc,GAAG,kBAAU,CAAC,kBAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;AAExE,qDAAqD;AACxC,QAAA,eAAe,GAAG;IAC7B,aAAa;IACb,OAAO;IACP,SAAS;IACT,OAAO;IACP,2FAA2F;IAC3F,gFAAgF;IAChF,gBAAgB;IAChB,WAAW;IACX,cAAc;IACd,SAAS;IACT,WAAW;IACX,mBAAmB;IACnB,uBAAuB;IACvB,cAAc;IACd,SAAS;CACD,CAAC;AAEX,qDAAqD;AACxC,QAAA,gBAAgB,GAAG;IAC9B,kBAAkB;IAClB,kBAAkB;IAClB,kBAAkB;IAClB,iBAAiB;IACjB,oBAAoB;IACpB,kBAAkB;IAClB,oBAAoB;IACpB,oBAAoB;IACpB,qBAAqB;IACrB,wBAAwB;IACxB,oBAAoB;IACpB,kBAAkB;IAClB,yBAAyB;IACzB,yBAAyB;IACzB,wBAAwB;IACxB,0BAA0B;IAC1B,+BAA+B;CACvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relation.d.ts","sourceRoot":"","sources":["../../src/models/relation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"relation.d.ts","sourceRoot":"","sources":["../../src/models/relation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAe,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAoB1F;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAM7F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,QAAQ,CAgFV;AAsDD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAkB9E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,oBAAoB,GAAG,QAAQ,EAAE,CA+B/F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,YAAY,EAAE,CAEvE"}
|
package/dist/models/relation.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Relation model: CRUD operations for graph relations
|
|
3
3
|
// MOD-010: Relations with constraint enforcement (AD-0017)
|
|
4
4
|
// + MOD-008: Schema dependency
|
|
5
|
+
// AD-0006: Advisory lock wrapping for write operations
|
|
5
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
7
|
exports.getRelationType = getRelationType;
|
|
7
8
|
exports.createRelation = createRelation;
|
|
@@ -9,6 +10,7 @@ exports.removeRelation = removeRelation;
|
|
|
9
10
|
exports.queryRelations = queryRelations;
|
|
10
11
|
exports.listRelationTypes = listRelationTypes;
|
|
11
12
|
const retry_1 = require("../db/retry");
|
|
13
|
+
const locks_1 = require("../db/locks");
|
|
12
14
|
const errors_1 = require("../utils/errors");
|
|
13
15
|
/** Parse JSON field safely */
|
|
14
16
|
function parseJson(raw, fallback) {
|
|
@@ -46,69 +48,72 @@ function getRelationType(db, name) {
|
|
|
46
48
|
* 8. Insert inverse
|
|
47
49
|
*/
|
|
48
50
|
function createRelation(db, fromId, relType, toId, properties) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
db.prepare(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
51
|
+
const owner = (0, locks_1.generateLockOwner)();
|
|
52
|
+
return (0, locks_1.withLock)(db, { name: 'graph:mutations', owner }, () => {
|
|
53
|
+
return (0, retry_1.withTransaction)(db, () => {
|
|
54
|
+
// 1. Self-relation check
|
|
55
|
+
if (fromId === toId) {
|
|
56
|
+
throw new errors_1.GraphSelfRelationError(fromId, relType);
|
|
57
|
+
}
|
|
58
|
+
// 2. Document existence (SAD-0035: entities table dropped, use documents)
|
|
59
|
+
const fromDoc = db.prepare('SELECT id, type AS type_name FROM documents WHERE id = ?').get(fromId);
|
|
60
|
+
if (!fromDoc)
|
|
61
|
+
throw new errors_1.GraphEntityNotFoundError(fromId);
|
|
62
|
+
const toDoc = db.prepare('SELECT id, type AS type_name FROM documents WHERE id = ?').get(toId);
|
|
63
|
+
if (!toDoc)
|
|
64
|
+
throw new errors_1.GraphEntityNotFoundError(toId);
|
|
65
|
+
// 3. Relation type existence
|
|
66
|
+
const rt = getRelationType(db, relType);
|
|
67
|
+
if (!rt)
|
|
68
|
+
throw new errors_1.GraphRelationTypeNotFoundError(relType);
|
|
69
|
+
// 4. Type constraint check
|
|
70
|
+
const fromTypes = parseJson(rt.from_types, []);
|
|
71
|
+
const toTypes = parseJson(rt.to_types, []);
|
|
72
|
+
if (fromTypes.length > 0 && !fromTypes.includes(fromDoc.type_name)) {
|
|
73
|
+
throw new errors_1.GraphTypeConstraintError(relType, 'from', fromDoc.type_name, fromTypes);
|
|
74
|
+
}
|
|
75
|
+
if (toTypes.length > 0 && !toTypes.includes(toDoc.type_name)) {
|
|
76
|
+
throw new errors_1.GraphTypeConstraintError(relType, 'to', toDoc.type_name, toTypes);
|
|
77
|
+
}
|
|
78
|
+
// 5. Cardinality constraint check
|
|
79
|
+
enforceCardinality(db, rt, fromId, toId, relType);
|
|
80
|
+
// 6. Duplicate check
|
|
81
|
+
const existing = db.prepare('SELECT id FROM relations WHERE from_id = ? AND rel_type = ? AND to_id = ?').get(fromId, relType, toId);
|
|
82
|
+
if (existing)
|
|
83
|
+
throw new errors_1.GraphDuplicateRelationError(fromId, relType, toId);
|
|
84
|
+
const now = new Date().toISOString();
|
|
85
|
+
const propsJson = JSON.stringify(properties ?? {});
|
|
86
|
+
// 7. Insert primary relation
|
|
87
|
+
const result = db.prepare(`
|
|
88
|
+
INSERT INTO relations (from_id, rel_type, to_id, properties, created_at, updated_at)
|
|
89
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
90
|
+
`).run(fromId, relType, toId, propsJson, now, now);
|
|
91
|
+
const relation = {
|
|
92
|
+
id: Number(result.lastInsertRowid),
|
|
93
|
+
from_id: fromId,
|
|
94
|
+
rel_type: relType,
|
|
95
|
+
to_id: toId,
|
|
96
|
+
properties: propsJson,
|
|
97
|
+
created_at: now,
|
|
98
|
+
updated_at: now,
|
|
99
|
+
};
|
|
100
|
+
// 8. Insert inverse relation if defined
|
|
101
|
+
if (rt.inverse_of) {
|
|
102
|
+
const inverseRt = getRelationType(db, rt.inverse_of);
|
|
103
|
+
if (inverseRt) {
|
|
104
|
+
// Check cardinality for inverse too
|
|
105
|
+
enforceCardinality(db, inverseRt, toId, fromId, rt.inverse_of);
|
|
106
|
+
const existingInverse = db.prepare('SELECT id FROM relations WHERE from_id = ? AND rel_type = ? AND to_id = ?').get(toId, rt.inverse_of, fromId);
|
|
107
|
+
if (!existingInverse) {
|
|
108
|
+
db.prepare(`
|
|
109
|
+
INSERT INTO relations (from_id, rel_type, to_id, properties, created_at, updated_at)
|
|
110
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
111
|
+
`).run(toId, rt.inverse_of, fromId, propsJson, now, now);
|
|
112
|
+
}
|
|
108
113
|
}
|
|
109
114
|
}
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
return relation;
|
|
116
|
+
});
|
|
112
117
|
});
|
|
113
118
|
}
|
|
114
119
|
/**
|
|
@@ -152,18 +157,21 @@ function enforceCardinality(db, rt, fromId, toId, relType) {
|
|
|
152
157
|
* Also removes any inverse relation.
|
|
153
158
|
*/
|
|
154
159
|
function removeRelation(db, relationId) {
|
|
155
|
-
(0,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
160
|
+
const owner = (0, locks_1.generateLockOwner)();
|
|
161
|
+
(0, locks_1.withLock)(db, { name: 'graph:mutations', owner }, () => {
|
|
162
|
+
(0, retry_1.withTransaction)(db, () => {
|
|
163
|
+
const rel = db.prepare('SELECT from_id, rel_type, to_id FROM relations WHERE id = ?')
|
|
164
|
+
.get(relationId);
|
|
165
|
+
if (!rel)
|
|
166
|
+
throw new errors_1.GraphRelationNotFoundError(relationId);
|
|
167
|
+
db.prepare('DELETE FROM relations WHERE id = ?').run(relationId);
|
|
168
|
+
// Remove inverse if defined
|
|
169
|
+
const rt = getRelationType(db, rel.rel_type);
|
|
170
|
+
if (rt?.inverse_of) {
|
|
171
|
+
db.prepare('DELETE FROM relations WHERE from_id = ? AND rel_type = ? AND to_id = ?')
|
|
172
|
+
.run(rel.to_id, rt.inverse_of, rel.from_id);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
167
175
|
});
|
|
168
176
|
}
|
|
169
177
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relation.js","sourceRoot":"","sources":["../../src/models/relation.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,2DAA2D;AAC3D,+BAA+B
|
|
1
|
+
{"version":3,"file":"relation.js","sourceRoot":"","sources":["../../src/models/relation.ts"],"names":[],"mappings":"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"}
|
package/dist/models/task.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface Task {
|
|
|
4
4
|
id: string;
|
|
5
5
|
sprint_id: string;
|
|
6
6
|
parent_id: string | null;
|
|
7
|
-
|
|
7
|
+
content: string | null;
|
|
8
8
|
task_type: TaskType;
|
|
9
9
|
priority: TaskPriority;
|
|
10
10
|
owner: RoleName;
|
|
@@ -28,7 +28,7 @@ export interface TaskResult {
|
|
|
28
28
|
id: string;
|
|
29
29
|
sprint_id: string;
|
|
30
30
|
parent_id: string | null;
|
|
31
|
-
|
|
31
|
+
content: string | null;
|
|
32
32
|
task_type: TaskType;
|
|
33
33
|
priority: TaskPriority;
|
|
34
34
|
owner: RoleName;
|
|
@@ -40,7 +40,7 @@ export interface TaskResult {
|
|
|
40
40
|
/** Detailed task information for `task show` */
|
|
41
41
|
export interface TaskDetail extends Task {
|
|
42
42
|
sprint_name: string | null;
|
|
43
|
-
|
|
43
|
+
parent_content: string | null;
|
|
44
44
|
}
|
|
45
45
|
/** Options for task creation */
|
|
46
46
|
export interface TaskAddOptions {
|
|
@@ -60,7 +60,7 @@ export interface TaskMoveContext {
|
|
|
60
60
|
}
|
|
61
61
|
/** Fields that can be updated on a task (metadata only — no stage/owner/sprint) */
|
|
62
62
|
export interface TaskUpdateFields {
|
|
63
|
-
|
|
63
|
+
content?: string;
|
|
64
64
|
task_type?: TaskType;
|
|
65
65
|
priority?: TaskPriority;
|
|
66
66
|
points?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/models/task.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAExF,gCAAgC;AAChC,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,
|
|
1
|
+
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/models/task.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAExF,gCAAgC;AAChC,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,8BAA8B;AAC9B,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,gDAAgD;AAChD,MAAM,WAAW,UAAW,SAAQ,IAAI;IACtC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,4BAA4B;AAC5B,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B"}
|
|
@@ -2,6 +2,7 @@ import type Database from 'better-sqlite3';
|
|
|
2
2
|
import type { Document, DocumentDetail, DocumentRegisterOptions, DocumentUpdateFields, SearchResult, ListOptions, SearchOptions } from '../models/document';
|
|
3
3
|
/**
|
|
4
4
|
* Register a new document.
|
|
5
|
+
* Wrapped with doc:global advisory lock (AD-0006) for multi-process coordination.
|
|
5
6
|
*/
|
|
6
7
|
export declare function registerDocument(db: Database.Database, id: string, type: string, title: string, opts?: DocumentRegisterOptions): Document;
|
|
7
8
|
/**
|
|
@@ -25,6 +26,7 @@ export declare function getDocument(db: Database.Database, id: string): Document
|
|
|
25
26
|
export declare function showDocument(db: Database.Database, id: string): DocumentDetail;
|
|
26
27
|
/**
|
|
27
28
|
* Update document metadata.
|
|
29
|
+
* Wrapped with doc:global advisory lock (AD-0006) for multi-process coordination.
|
|
28
30
|
* --meta replaces entirely (not merge), per spec.
|
|
29
31
|
*
|
|
30
32
|
* MOD-021: When fields.type changes the document type, the old entity is deleted
|
|
@@ -35,6 +37,7 @@ export declare function showDocument(db: Database.Database, id: string): Documen
|
|
|
35
37
|
export declare function updateDocument(db: Database.Database, id: string, fields: DocumentUpdateFields): Document;
|
|
36
38
|
/**
|
|
37
39
|
* Remove a document registration.
|
|
40
|
+
* Wrapped with doc:global advisory lock (AD-0006) for multi-process coordination.
|
|
38
41
|
*
|
|
39
42
|
* Deletion ordering per SAD-0035 (3-step sequence after entities table drop):
|
|
40
43
|
* Step 1: Delete relations (outgoing and incoming) referencing the document
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-service.d.ts","sourceRoot":"","sources":["../../src/services/document-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"document-service.d.ts","sourceRoot":"","sources":["../../src/services/document-service.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAoC5J;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,uBAAuB,GAC7B,QAAQ,CA8DV;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,IAAI,CAAC,EAAE,WAAW,GACjB,QAAQ,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAkDhC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,QAAQ,CAIvE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,CAkB9E;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,oBAAoB,GAC3B,QAAQ,CAiEV;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAgBtE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,aAAa,GACnB,YAAY,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAgIpC"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// SAD-0003: MOD-012 Document Registry & Content
|
|
4
4
|
// AD-0018: FTS5 with trigger-based index
|
|
5
5
|
// AD-0019: Dual-mode document storage
|
|
6
|
+
// AD-0006: Advisory lock wrapping for multi-step write operations
|
|
6
7
|
// SAD-0035: Entity auto-sync removed — entities table dropped
|
|
7
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
9
|
exports.registerDocument = registerDocument;
|
|
@@ -16,6 +17,7 @@ const crypto_1 = require("crypto");
|
|
|
16
17
|
const fs_1 = require("fs");
|
|
17
18
|
const errors_1 = require("../utils/errors");
|
|
18
19
|
const retry_1 = require("../db/retry");
|
|
20
|
+
const locks_1 = require("../db/locks");
|
|
19
21
|
// ─── Order-by validation ───
|
|
20
22
|
const VALID_ORDER_FIELDS = ['id', 'type', 'title', 'created', 'updated'];
|
|
21
23
|
function parseOrderBy(raw) {
|
|
@@ -41,6 +43,7 @@ function sha256(content) {
|
|
|
41
43
|
// ─── Public API ───
|
|
42
44
|
/**
|
|
43
45
|
* Register a new document.
|
|
46
|
+
* Wrapped with doc:global advisory lock (AD-0006) for multi-process coordination.
|
|
44
47
|
*/
|
|
45
48
|
function registerDocument(db, id, type, title, opts) {
|
|
46
49
|
if (!id || !id.trim())
|
|
@@ -49,25 +52,13 @@ function registerDocument(db, id, type, title, opts) {
|
|
|
49
52
|
throw new errors_1.DocumentMissingFieldError('type');
|
|
50
53
|
if (!title || !title.trim())
|
|
51
54
|
throw new errors_1.DocumentMissingFieldError('title');
|
|
52
|
-
// Check duplicate
|
|
53
|
-
const existing = db.prepare('SELECT id FROM documents WHERE id = ?').get(id);
|
|
54
|
-
if (existing)
|
|
55
|
-
throw new errors_1.DocumentDuplicateError(id);
|
|
56
|
-
// Pre-transaction doc type validation gate (FS-SS-012-0011, SAD-0027 §5)
|
|
57
|
-
// builtin:3.0: doc type IS entity type name (no mapping needed)
|
|
58
55
|
// Pre-transaction SRC storage enforcement (FS-SS-029-0001 v2 VR-001/VR-002)
|
|
59
56
|
// SRC documents must use filesystem storage; DB storage via --content-file is rejected
|
|
60
57
|
if (type === 'SRC' && (opts?.content || opts?.contentFile)) {
|
|
61
58
|
throw new errors_1.DocSrcStorageError();
|
|
62
59
|
}
|
|
63
|
-
//
|
|
64
|
-
// content_storage defaults to 'db'. Otherwise defaults to 'filesystem'.
|
|
65
|
-
const storage = opts?.contentStorage ?? ((opts?.content || opts?.contentFile) ? 'db' : 'filesystem');
|
|
66
|
-
const metadata = opts?.metadata ? JSON.stringify(opts.metadata) : '{}';
|
|
67
|
-
const now = new Date().toISOString();
|
|
60
|
+
// Pre-transaction file read (fail fast before lock acquisition)
|
|
68
61
|
// Handle content: compute hash and load content from file or direct input
|
|
69
|
-
// Content is ALWAYS stored in document_contents to enable FTS5 indexing,
|
|
70
|
-
// regardless of whether content_storage is 'db' or 'filesystem'.
|
|
71
62
|
let contentHash = null;
|
|
72
63
|
let contentValue = null;
|
|
73
64
|
let contentFilePath = null;
|
|
@@ -87,19 +78,27 @@ function registerDocument(db, id, type, title, opts) {
|
|
|
87
78
|
throw new errors_1.DocumentFileError(opts.contentFile, msg);
|
|
88
79
|
}
|
|
89
80
|
}
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
const storage = opts?.contentStorage ?? ((opts?.content || opts?.contentFile) ? 'db' : 'filesystem');
|
|
82
|
+
const metadata = opts?.metadata ? JSON.stringify(opts.metadata) : '{}';
|
|
92
83
|
const storedPath = opts?.path ?? contentFilePath ?? null;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
db.prepare(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
84
|
+
const owner = (0, locks_1.generateLockOwner)();
|
|
85
|
+
return (0, locks_1.withLock)(db, { name: 'doc:global', owner }, () => {
|
|
86
|
+
// Check duplicate inside lock to prevent TOCTOU races
|
|
87
|
+
const existing = db.prepare('SELECT id FROM documents WHERE id = ?').get(id);
|
|
88
|
+
if (existing)
|
|
89
|
+
throw new errors_1.DocumentDuplicateError(id);
|
|
90
|
+
const now = new Date().toISOString();
|
|
91
|
+
// Insert document and content (FK: document_contents → documents)
|
|
92
|
+
// SAD-0035: Entity auto-creation removed — entities table dropped
|
|
93
|
+
(0, retry_1.withTransaction)(db, () => {
|
|
94
|
+
db.prepare(`INSERT INTO documents (id, type, title, path, content_hash, content_storage, metadata, created_at, updated_at)
|
|
95
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, type, title, storedPath, contentHash, storage, metadata, now, now);
|
|
96
|
+
if (contentValue !== null) {
|
|
97
|
+
db.prepare(`INSERT INTO document_contents (document_id, content, content_hash, updated_at) VALUES (?, ?, ?, ?)`).run(id, contentValue, contentHash, now);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return getDocument(db, id);
|
|
101
101
|
});
|
|
102
|
-
return getDocument(db, id);
|
|
103
102
|
}
|
|
104
103
|
/**
|
|
105
104
|
* List documents with optional filters, pagination, sorting, and count-only support.
|
|
@@ -190,6 +189,7 @@ function showDocument(db, id) {
|
|
|
190
189
|
}
|
|
191
190
|
/**
|
|
192
191
|
* Update document metadata.
|
|
192
|
+
* Wrapped with doc:global advisory lock (AD-0006) for multi-process coordination.
|
|
193
193
|
* --meta replaces entirely (not merge), per spec.
|
|
194
194
|
*
|
|
195
195
|
* MOD-021: When fields.type changes the document type, the old entity is deleted
|
|
@@ -198,29 +198,10 @@ function showDocument(db, id) {
|
|
|
198
198
|
* atomic transaction per FS-SS-021-0004.
|
|
199
199
|
*/
|
|
200
200
|
function updateDocument(db, id, fields) {
|
|
201
|
+
// Pre-lock validation
|
|
201
202
|
const doc = getDocument(db, id);
|
|
202
|
-
|
|
203
|
-
const sets = ['updated_at = ?'];
|
|
204
|
-
const params = [now];
|
|
205
|
-
if (fields.title !== undefined) {
|
|
206
|
-
sets.push('title = ?');
|
|
207
|
-
params.push(fields.title);
|
|
208
|
-
}
|
|
209
|
-
if (fields.type !== undefined) {
|
|
210
|
-
sets.push('type = ?');
|
|
211
|
-
params.push(fields.type);
|
|
212
|
-
}
|
|
213
|
-
if (fields.metadata !== undefined) {
|
|
214
|
-
sets.push('metadata = ?');
|
|
215
|
-
params.push(JSON.stringify(fields.metadata));
|
|
216
|
-
}
|
|
217
|
-
if (fields.content_storage !== undefined) {
|
|
218
|
-
sets.push('content_storage = ?');
|
|
219
|
-
params.push(fields.content_storage);
|
|
220
|
-
}
|
|
221
|
-
// Handle content update
|
|
203
|
+
// Handle content file read before lock acquisition (fail fast)
|
|
222
204
|
let newContent;
|
|
223
|
-
let contentHash;
|
|
224
205
|
if (fields.contentFile) {
|
|
225
206
|
try {
|
|
226
207
|
newContent = (0, fs_1.readFileSync)(fields.contentFile, 'utf-8');
|
|
@@ -233,39 +214,58 @@ function updateDocument(db, id, fields) {
|
|
|
233
214
|
else if (fields.content !== undefined) {
|
|
234
215
|
newContent = fields.content;
|
|
235
216
|
}
|
|
236
|
-
if (newContent !== undefined) {
|
|
237
|
-
contentHash = sha256(newContent);
|
|
238
|
-
sets.push('content_hash = ?');
|
|
239
|
-
params.push(contentHash);
|
|
240
|
-
}
|
|
241
|
-
// SAD-0035: Entity sync on type change removed — entities table dropped
|
|
242
217
|
// content_storage switch validation (FR-URD-G001-0001 M6)
|
|
243
|
-
// Switching from filesystem to db without providing content creates an
|
|
244
|
-
// inconsistency: the document_contents table has no authoritative copy.
|
|
245
218
|
if (fields.content_storage !== undefined && fields.content_storage !== doc.content_storage) {
|
|
246
219
|
if (fields.content_storage === 'db' && doc.content_storage === 'filesystem' && newContent === undefined && !fields.contentFile) {
|
|
247
220
|
throw new errors_1.DocumentMissingFieldError('content');
|
|
248
221
|
}
|
|
249
222
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
223
|
+
const owner = (0, locks_1.generateLockOwner)();
|
|
224
|
+
return (0, locks_1.withLock)(db, { name: 'doc:global', owner }, () => {
|
|
225
|
+
const now = new Date().toISOString();
|
|
226
|
+
const sets = ['updated_at = ?'];
|
|
227
|
+
const params = [now];
|
|
228
|
+
if (fields.title !== undefined) {
|
|
229
|
+
sets.push('title = ?');
|
|
230
|
+
params.push(fields.title);
|
|
231
|
+
}
|
|
232
|
+
if (fields.type !== undefined) {
|
|
233
|
+
sets.push('type = ?');
|
|
234
|
+
params.push(fields.type);
|
|
235
|
+
}
|
|
236
|
+
if (fields.metadata !== undefined) {
|
|
237
|
+
sets.push('metadata = ?');
|
|
238
|
+
params.push(JSON.stringify(fields.metadata));
|
|
239
|
+
}
|
|
240
|
+
if (fields.content_storage !== undefined) {
|
|
241
|
+
sets.push('content_storage = ?');
|
|
242
|
+
params.push(fields.content_storage);
|
|
243
|
+
}
|
|
244
|
+
let contentHash;
|
|
257
245
|
if (newContent !== undefined) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
246
|
+
contentHash = sha256(newContent);
|
|
247
|
+
sets.push('content_hash = ?');
|
|
248
|
+
params.push(contentHash);
|
|
261
249
|
}
|
|
262
|
-
|
|
263
|
-
|
|
250
|
+
params.push(id);
|
|
251
|
+
// Single atomic transaction: content upsert + document update
|
|
252
|
+
// SAD-0035: Entity sync removed — entities table dropped
|
|
253
|
+
(0, retry_1.withTransaction)(db, () => {
|
|
254
|
+
// Step 1: Content upsert
|
|
255
|
+
if (newContent !== undefined) {
|
|
256
|
+
db.prepare(`INSERT INTO document_contents (document_id, content, content_hash, updated_at)
|
|
257
|
+
VALUES (?, ?, ?, ?)
|
|
258
|
+
ON CONFLICT(document_id) DO UPDATE SET content = excluded.content, content_hash = excluded.content_hash, updated_at = excluded.updated_at`).run(id, newContent, contentHash, now);
|
|
259
|
+
}
|
|
260
|
+
// Step 2: Document row UPDATE
|
|
261
|
+
db.prepare(`UPDATE documents SET ${sets.join(', ')} WHERE id = ?`).run(...params);
|
|
262
|
+
});
|
|
263
|
+
return getDocument(db, id);
|
|
264
264
|
});
|
|
265
|
-
return getDocument(db, id);
|
|
266
265
|
}
|
|
267
266
|
/**
|
|
268
267
|
* Remove a document registration.
|
|
268
|
+
* Wrapped with doc:global advisory lock (AD-0006) for multi-process coordination.
|
|
269
269
|
*
|
|
270
270
|
* Deletion ordering per SAD-0035 (3-step sequence after entities table drop):
|
|
271
271
|
* Step 1: Delete relations (outgoing and incoming) referencing the document
|
|
@@ -276,16 +276,17 @@ function updateDocument(db, id, fields) {
|
|
|
276
276
|
* Content is always deleted unconditionally per AD-0028 — there is no soft-delete option.
|
|
277
277
|
*/
|
|
278
278
|
function removeDocument(db, id) {
|
|
279
|
-
getDocument(db, id); // verify exists
|
|
280
|
-
(0,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
279
|
+
getDocument(db, id); // verify exists before lock
|
|
280
|
+
const owner = (0, locks_1.generateLockOwner)();
|
|
281
|
+
(0, locks_1.withLock)(db, { name: 'doc:global', owner }, () => {
|
|
282
|
+
(0, retry_1.withTransaction)(db, () => {
|
|
283
|
+
// Step 1: Delete all relations referencing this document (from_id OR to_id)
|
|
284
|
+
db.prepare('DELETE FROM relations WHERE from_id = ? OR to_id = ?').run(id, id);
|
|
285
|
+
// Step 2: Delete document_contents unconditionally per AD-0028
|
|
286
|
+
db.prepare('DELETE FROM document_contents WHERE document_id = ?').run(id);
|
|
287
|
+
// Step 3: Delete document row
|
|
288
|
+
db.prepare('DELETE FROM documents WHERE id = ?').run(id);
|
|
289
|
+
});
|
|
289
290
|
});
|
|
290
291
|
}
|
|
291
292
|
/**
|