1import { legacyThreadPullRequestKey } from "@t3tools/shared/threadPullRequests";
2import * as Effect from "effect/Effect";
3import * as SqlClient from "effect/sql/SqlClient";
4
5interface LegacyLinkedThreadRow {
6 readonly threadId: string;
7 readonly updatedAt: string;
8 readonly linkedPullRequestJson: string;
9}
10
11interface LegacyLinkedPullRequest {
12 readonly repository: string;
13 readonly number: number;
14 readonly url: string;
15}
16
17function parseLegacyLinkedPullRequest(json: string): LegacyLinkedPullRequest | null {
18 try {
19 const value: unknown = JSON.parse(json);
20 if (typeof value !== "object" || value === null) return null;
21 const { repository, number, url } = value as Record<string, unknown>;
22 if (typeof repository !== "string" || repository.trim().length === 0) return null;
23 if (typeof number !== "number" || !Number.isInteger(number) || number < 1) return null;
24 if (typeof url !== "string" || url.trim().length === 0) return null;
25 return { repository, number, url };
26 } catch {
27 return null;
28 }
29}
30
31export default Effect.gen(function* () {
32 const sql = yield* SqlClient.SqlClient;
33
34 yield* sql`
35 CREATE TABLE IF NOT EXISTS projection_thread_pull_requests (
36 thread_id TEXT NOT NULL,
37 host TEXT NOT NULL,
38 repository TEXT NOT NULL,
39 number INTEGER NOT NULL,
40 url TEXT NOT NULL,
41 source TEXT NOT NULL,
42 linked_at TEXT NOT NULL,
43 snapshot_json TEXT,
44 stack_json TEXT,
45 PRIMARY KEY (thread_id, host, repository, number)
46 )
47 `;
48
49 yield* sql`
50 CREATE INDEX IF NOT EXISTS idx_projection_thread_pull_requests_pr
51 ON projection_thread_pull_requests(host, repository, number)
52 `;
53
54 const legacyRows = yield* sql<LegacyLinkedThreadRow>`
55 SELECT
56 thread_id AS "threadId",
57 updated_at AS "updatedAt",
58 linked_pull_request_json AS "linkedPullRequestJson"
59 FROM projection_threads
60 WHERE linked_pull_request_json IS NOT NULL
61 `;
62
63 for (const row of legacyRows) {
64 const linked = parseLegacyLinkedPullRequest(row.linkedPullRequestJson);
65 if (linked === null) continue;
66 const key = legacyThreadPullRequestKey(linked);
67 yield* sql`
68 INSERT OR IGNORE INTO projection_thread_pull_requests (
69 thread_id,
70 host,
71 repository,
72 number,
73 url,
74 source,
75 linked_at,
76 snapshot_json,
77 stack_json
78 )
79 VALUES (
80 ${row.threadId},
81 ${key.host},
82 ${key.repository},
83 ${linked.number},
84 ${linked.url},
85 'manual',
86 ${row.updatedAt},
87 NULL,
88 NULL
89 )
90 `;
91 }
92});