tenbrains 2.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/AGENTS.md +112 -0
- package/README.md +218 -0
- package/dist/ai/analyzer.d.ts +28 -0
- package/dist/ai/analyzer.d.ts.map +1 -0
- package/dist/ai/analyzer.js +35 -0
- package/dist/ai/analyzer.js.map +1 -0
- package/dist/ai/client.d.ts +12 -0
- package/dist/ai/client.d.ts.map +1 -0
- package/dist/ai/client.js +152 -0
- package/dist/ai/client.js.map +1 -0
- package/dist/ai/json.d.ts +7 -0
- package/dist/ai/json.d.ts.map +1 -0
- package/dist/ai/json.js +57 -0
- package/dist/ai/json.js.map +1 -0
- package/dist/ai/mock.d.ts +6 -0
- package/dist/ai/mock.d.ts.map +1 -0
- package/dist/ai/mock.js +66 -0
- package/dist/ai/mock.js.map +1 -0
- package/dist/ai/prompts.d.ts +19 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +40 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/providers.d.ts +22 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +56 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/ai/resolve.d.ts +24 -0
- package/dist/ai/resolve.d.ts.map +1 -0
- package/dist/ai/resolve.js +33 -0
- package/dist/ai/resolve.js.map +1 -0
- package/dist/bin/preflight.d.ts +2 -0
- package/dist/bin/preflight.d.ts.map +1 -0
- package/dist/bin/preflight.js +11 -0
- package/dist/bin/preflight.js.map +1 -0
- package/dist/bin/tenbrains.d.ts +3 -0
- package/dist/bin/tenbrains.d.ts.map +1 -0
- package/dist/bin/tenbrains.js +9 -0
- package/dist/bin/tenbrains.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +554 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/analyze.d.ts +7 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +204 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/bookmark.d.ts +9 -0
- package/dist/commands/bookmark.d.ts.map +1 -0
- package/dist/commands/bookmark.js +120 -0
- package/dist/commands/bookmark.js.map +1 -0
- package/dist/commands/config.d.ts +15 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +142 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/db.d.ts +9 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/db.js +64 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/digest.d.ts +19 -0
- package/dist/commands/digest.d.ts.map +1 -0
- package/dist/commands/digest.js +71 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/import.d.ts +11 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/commands/import.js +78 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/learn.d.ts +9 -0
- package/dist/commands/learn.d.ts.map +1 -0
- package/dist/commands/learn.js +149 -0
- package/dist/commands/learn.js.map +1 -0
- package/dist/commands/manifest.d.ts +13 -0
- package/dist/commands/manifest.d.ts.map +1 -0
- package/dist/commands/manifest.js +62 -0
- package/dist/commands/manifest.js.map +1 -0
- package/dist/commands/record.d.ts +10 -0
- package/dist/commands/record.d.ts.map +1 -0
- package/dist/commands/record.js +37 -0
- package/dist/commands/record.js.map +1 -0
- package/dist/commands/search.d.ts +5 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +33 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/shared.d.ts +9 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +13 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/suggest.d.ts +9 -0
- package/dist/commands/suggest.d.ts.map +1 -0
- package/dist/commands/suggest.js +102 -0
- package/dist/commands/suggest.js.map +1 -0
- package/dist/commands/takeaway.d.ts +9 -0
- package/dist/commands/takeaway.d.ts.map +1 -0
- package/dist/commands/takeaway.js +167 -0
- package/dist/commands/takeaway.js.map +1 -0
- package/dist/core/config.d.ts +52 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +180 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/context.d.ts +28 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +40 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/errors.d.ts +33 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +82 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/input.d.ts +9 -0
- package/dist/core/input.d.ts.map +1 -0
- package/dist/core/input.js +47 -0
- package/dist/core/input.js.map +1 -0
- package/dist/core/opts.d.ts +8 -0
- package/dist/core/opts.d.ts.map +1 -0
- package/dist/core/opts.js +38 -0
- package/dist/core/opts.js.map +1 -0
- package/dist/core/output.d.ts +58 -0
- package/dist/core/output.d.ts.map +1 -0
- package/dist/core/output.js +76 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/paths.d.ts +20 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +31 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/text.d.ts +10 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +132 -0
- package/dist/core/text.js.map +1 -0
- package/dist/core/validate.d.ts +5 -0
- package/dist/core/validate.d.ts.map +1 -0
- package/dist/core/validate.js +17 -0
- package/dist/core/validate.js.map +1 -0
- package/dist/db/database.d.ts +27 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/database.js +90 -0
- package/dist/db/database.js.map +1 -0
- package/dist/db/fts.d.ts +12 -0
- package/dist/db/fts.d.ts.map +1 -0
- package/dist/db/fts.js +42 -0
- package/dist/db/fts.js.map +1 -0
- package/dist/db/migrations.d.ts +20 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +243 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/repositories.d.ts +130 -0
- package/dist/db/repositories.d.ts.map +1 -0
- package/dist/db/repositories.js +418 -0
- package/dist/db/repositories.js.map +1 -0
- package/dist/domain/learn.d.ts +25 -0
- package/dist/domain/learn.d.ts.map +1 -0
- package/dist/domain/learn.js +85 -0
- package/dist/domain/learn.js.map +1 -0
- package/dist/domain/schemas.d.ts +169 -0
- package/dist/domain/schemas.d.ts.map +1 -0
- package/dist/domain/schemas.js +54 -0
- package/dist/domain/schemas.js.map +1 -0
- package/dist/domain/search.d.ts +32 -0
- package/dist/domain/search.d.ts.map +1 -0
- package/dist/domain/search.js +77 -0
- package/dist/domain/search.js.map +1 -0
- package/dist/domain/suggest.d.ts +17 -0
- package/dist/domain/suggest.d.ts.map +1 -0
- package/dist/domain/suggest.js +114 -0
- package/dist/domain/suggest.js.map +1 -0
- package/dist/domain/tags.d.ts +9 -0
- package/dist/domain/tags.d.ts.map +1 -0
- package/dist/domain/tags.js +40 -0
- package/dist/domain/tags.js.map +1 -0
- package/dist/domain/types.d.ts +84 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +2 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/x/archive.d.ts +32 -0
- package/dist/x/archive.d.ts.map +1 -0
- package/dist/x/archive.js +108 -0
- package/dist/x/archive.js.map +1 -0
- package/dist/x/client.d.ts +61 -0
- package/dist/x/client.d.ts.map +1 -0
- package/dist/x/client.js +321 -0
- package/dist/x/client.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
export type IdPrefix = "post" | "ana" | "acc" | "snap" | "bm" | "sug" | "trk";
|
|
3
|
+
/** Sortable, prefixed id: <prefix>_<base36 time><random hex>. */
|
|
4
|
+
export declare function newId(prefix: IdPrefix): string;
|
|
5
|
+
export declare function nowIso(): string;
|
|
6
|
+
export interface OpenDbOptions {
|
|
7
|
+
path: string;
|
|
8
|
+
/** When true, only opens if the schema already exists (no migrations run). */
|
|
9
|
+
readonly?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Thin wrapper over Node's built-in SQLite. Owns connection lifecycle, pragmas,
|
|
13
|
+
* and migration. The raw handle is exposed for the repositories layer.
|
|
14
|
+
*/
|
|
15
|
+
export declare class Database {
|
|
16
|
+
readonly handle: DatabaseSync;
|
|
17
|
+
readonly path: string;
|
|
18
|
+
private constructor();
|
|
19
|
+
static open(options: OpenDbOptions): Database;
|
|
20
|
+
schemaVersion(): number;
|
|
21
|
+
/** Row counts per table, for `db stats`. */
|
|
22
|
+
stats(): Record<string, number>;
|
|
23
|
+
/** Run a function inside a transaction, rolling back on any thrown error. */
|
|
24
|
+
transaction<T>(fn: () => T): T;
|
|
25
|
+
close(): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;AAE9E,iEAAiE;AACjE,wBAAgB,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAI9C;AAED,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,QAAQ;IACnB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,QAAQ;IAuB7C,aAAa,IAAI,MAAM;IAOvB,4CAA4C;IAC5C,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAmB/B,6EAA6E;IAC7E,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAY9B,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { DatabaseSync } from "node:sqlite";
|
|
5
|
+
import { CliError } from "../core/errors.js";
|
|
6
|
+
import { runMigrations } from "./migrations.js";
|
|
7
|
+
/** Sortable, prefixed id: <prefix>_<base36 time><random hex>. */
|
|
8
|
+
export function newId(prefix) {
|
|
9
|
+
const time = Date.now().toString(36).padStart(9, "0");
|
|
10
|
+
const rand = randomBytes(5).toString("hex");
|
|
11
|
+
return `${prefix}_${time}${rand}`;
|
|
12
|
+
}
|
|
13
|
+
export function nowIso() {
|
|
14
|
+
return new Date().toISOString();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Thin wrapper over Node's built-in SQLite. Owns connection lifecycle, pragmas,
|
|
18
|
+
* and migration. The raw handle is exposed for the repositories layer.
|
|
19
|
+
*/
|
|
20
|
+
export class Database {
|
|
21
|
+
handle;
|
|
22
|
+
path;
|
|
23
|
+
constructor(handle, dbPath) {
|
|
24
|
+
this.handle = handle;
|
|
25
|
+
this.path = dbPath;
|
|
26
|
+
}
|
|
27
|
+
static open(options) {
|
|
28
|
+
const dbPath = options.path;
|
|
29
|
+
if (dbPath !== ":memory:") {
|
|
30
|
+
mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
let handle;
|
|
33
|
+
try {
|
|
34
|
+
handle = new DatabaseSync(dbPath);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new CliError("CONFIG_ERROR", `Failed to open database at ${dbPath}`, { cause: error });
|
|
38
|
+
}
|
|
39
|
+
handle.exec("PRAGMA foreign_keys = ON");
|
|
40
|
+
if (dbPath !== ":memory:") {
|
|
41
|
+
handle.exec("PRAGMA journal_mode = WAL");
|
|
42
|
+
handle.exec("PRAGMA busy_timeout = 5000");
|
|
43
|
+
}
|
|
44
|
+
const db = new Database(handle, dbPath);
|
|
45
|
+
if (!options.readonly) {
|
|
46
|
+
runMigrations(handle);
|
|
47
|
+
}
|
|
48
|
+
return db;
|
|
49
|
+
}
|
|
50
|
+
schemaVersion() {
|
|
51
|
+
const row = this.handle.prepare("PRAGMA user_version").get();
|
|
52
|
+
return row?.user_version ?? 0;
|
|
53
|
+
}
|
|
54
|
+
/** Row counts per table, for `db stats`. */
|
|
55
|
+
stats() {
|
|
56
|
+
const tables = [
|
|
57
|
+
"posts",
|
|
58
|
+
"analyses",
|
|
59
|
+
"accounts",
|
|
60
|
+
"takeaway_snapshots",
|
|
61
|
+
"bookmarks",
|
|
62
|
+
"suggestions",
|
|
63
|
+
"learning_tracks",
|
|
64
|
+
"track_progress",
|
|
65
|
+
];
|
|
66
|
+
const out = {};
|
|
67
|
+
for (const table of tables) {
|
|
68
|
+
const row = this.handle.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get();
|
|
69
|
+
out[table] = row.n;
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** Run a function inside a transaction, rolling back on any thrown error. */
|
|
74
|
+
transaction(fn) {
|
|
75
|
+
this.handle.exec("BEGIN");
|
|
76
|
+
try {
|
|
77
|
+
const result = fn();
|
|
78
|
+
this.handle.exec("COMMIT");
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
this.handle.exec("ROLLBACK");
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
close() {
|
|
87
|
+
this.handle.close();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
|
package/dist/db/fts.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
/**
|
|
3
|
+
* Rebuild the `search_fts` index from the source tables. The doc-text
|
|
4
|
+
* expressions here define the *current* index shape; the frozen copies in the
|
|
5
|
+
* `fts5-search-index` migration were snapshotted from this file. Keep changes
|
|
6
|
+
* here (plus a new migration updating the triggers) — never edit the old
|
|
7
|
+
* migration.
|
|
8
|
+
*/
|
|
9
|
+
export declare function rebuildSearchIndex(db: DatabaseSync): {
|
|
10
|
+
indexed: number;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=fts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fts.d.ts","sourceRoot":"","sources":["../../src/db/fts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAiCxE"}
|
package/dist/db/fts.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebuild the `search_fts` index from the source tables. The doc-text
|
|
3
|
+
* expressions here define the *current* index shape; the frozen copies in the
|
|
4
|
+
* `fts5-search-index` migration were snapshotted from this file. Keep changes
|
|
5
|
+
* here (plus a new migration updating the triggers) — never edit the old
|
|
6
|
+
* migration.
|
|
7
|
+
*/
|
|
8
|
+
export function rebuildSearchIndex(db) {
|
|
9
|
+
db.exec(/* sql */ `
|
|
10
|
+
DELETE FROM search_fts;
|
|
11
|
+
|
|
12
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
13
|
+
SELECT 'analysis', a.id, a.topic,
|
|
14
|
+
COALESCE(p.text, '') || ' ' || a.topic || ' ' || a.summary || ' ' || a.intent || ' ' ||
|
|
15
|
+
COALESCE((
|
|
16
|
+
SELECT group_concat(json_extract(j.value, '$.name') || ' ' ||
|
|
17
|
+
COALESCE(json_extract(j.value, '$.whyItMattersInTweet'), ''), ' ')
|
|
18
|
+
FROM json_each(a.concepts_json) j
|
|
19
|
+
), '')
|
|
20
|
+
FROM analyses a LEFT JOIN posts p ON p.id = a.post_id;
|
|
21
|
+
|
|
22
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
23
|
+
SELECT 'takeaway', s.id, '@' || COALESCE(acc.username, 'account'),
|
|
24
|
+
COALESCE(acc.username, '') || ' ' || s.summary || ' ' ||
|
|
25
|
+
COALESCE((SELECT group_concat(j.value, ' ') FROM json_each(s.takeaways_json) j), '')
|
|
26
|
+
FROM takeaway_snapshots s LEFT JOIN accounts acc ON acc.id = s.account_id;
|
|
27
|
+
|
|
28
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
29
|
+
SELECT 'bookmark', b.id,
|
|
30
|
+
CASE WHEN json_array_length(b.tags_json) > 0
|
|
31
|
+
THEN '#' || json_extract(b.tags_json, '$[0]')
|
|
32
|
+
ELSE COALESCE(p.author_username, 'bookmark')
|
|
33
|
+
END,
|
|
34
|
+
COALESCE(p.text, '') || ' ' ||
|
|
35
|
+
COALESCE((SELECT group_concat(j.value, ' ') FROM json_each(b.tags_json) j), '') || ' ' ||
|
|
36
|
+
COALESCE(b.note, '')
|
|
37
|
+
FROM bookmarks b LEFT JOIN posts p ON p.id = b.post_id;
|
|
38
|
+
`);
|
|
39
|
+
const row = db.prepare("SELECT COUNT(*) AS n FROM search_fts").get();
|
|
40
|
+
return { indexed: row.n };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=fts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fts.js","sourceRoot":"","sources":["../../src/db/fts.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAgB;IACjD,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BjB,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,GAAG,EAAmB,CAAC;IACtF,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
export interface Migration {
|
|
3
|
+
version: number;
|
|
4
|
+
name: string;
|
|
5
|
+
up: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Ordered, append-only schema migrations. The applied version is tracked with
|
|
9
|
+
* SQLite's built-in `PRAGMA user_version`, so no bookkeeping table is needed.
|
|
10
|
+
* Never edit an existing migration's SQL — add a new one.
|
|
11
|
+
*/
|
|
12
|
+
export declare const MIGRATIONS: Migration[];
|
|
13
|
+
export declare function currentSchemaVersion(): number;
|
|
14
|
+
/** Apply any migrations newer than the database's current user_version. */
|
|
15
|
+
export declare function runMigrations(db: DatabaseSync): {
|
|
16
|
+
from: number;
|
|
17
|
+
to: number;
|
|
18
|
+
applied: string[];
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,SAAS,EAiNjC,CAAC;AAEF,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,EAAE,EAAE,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAqB/F"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordered, append-only schema migrations. The applied version is tracked with
|
|
3
|
+
* SQLite's built-in `PRAGMA user_version`, so no bookkeeping table is needed.
|
|
4
|
+
* Never edit an existing migration's SQL — add a new one.
|
|
5
|
+
*/
|
|
6
|
+
export const MIGRATIONS = [
|
|
7
|
+
{
|
|
8
|
+
version: 1,
|
|
9
|
+
name: "initial-schema",
|
|
10
|
+
up: /* sql */ `
|
|
11
|
+
CREATE TABLE posts (
|
|
12
|
+
id TEXT PRIMARY KEY,
|
|
13
|
+
external_id TEXT UNIQUE,
|
|
14
|
+
url TEXT,
|
|
15
|
+
author_username TEXT,
|
|
16
|
+
author_name TEXT,
|
|
17
|
+
text TEXT NOT NULL,
|
|
18
|
+
posted_at TEXT,
|
|
19
|
+
raw_json TEXT,
|
|
20
|
+
created_at TEXT NOT NULL
|
|
21
|
+
);
|
|
22
|
+
CREATE INDEX idx_posts_author ON posts(author_username);
|
|
23
|
+
CREATE INDEX idx_posts_created ON posts(created_at);
|
|
24
|
+
|
|
25
|
+
CREATE TABLE analyses (
|
|
26
|
+
id TEXT PRIMARY KEY,
|
|
27
|
+
post_id TEXT NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
|
|
28
|
+
provider TEXT NOT NULL,
|
|
29
|
+
model TEXT NOT NULL,
|
|
30
|
+
topic TEXT NOT NULL,
|
|
31
|
+
summary TEXT NOT NULL,
|
|
32
|
+
intent TEXT NOT NULL,
|
|
33
|
+
concepts_json TEXT NOT NULL,
|
|
34
|
+
mock INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
created_at TEXT NOT NULL
|
|
36
|
+
);
|
|
37
|
+
CREATE INDEX idx_analyses_post ON analyses(post_id);
|
|
38
|
+
CREATE INDEX idx_analyses_created ON analyses(created_at);
|
|
39
|
+
|
|
40
|
+
CREATE TABLE accounts (
|
|
41
|
+
id TEXT PRIMARY KEY,
|
|
42
|
+
username TEXT NOT NULL UNIQUE,
|
|
43
|
+
name TEXT,
|
|
44
|
+
followed_at TEXT NOT NULL,
|
|
45
|
+
last_refreshed_at TEXT
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE TABLE takeaway_snapshots (
|
|
49
|
+
id TEXT PRIMARY KEY,
|
|
50
|
+
account_id TEXT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
51
|
+
provider TEXT NOT NULL,
|
|
52
|
+
model TEXT NOT NULL,
|
|
53
|
+
summary TEXT NOT NULL,
|
|
54
|
+
takeaways_json TEXT NOT NULL,
|
|
55
|
+
source_post_ids_json TEXT NOT NULL,
|
|
56
|
+
post_count INTEGER NOT NULL,
|
|
57
|
+
mock INTEGER NOT NULL DEFAULT 0,
|
|
58
|
+
created_at TEXT NOT NULL
|
|
59
|
+
);
|
|
60
|
+
CREATE INDEX idx_snapshots_account ON takeaway_snapshots(account_id, created_at);
|
|
61
|
+
|
|
62
|
+
CREATE TABLE bookmarks (
|
|
63
|
+
id TEXT PRIMARY KEY,
|
|
64
|
+
post_id TEXT NOT NULL UNIQUE REFERENCES posts(id) ON DELETE CASCADE,
|
|
65
|
+
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
66
|
+
note TEXT,
|
|
67
|
+
source TEXT NOT NULL DEFAULT 'cli',
|
|
68
|
+
created_at TEXT NOT NULL,
|
|
69
|
+
updated_at TEXT NOT NULL
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
CREATE TABLE suggestions (
|
|
73
|
+
id TEXT PRIMARY KEY,
|
|
74
|
+
post_id TEXT NOT NULL UNIQUE REFERENCES posts(id) ON DELETE CASCADE,
|
|
75
|
+
reason TEXT NOT NULL,
|
|
76
|
+
score REAL NOT NULL DEFAULT 0,
|
|
77
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
78
|
+
created_at TEXT NOT NULL,
|
|
79
|
+
updated_at TEXT NOT NULL
|
|
80
|
+
);
|
|
81
|
+
CREATE INDEX idx_suggestions_status ON suggestions(status, score);
|
|
82
|
+
|
|
83
|
+
CREATE TABLE learning_tracks (
|
|
84
|
+
id TEXT PRIMARY KEY,
|
|
85
|
+
analysis_id TEXT NOT NULL REFERENCES analyses(id) ON DELETE CASCADE,
|
|
86
|
+
minutes_per_day INTEGER NOT NULL,
|
|
87
|
+
ratings_json TEXT NOT NULL,
|
|
88
|
+
days_json TEXT NOT NULL,
|
|
89
|
+
created_at TEXT NOT NULL
|
|
90
|
+
);
|
|
91
|
+
CREATE INDEX idx_tracks_analysis ON learning_tracks(analysis_id);
|
|
92
|
+
`,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
version: 2,
|
|
96
|
+
name: "fts5-search-index",
|
|
97
|
+
// One FTS5 table indexes every searchable record (analyses, takeaway
|
|
98
|
+
// snapshots, bookmarks) as a (title, body) document, kept in sync by
|
|
99
|
+
// triggers on the source tables so no application write path can forget
|
|
100
|
+
// it. Porter stemming makes "embedding" match "embeddings". The doc-text
|
|
101
|
+
// expressions are mirrored in src/db/fts.ts (`rebuildSearchIndex`) — keep
|
|
102
|
+
// future changes there, this migration is frozen.
|
|
103
|
+
up: /* sql */ `
|
|
104
|
+
CREATE VIRTUAL TABLE search_fts USING fts5(
|
|
105
|
+
type UNINDEXED,
|
|
106
|
+
ref_id UNINDEXED,
|
|
107
|
+
title,
|
|
108
|
+
body,
|
|
109
|
+
tokenize = 'porter unicode61 remove_diacritics 2'
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
CREATE TRIGGER trg_fts_analysis_ai AFTER INSERT ON analyses BEGIN
|
|
113
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
114
|
+
VALUES (
|
|
115
|
+
'analysis', NEW.id, NEW.topic,
|
|
116
|
+
COALESCE((SELECT text FROM posts WHERE id = NEW.post_id), '') || ' ' ||
|
|
117
|
+
NEW.topic || ' ' || NEW.summary || ' ' || NEW.intent || ' ' ||
|
|
118
|
+
COALESCE((
|
|
119
|
+
SELECT group_concat(json_extract(j.value, '$.name') || ' ' ||
|
|
120
|
+
COALESCE(json_extract(j.value, '$.whyItMattersInTweet'), ''), ' ')
|
|
121
|
+
FROM json_each(NEW.concepts_json) j
|
|
122
|
+
), '')
|
|
123
|
+
);
|
|
124
|
+
END;
|
|
125
|
+
CREATE TRIGGER trg_fts_analysis_ad AFTER DELETE ON analyses BEGIN
|
|
126
|
+
DELETE FROM search_fts WHERE type = 'analysis' AND ref_id = OLD.id;
|
|
127
|
+
END;
|
|
128
|
+
|
|
129
|
+
CREATE TRIGGER trg_fts_snapshot_ai AFTER INSERT ON takeaway_snapshots BEGIN
|
|
130
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
131
|
+
VALUES (
|
|
132
|
+
'takeaway', NEW.id,
|
|
133
|
+
'@' || COALESCE((SELECT username FROM accounts WHERE id = NEW.account_id), 'account'),
|
|
134
|
+
COALESCE((SELECT username FROM accounts WHERE id = NEW.account_id), '') || ' ' ||
|
|
135
|
+
NEW.summary || ' ' ||
|
|
136
|
+
COALESCE((SELECT group_concat(j.value, ' ') FROM json_each(NEW.takeaways_json) j), '')
|
|
137
|
+
);
|
|
138
|
+
END;
|
|
139
|
+
CREATE TRIGGER trg_fts_snapshot_ad AFTER DELETE ON takeaway_snapshots BEGIN
|
|
140
|
+
DELETE FROM search_fts WHERE type = 'takeaway' AND ref_id = OLD.id;
|
|
141
|
+
END;
|
|
142
|
+
|
|
143
|
+
CREATE TRIGGER trg_fts_bookmark_ai AFTER INSERT ON bookmarks BEGIN
|
|
144
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
145
|
+
VALUES (
|
|
146
|
+
'bookmark', NEW.id,
|
|
147
|
+
CASE WHEN json_array_length(NEW.tags_json) > 0
|
|
148
|
+
THEN '#' || json_extract(NEW.tags_json, '$[0]')
|
|
149
|
+
ELSE COALESCE((SELECT author_username FROM posts WHERE id = NEW.post_id), 'bookmark')
|
|
150
|
+
END,
|
|
151
|
+
COALESCE((SELECT text FROM posts WHERE id = NEW.post_id), '') || ' ' ||
|
|
152
|
+
COALESCE((SELECT group_concat(j.value, ' ') FROM json_each(NEW.tags_json) j), '') || ' ' ||
|
|
153
|
+
COALESCE(NEW.note, '')
|
|
154
|
+
);
|
|
155
|
+
END;
|
|
156
|
+
CREATE TRIGGER trg_fts_bookmark_au AFTER UPDATE ON bookmarks BEGIN
|
|
157
|
+
DELETE FROM search_fts WHERE type = 'bookmark' AND ref_id = OLD.id;
|
|
158
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
159
|
+
VALUES (
|
|
160
|
+
'bookmark', NEW.id,
|
|
161
|
+
CASE WHEN json_array_length(NEW.tags_json) > 0
|
|
162
|
+
THEN '#' || json_extract(NEW.tags_json, '$[0]')
|
|
163
|
+
ELSE COALESCE((SELECT author_username FROM posts WHERE id = NEW.post_id), 'bookmark')
|
|
164
|
+
END,
|
|
165
|
+
COALESCE((SELECT text FROM posts WHERE id = NEW.post_id), '') || ' ' ||
|
|
166
|
+
COALESCE((SELECT group_concat(j.value, ' ') FROM json_each(NEW.tags_json) j), '') || ' ' ||
|
|
167
|
+
COALESCE(NEW.note, '')
|
|
168
|
+
);
|
|
169
|
+
END;
|
|
170
|
+
CREATE TRIGGER trg_fts_bookmark_ad AFTER DELETE ON bookmarks BEGIN
|
|
171
|
+
DELETE FROM search_fts WHERE type = 'bookmark' AND ref_id = OLD.id;
|
|
172
|
+
END;
|
|
173
|
+
|
|
174
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
175
|
+
SELECT 'analysis', a.id, a.topic,
|
|
176
|
+
COALESCE(p.text, '') || ' ' || a.topic || ' ' || a.summary || ' ' || a.intent || ' ' ||
|
|
177
|
+
COALESCE((
|
|
178
|
+
SELECT group_concat(json_extract(j.value, '$.name') || ' ' ||
|
|
179
|
+
COALESCE(json_extract(j.value, '$.whyItMattersInTweet'), ''), ' ')
|
|
180
|
+
FROM json_each(a.concepts_json) j
|
|
181
|
+
), '')
|
|
182
|
+
FROM analyses a LEFT JOIN posts p ON p.id = a.post_id;
|
|
183
|
+
|
|
184
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
185
|
+
SELECT 'takeaway', s.id, '@' || COALESCE(acc.username, 'account'),
|
|
186
|
+
COALESCE(acc.username, '') || ' ' || s.summary || ' ' ||
|
|
187
|
+
COALESCE((SELECT group_concat(j.value, ' ') FROM json_each(s.takeaways_json) j), '')
|
|
188
|
+
FROM takeaway_snapshots s LEFT JOIN accounts acc ON acc.id = s.account_id;
|
|
189
|
+
|
|
190
|
+
INSERT INTO search_fts (type, ref_id, title, body)
|
|
191
|
+
SELECT 'bookmark', b.id,
|
|
192
|
+
CASE WHEN json_array_length(b.tags_json) > 0
|
|
193
|
+
THEN '#' || json_extract(b.tags_json, '$[0]')
|
|
194
|
+
ELSE COALESCE(p.author_username, 'bookmark')
|
|
195
|
+
END,
|
|
196
|
+
COALESCE(p.text, '') || ' ' ||
|
|
197
|
+
COALESCE((SELECT group_concat(j.value, ' ') FROM json_each(b.tags_json) j), '') || ' ' ||
|
|
198
|
+
COALESCE(b.note, '')
|
|
199
|
+
FROM bookmarks b LEFT JOIN posts p ON p.id = b.post_id;
|
|
200
|
+
`,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
version: 3,
|
|
204
|
+
name: "track-progress",
|
|
205
|
+
up: /* sql */ `
|
|
206
|
+
CREATE TABLE track_progress (
|
|
207
|
+
track_id TEXT NOT NULL REFERENCES learning_tracks(id) ON DELETE CASCADE,
|
|
208
|
+
day INTEGER NOT NULL,
|
|
209
|
+
notes TEXT,
|
|
210
|
+
completed_at TEXT NOT NULL,
|
|
211
|
+
PRIMARY KEY (track_id, day)
|
|
212
|
+
) WITHOUT ROWID;
|
|
213
|
+
`,
|
|
214
|
+
},
|
|
215
|
+
];
|
|
216
|
+
export function currentSchemaVersion() {
|
|
217
|
+
return MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
218
|
+
}
|
|
219
|
+
/** Apply any migrations newer than the database's current user_version. */
|
|
220
|
+
export function runMigrations(db) {
|
|
221
|
+
const row = db.prepare("PRAGMA user_version").get();
|
|
222
|
+
const from = row?.user_version ?? 0;
|
|
223
|
+
const applied = [];
|
|
224
|
+
for (const migration of MIGRATIONS) {
|
|
225
|
+
if (migration.version <= from) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
db.exec("BEGIN");
|
|
229
|
+
try {
|
|
230
|
+
db.exec(migration.up);
|
|
231
|
+
db.exec(`PRAGMA user_version = ${migration.version}`);
|
|
232
|
+
db.exec("COMMIT");
|
|
233
|
+
applied.push(migration.name);
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
db.exec("ROLLBACK");
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const to = currentSchemaVersion();
|
|
241
|
+
return { from, to, applied };
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAgB;IACrC;QACE,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,gBAAgB;QACtB,EAAE,EAAE,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkFb;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,mBAAmB;QACzB,qEAAqE;QACrE,qEAAqE;QACrE,wEAAwE;QACxE,yEAAyE;QACzE,0EAA0E;QAC1E,kDAAkD;QAClD,EAAE,EAAE,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiGb;KACF;IACD;QACE,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,gBAAgB;QACtB,EAAE,EAAE,SAAS,CAAC;;;;;;;;KAQb;KACF;CACF,CAAC;AAEF,MAAM,UAAU,oBAAoB;IAClC,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,aAAa,CAAC,EAAgB;IAC5C,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,EAA0C,CAAC;IAC5F,MAAM,IAAI,GAAG,GAAG,EAAE,YAAY,IAAI,CAAC,CAAC;IACpC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,SAAS,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;YAC9B,SAAS;QACX,CAAC;QACD,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACtB,EAAE,CAAC,IAAI,CAAC,yBAAyB,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;YACtD,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACpB,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,EAAE,GAAG,oBAAoB,EAAE,CAAC;IAClC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import type { Account, Analysis, Bookmark, Concept, ConceptRating, LearningDay, LearningTrack, Post, Suggestion, SuggestionStatus, TakeawaySnapshot, TrackDayProgress } from "../domain/types.js";
|
|
3
|
+
import { type Database } from "./database.js";
|
|
4
|
+
export interface NewPost {
|
|
5
|
+
text: string;
|
|
6
|
+
externalId?: string | undefined;
|
|
7
|
+
url?: string | undefined;
|
|
8
|
+
authorUsername?: string | undefined;
|
|
9
|
+
authorName?: string | undefined;
|
|
10
|
+
postedAt?: string | undefined;
|
|
11
|
+
raw?: unknown;
|
|
12
|
+
}
|
|
13
|
+
export declare class PostsRepo {
|
|
14
|
+
private readonly db;
|
|
15
|
+
constructor(db: DatabaseSync);
|
|
16
|
+
create(data: NewPost): Post;
|
|
17
|
+
/** Insert, or return the existing post when externalId already present. */
|
|
18
|
+
ingest(data: NewPost): {
|
|
19
|
+
post: Post;
|
|
20
|
+
deduped: boolean;
|
|
21
|
+
};
|
|
22
|
+
findById(id: string): Post | null;
|
|
23
|
+
findByExternalId(externalId: string): Post | null;
|
|
24
|
+
list(limit: number, offset: number): Post[];
|
|
25
|
+
all(): Post[];
|
|
26
|
+
}
|
|
27
|
+
export declare class AnalysesRepo {
|
|
28
|
+
private readonly db;
|
|
29
|
+
constructor(db: DatabaseSync);
|
|
30
|
+
create(data: {
|
|
31
|
+
postId: string;
|
|
32
|
+
provider: string;
|
|
33
|
+
model: string;
|
|
34
|
+
topic: string;
|
|
35
|
+
summary: string;
|
|
36
|
+
intent: string;
|
|
37
|
+
concepts: Concept[];
|
|
38
|
+
mock: boolean;
|
|
39
|
+
}): Analysis;
|
|
40
|
+
findById(id: string): Analysis | null;
|
|
41
|
+
latestForPost(postId: string): Analysis | null;
|
|
42
|
+
list(limit: number, offset: number, author?: string): Analysis[];
|
|
43
|
+
all(): Analysis[];
|
|
44
|
+
}
|
|
45
|
+
export declare class AccountsRepo {
|
|
46
|
+
private readonly db;
|
|
47
|
+
constructor(db: DatabaseSync);
|
|
48
|
+
create(username: string, name?: string): Account;
|
|
49
|
+
findById(id: string): Account | null;
|
|
50
|
+
findByUsername(username: string): Account | null;
|
|
51
|
+
list(): Account[];
|
|
52
|
+
touch(id: string): void;
|
|
53
|
+
delete(id: string): void;
|
|
54
|
+
}
|
|
55
|
+
export declare class SnapshotsRepo {
|
|
56
|
+
private readonly db;
|
|
57
|
+
constructor(db: DatabaseSync);
|
|
58
|
+
create(data: {
|
|
59
|
+
accountId: string;
|
|
60
|
+
provider: string;
|
|
61
|
+
model: string;
|
|
62
|
+
summary: string;
|
|
63
|
+
takeaways: string[];
|
|
64
|
+
sourcePostIds: string[];
|
|
65
|
+
mock: boolean;
|
|
66
|
+
}): TakeawaySnapshot;
|
|
67
|
+
findById(id: string): TakeawaySnapshot | null;
|
|
68
|
+
latestByAccount(accountId: string): TakeawaySnapshot | null;
|
|
69
|
+
listByAccount(accountId: string, limit: number): TakeawaySnapshot[];
|
|
70
|
+
all(): TakeawaySnapshot[];
|
|
71
|
+
}
|
|
72
|
+
export declare class BookmarksRepo {
|
|
73
|
+
private readonly db;
|
|
74
|
+
constructor(db: DatabaseSync);
|
|
75
|
+
create(data: {
|
|
76
|
+
postId: string;
|
|
77
|
+
tags: string[];
|
|
78
|
+
note?: string;
|
|
79
|
+
source: string;
|
|
80
|
+
}): Bookmark;
|
|
81
|
+
findById(id: string): Bookmark | null;
|
|
82
|
+
findByPostId(postId: string): Bookmark | null;
|
|
83
|
+
list(limit: number, offset: number): Bookmark[];
|
|
84
|
+
all(): Bookmark[];
|
|
85
|
+
updateTags(id: string, tags: string[]): Bookmark;
|
|
86
|
+
delete(id: string): void;
|
|
87
|
+
}
|
|
88
|
+
export declare class SuggestionsRepo {
|
|
89
|
+
private readonly db;
|
|
90
|
+
constructor(db: DatabaseSync);
|
|
91
|
+
/** Insert a pending suggestion or refresh score/reason if one exists for the post. */
|
|
92
|
+
upsert(data: {
|
|
93
|
+
postId: string;
|
|
94
|
+
reason: string;
|
|
95
|
+
score: number;
|
|
96
|
+
}): Suggestion;
|
|
97
|
+
findById(id: string): Suggestion | null;
|
|
98
|
+
findByPostId(postId: string): Suggestion | null;
|
|
99
|
+
list(status: SuggestionStatus | "all", limit: number): Suggestion[];
|
|
100
|
+
setStatus(id: string, status: SuggestionStatus): Suggestion;
|
|
101
|
+
}
|
|
102
|
+
export declare class TracksRepo {
|
|
103
|
+
private readonly db;
|
|
104
|
+
constructor(db: DatabaseSync);
|
|
105
|
+
create(data: {
|
|
106
|
+
analysisId: string;
|
|
107
|
+
minutesPerDay: number;
|
|
108
|
+
ratings: ConceptRating[];
|
|
109
|
+
days: LearningDay[];
|
|
110
|
+
}): LearningTrack;
|
|
111
|
+
findById(id: string): LearningTrack | null;
|
|
112
|
+
list(limit: number, analysisId?: string): LearningTrack[];
|
|
113
|
+
progressFor(trackId: string): TrackDayProgress[];
|
|
114
|
+
/** Record a day as done. Caller guards against duplicates (PK would throw). */
|
|
115
|
+
markDone(trackId: string, day: number, notes?: string): LearningTrack;
|
|
116
|
+
}
|
|
117
|
+
/** Facade exposing every repository over a single Database. */
|
|
118
|
+
export declare class Store {
|
|
119
|
+
readonly database: Database;
|
|
120
|
+
readonly posts: PostsRepo;
|
|
121
|
+
readonly analyses: AnalysesRepo;
|
|
122
|
+
readonly accounts: AccountsRepo;
|
|
123
|
+
readonly snapshots: SnapshotsRepo;
|
|
124
|
+
readonly bookmarks: BookmarksRepo;
|
|
125
|
+
readonly suggestions: SuggestionsRepo;
|
|
126
|
+
readonly tracks: TracksRepo;
|
|
127
|
+
constructor(database: Database);
|
|
128
|
+
transaction<T>(fn: () => T): T;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=repositories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../../src/db/repositories.ts"],"names":[],"mappings":"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"}
|