@catalyst-cloud/schema 0.1.1 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@catalyst-cloud/schema",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "Typed Drizzle schema = single source of truth for the per-tenant Mirror DO SQLite store (CTC-13 / ADR-0002). Shared by the mirror Worker, the host-sync replica, and the browser OPFS replica.",
6
6
  "license": "MIT",
@@ -10,17 +10,13 @@
10
10
  "directory": "packages/schema"
11
11
  },
12
12
  "files": [
13
- "dist"
13
+ "src"
14
14
  ],
15
15
  "publishConfig": {
16
16
  "access": "public"
17
17
  },
18
18
  "exports": {
19
- ".": {
20
- "types": "./dist/index.d.ts",
21
- "import": "./dist/index.js",
22
- "default": "./dist/index.js"
23
- }
19
+ ".": "./src/index.ts"
24
20
  },
25
21
  "scripts": {
26
22
  "typecheck": "tsc --noEmit",
package/src/index.ts ADDED
@@ -0,0 +1,122 @@
1
+ // @catalyst-cloud/schema — the typed Drizzle SSOT for the Mirror DO store (CTC-13 / ADR-0002).
2
+ //
3
+ // Re-exports every table plus two consumption surfaces:
4
+ // • `mirrorSchema` — all tables, to pass as `drizzle(storage, { schema: mirrorSchema })` in the DO
5
+ // (and `drizzle(bunSqlite, …)` in the host-sync replica, `drizzle(sqliteProxy, …)` in the browser).
6
+ // • `MIRROR_TABLE_META` — per-ENTITY `{ pk, softDelete }` DERIVED from the schema via getTableConfig.
7
+ // This is what packages/replicate's applyDelta consumes to fold a change_log delta into a replica
8
+ // — replacing the hand-maintained `TABLES` map in apps/host-sync/src/apply.ts (the ADR-0002 "4th
9
+ // hand-copy" this schema deletes). Because it is derived, it can never drift from the table defs.
10
+
11
+ import { getTableConfig, type SQLiteTable } from "drizzle-orm/sqlite-core";
12
+ import {
13
+ issues,
14
+ labels,
15
+ users,
16
+ issue_labels,
17
+ relations,
18
+ issue_history,
19
+ projects,
20
+ cycles,
21
+ initiatives,
22
+ project_initiatives,
23
+ comments,
24
+ pull_requests,
25
+ check_runs,
26
+ commit_statuses,
27
+ reviews,
28
+ processed_events,
29
+ change_log,
30
+ sync_cursors,
31
+ } from "./mirror.js";
32
+
33
+ export * from "./mirror.js";
34
+
35
+ // The committed cross-surface migration bundle + the shared runner (CTC-41). Re-exported here so
36
+ // every consumer imports from the one package entrypoint: `applyMigrations(adapter, MIRROR_MIGRATIONS)`.
37
+ export { MIRROR_MIGRATIONS } from "./migrations.generated.js";
38
+ export {
39
+ applyMigrations,
40
+ type MigrationDb,
41
+ type MigrationBundle,
42
+ type MigrationJournalEntry,
43
+ } from "./migrate.js";
44
+
45
+ /** Every table in the Mirror DO store — pass as `drizzle(storage, { schema: mirrorSchema })`. */
46
+ export const mirrorSchema = {
47
+ issues,
48
+ labels,
49
+ users,
50
+ issue_labels,
51
+ relations,
52
+ issue_history,
53
+ projects,
54
+ cycles,
55
+ initiatives,
56
+ project_initiatives,
57
+ comments,
58
+ pull_requests,
59
+ check_runs,
60
+ commit_statuses,
61
+ reviews,
62
+ processed_events,
63
+ change_log,
64
+ sync_cursors,
65
+ } as const;
66
+
67
+ /**
68
+ * The domain-entity tables that flow over the change-feed (the keys are the wire `entity` names). The
69
+ * infra tables — processed_events / change_log / sync_cursors — are deliberately excluded: they never
70
+ * ride a delta.
71
+ */
72
+ export const mirrorEntityTables = {
73
+ issues,
74
+ labels,
75
+ users,
76
+ issue_labels,
77
+ relations,
78
+ issue_history,
79
+ projects,
80
+ cycles,
81
+ initiatives,
82
+ project_initiatives,
83
+ comments,
84
+ pull_requests,
85
+ check_runs,
86
+ commit_statuses,
87
+ reviews,
88
+ } as const;
89
+
90
+ /** The wire `entity` name of a change-feed-carried table (keys of mirrorEntityTables). */
91
+ export type MirrorEntityName = keyof typeof mirrorEntityTables;
92
+
93
+ /** Per-table replication metadata — the PK columns and whether deletes are soft (removed_at) vs hard. */
94
+ export interface TableMeta {
95
+ /** PK column SQL names, in declared order (composite PKs yield more than one). */
96
+ pk: string[];
97
+ /** True iff the table carries a soft-delete `removed_at` column (delete sets it; else hard-delete). */
98
+ softDelete: boolean;
99
+ /** Every SQL column name for this table (getTableConfig-derived, drift-proof). The forward-compat
100
+ * default `knownColumns` in @catalyst-cloud/replicate: a delta row key not in this set is a column
101
+ * the mirror added ahead of this client's bundled schema, and is dropped rather than crashing the
102
+ * apply (#127 server-ahead skew). */
103
+ columns: string[];
104
+ }
105
+
106
+ /** Derive a table's PK columns + soft-delete posture from the Drizzle definition (no hand-maintenance). */
107
+ export function tableMeta(table: SQLiteTable): TableMeta {
108
+ const cfg = getTableConfig(table);
109
+ // A single-column PK (`.primaryKey()`) marks the column `.primary`; a composite PK
110
+ // (`primaryKey({ columns })`) instead lands in cfg.primaryKeys. Cover both.
111
+ const singlePk = cfg.columns.filter((c) => c.primary).map((c) => c.name);
112
+ const compositePk = cfg.primaryKeys.flatMap((pk) => pk.columns.map((c) => c.name));
113
+ const pk = singlePk.length > 0 ? singlePk : compositePk;
114
+ const softDelete = cfg.columns.some((c) => c.name === "removed_at");
115
+ const columns = cfg.columns.map((c) => c.name);
116
+ return { pk, softDelete, columns };
117
+ }
118
+
119
+ /** Schema-derived replication metadata for every change-feed entity, keyed by wire `entity` name. */
120
+ export const MIRROR_TABLE_META: Record<MirrorEntityName, TableMeta> = Object.fromEntries(
121
+ Object.entries(mirrorEntityTables).map(([name, table]) => [name, tableMeta(table)]),
122
+ ) as Record<MirrorEntityName, TableMeta>;
@@ -22,11 +22,43 @@
22
22
  // 3. Do NOT pre-seed a drizzle-kit-NATIVE `__drizzle_migrations` (which stores SHA256 hashes) — this
23
23
  // runner keys on tags, so native-hash rows would be unrecognized and the baseline would re-run
24
24
  // (safe only while the baseline stays IF NOT EXISTS). This runner owns the ledger end to end.
25
+
25
26
  import { MIRROR_MIGRATIONS } from "./migrations.generated.js";
27
+
28
+ /** The minimal native SQLite surface the runner needs — one exec + one row-returning query. */
29
+ export interface MigrationDb {
30
+ /** Execute one or more SQL statements for side effect (no rows returned). */
31
+ exec(sql: string): void;
32
+ /** Run a query and return its rows as plain objects (column name → value). */
33
+ query(sql: string): Array<Record<string, unknown>>;
34
+ }
35
+
36
+ /** A journal entry as emitted by drizzle-kit into meta/_journal.json (and our committed bundle). */
37
+ export interface MigrationJournalEntry {
38
+ idx: number;
39
+ version: string;
40
+ when: number;
41
+ tag: string;
42
+ breakpoints: boolean;
43
+ }
44
+
45
+ /** The committed migration bundle shape: the drizzle journal + tag → SQL string map. */
46
+ export interface MigrationBundle {
47
+ journal: {
48
+ version: string;
49
+ dialect: string;
50
+ entries: readonly MigrationJournalEntry[];
51
+ };
52
+ migrations: Readonly<Record<string, string>>;
53
+ }
54
+
26
55
  /** The breakpoint marker drizzle-kit inserts between statements within one migration file. */
27
56
  const STATEMENT_BREAKPOINT = "--> statement-breakpoint";
28
- const LEDGER_DDL = "CREATE TABLE IF NOT EXISTS __drizzle_migrations (" +
29
- "id INTEGER PRIMARY KEY AUTOINCREMENT, hash TEXT NOT NULL, created_at INTEGER)";
57
+
58
+ const LEDGER_DDL =
59
+ "CREATE TABLE IF NOT EXISTS __drizzle_migrations (" +
60
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, hash TEXT NOT NULL, created_at INTEGER)";
61
+
30
62
  /**
31
63
  * Apply every not-yet-applied migration in `bundle` to `db`, in journal `idx` order. Records each
32
64
  * applied tag in `__drizzle_migrations` (the tag is the `hash` value). Pure, synchronous, and
@@ -36,37 +68,51 @@ const LEDGER_DDL = "CREATE TABLE IF NOT EXISTS __drizzle_migrations (" +
36
68
  * TIME (never at import time), so the module can be imported into a Worker without a clock read.
37
69
  * @returns the tags applied during THIS call (empty if the DB was already up to date).
38
70
  */
39
- export function applyMigrations(db, bundle = MIRROR_MIGRATIONS, now = Date.now()) {
40
- // 1) Ensure the ledger exists.
41
- db.exec(LEDGER_DDL);
42
- // 2) Read already-applied tags (stored in the `hash` column).
43
- const appliedRows = db.query("SELECT hash FROM __drizzle_migrations");
44
- const applied = new Set(appliedRows.map((r) => String(r.hash)).filter((h) => h.length > 0));
45
- // 3) Apply each journal entry not yet recorded, in idx order.
46
- const entries = [...bundle.journal.entries].sort((a, b) => a.idx - b.idx);
47
- const appliedTags = [];
48
- for (const entry of entries) {
49
- if (applied.has(entry.tag))
50
- continue;
51
- const sql = bundle.migrations[entry.tag];
52
- if (sql === undefined) {
53
- throw new Error(`applyMigrations: journal tag "${entry.tag}" has no SQL in the bundle`);
54
- }
55
- // Split on the breakpoint marker and exec each non-empty statement individually. (The DO's
56
- // ctx.storage.sql.exec and bun:sqlite both accept multi-statement strings, but splitting keeps
57
- // the runner portable to surfaces that don't, and matches drizzle-kit's own migrator.)
58
- for (const raw of sql.split(STATEMENT_BREAKPOINT)) {
59
- const stmt = raw.trim();
60
- if (stmt.length === 0)
61
- continue;
62
- execReplaySafe(db, stmt);
63
- }
64
- // Record the tag (as the hash). A parameterless INSERT keeps the adapter surface to exec-only.
65
- db.exec(`INSERT INTO __drizzle_migrations (hash, created_at) VALUES ('${escapeSqlLiteral(entry.tag)}', ${now})`);
66
- appliedTags.push(entry.tag);
71
+ export function applyMigrations(
72
+ db: MigrationDb,
73
+ bundle: MigrationBundle = MIRROR_MIGRATIONS,
74
+ now: number = Date.now(),
75
+ ): { appliedTags: string[] } {
76
+ // 1) Ensure the ledger exists.
77
+ db.exec(LEDGER_DDL);
78
+
79
+ // 2) Read already-applied tags (stored in the `hash` column).
80
+ const appliedRows = db.query("SELECT hash FROM __drizzle_migrations");
81
+ const applied = new Set<string>(
82
+ appliedRows.map((r) => String(r.hash)).filter((h) => h.length > 0),
83
+ );
84
+
85
+ // 3) Apply each journal entry not yet recorded, in idx order.
86
+ const entries = [...bundle.journal.entries].sort((a, b) => a.idx - b.idx);
87
+ const appliedTags: string[] = [];
88
+
89
+ for (const entry of entries) {
90
+ if (applied.has(entry.tag)) continue;
91
+
92
+ const sql = bundle.migrations[entry.tag];
93
+ if (sql === undefined) {
94
+ throw new Error(`applyMigrations: journal tag "${entry.tag}" has no SQL in the bundle`);
67
95
  }
68
- return { appliedTags };
96
+
97
+ // Split on the breakpoint marker and exec each non-empty statement individually. (The DO's
98
+ // ctx.storage.sql.exec and bun:sqlite both accept multi-statement strings, but splitting keeps
99
+ // the runner portable to surfaces that don't, and matches drizzle-kit's own migrator.)
100
+ for (const raw of sql.split(STATEMENT_BREAKPOINT)) {
101
+ const stmt = raw.trim();
102
+ if (stmt.length === 0) continue;
103
+ execReplaySafe(db, stmt);
104
+ }
105
+
106
+ // Record the tag (as the hash). A parameterless INSERT keeps the adapter surface to exec-only.
107
+ db.exec(
108
+ `INSERT INTO __drizzle_migrations (hash, created_at) VALUES ('${escapeSqlLiteral(entry.tag)}', ${now})`,
109
+ );
110
+ appliedTags.push(entry.tag);
111
+ }
112
+
113
+ return { appliedTags };
69
114
  }
115
+
70
116
  /**
71
117
  * Execute one migration statement, tolerating the three errors that make an additive migration
72
118
  * non-replay-safe when the run-once ledger guard is bypassed — e.g. a crash between a statement
@@ -83,22 +129,19 @@ export function applyMigrations(db, bundle = MIRROR_MIGRATIONS, now = Date.now()
83
129
  * tables were created from a newer `schema.sql` likewise adopts these additive tags as no-ops rather
84
130
  * than throwing. Only these idempotent cases are swallowed; any other SQL error still propagates.
85
131
  */
86
- function execReplaySafe(db, stmt) {
87
- try {
88
- db.exec(stmt);
89
- }
90
- catch (err) {
91
- const msg = err instanceof Error ? err.message : String(err);
92
- if (/duplicate column name/i.test(msg) && /\bADD\b/i.test(stmt))
93
- return;
94
- if (/already exists/i.test(msg) && /\bCREATE\s+INDEX\b/i.test(stmt))
95
- return;
96
- if (/already exists/i.test(msg) && /\bCREATE\s+TABLE\b/i.test(stmt))
97
- return;
98
- throw err;
99
- }
132
+ function execReplaySafe(db: MigrationDb, stmt: string): void {
133
+ try {
134
+ db.exec(stmt);
135
+ } catch (err) {
136
+ const msg = err instanceof Error ? err.message : String(err);
137
+ if (/duplicate column name/i.test(msg) && /\bADD\b/i.test(stmt)) return;
138
+ if (/already exists/i.test(msg) && /\bCREATE\s+INDEX\b/i.test(stmt)) return;
139
+ if (/already exists/i.test(msg) && /\bCREATE\s+TABLE\b/i.test(stmt)) return;
140
+ throw err;
141
+ }
100
142
  }
143
+
101
144
  /** Escape a string for use inside a single-quoted SQL literal (double any embedded single quote). */
102
- function escapeSqlLiteral(value) {
103
- return value.replace(/'/g, "''");
145
+ function escapeSqlLiteral(value: string): string {
146
+ return value.replace(/'/g, "''");
104
147
  }
@@ -0,0 +1,99 @@
1
+ // AUTO-GENERATED by scripts/build-bundle.ts — DO NOT EDIT BY HAND.
2
+ //
3
+ // The committed Mirror migration bundle (CTC-41): the drizzle-kit journal + every migration SQL as a
4
+ // string literal (NO `.sql`-as-import), so the SAME module imports cleanly in the DO (Workers), the
5
+ // bun daemon, node tests, and the browser. Regenerate via `bun run --cwd packages/schema build:bundle`
6
+ // (or the root `migration:generate`). The CI drift gate (`migration:check`) fails if this file is
7
+ // stale vs drizzle/.
8
+
9
+ export const MIRROR_MIGRATIONS = {
10
+ journal: {
11
+ version: "7",
12
+ dialect: "sqlite",
13
+ entries: [
14
+ {
15
+ idx: 0,
16
+ version: "6",
17
+ when: 1782051267561,
18
+ tag: "0000_baseline",
19
+ breakpoints: true,
20
+ },
21
+ {
22
+ idx: 1,
23
+ version: "6",
24
+ when: 1782110588498,
25
+ tag: "0001_short_glorian",
26
+ breakpoints: true,
27
+ },
28
+ {
29
+ idx: 2,
30
+ version: "6",
31
+ when: 1782179798312,
32
+ tag: "0002_many_captain_marvel",
33
+ breakpoints: true,
34
+ },
35
+ {
36
+ idx: 3,
37
+ version: "6",
38
+ when: 1782182056315,
39
+ tag: "0003_brainy_silver_samurai",
40
+ breakpoints: true,
41
+ },
42
+ {
43
+ idx: 4,
44
+ version: "6",
45
+ when: 1782183082087,
46
+ tag: "0004_fat_mindworm",
47
+ breakpoints: true,
48
+ },
49
+ {
50
+ idx: 5,
51
+ version: "6",
52
+ when: 1782184123941,
53
+ tag: "0005_blue_black_queen",
54
+ breakpoints: true,
55
+ },
56
+ {
57
+ idx: 6,
58
+ version: "6",
59
+ when: 1782186590417,
60
+ tag: "0006_lively_vector",
61
+ breakpoints: true,
62
+ },
63
+ {
64
+ idx: 7,
65
+ version: "6",
66
+ when: 1782229820186,
67
+ tag: "0007_rapid_logan",
68
+ breakpoints: true,
69
+ },
70
+ {
71
+ idx: 8,
72
+ version: "6",
73
+ when: 1782843988558,
74
+ tag: "0008_optimal_rattler",
75
+ breakpoints: true,
76
+ },
77
+ ],
78
+ },
79
+ migrations: {
80
+ "0000_baseline":
81
+ "CREATE TABLE IF NOT EXISTS `change_log` (\n\t`seq` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n\t`entity` text,\n\t`entity_id` text,\n\t`op` text,\n\t`payload` text,\n\t`ts` integer\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `check_runs` (\n\t`repo_id` text NOT NULL,\n\t`check_run_id` text PRIMARY KEY NOT NULL,\n\t`head_sha` text,\n\t`name` text,\n\t`status` text,\n\t`conclusion` text,\n\t`check_suite_id` text,\n\t`updated_at` integer\n);\n--> statement-breakpoint\nCREATE INDEX IF NOT EXISTS `idx_check_runs_sha` ON `check_runs` (`head_sha`);--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `comments` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`issue_id` text,\n\t`body` text,\n\t`updated_at` integer,\n\t`removed_at` integer\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `commit_statuses` (\n\t`repo_id` text NOT NULL,\n\t`sha` text NOT NULL,\n\t`context` text NOT NULL,\n\t`state` text,\n\t`target_url` text,\n\t`updated_at` integer,\n\tPRIMARY KEY(`repo_id`, `sha`, `context`)\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `cycles` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`number` integer,\n\t`starts_at` integer,\n\t`ends_at` integer,\n\t`updated_at` integer\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `issue_labels` (\n\t`issue_id` text NOT NULL,\n\t`label_id` text NOT NULL,\n\tPRIMARY KEY(`issue_id`, `label_id`)\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `issues` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`identifier` text,\n\t`title` text,\n\t`state` text,\n\t`assignee` text,\n\t`priority` integer,\n\t`estimate` real,\n\t`project_id` text,\n\t`cycle_id` text,\n\t`team_id` text,\n\t`updated_at` integer,\n\t`archived_at` integer,\n\t`removed_at` integer,\n\t`raw` text\n);\n--> statement-breakpoint\nCREATE INDEX IF NOT EXISTS `idx_issues_team` ON `issues` (`team_id`);--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `labels` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`name` text,\n\t`updated_at` integer,\n\t`removed_at` integer\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `processed_events` (\n\t`delivery_id` text PRIMARY KEY NOT NULL,\n\t`source` text,\n\t`event_type` text,\n\t`received_at` integer\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `projects` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`name` text,\n\t`state` text,\n\t`updated_at` integer,\n\t`removed_at` integer\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `pull_requests` (\n\t`repo_id` text NOT NULL,\n\t`number` integer NOT NULL,\n\t`node_id` text,\n\t`state` text,\n\t`draft` integer,\n\t`merged` integer,\n\t`merged_at` integer,\n\t`head_sha` text,\n\t`base_ref` text,\n\t`mergeable` integer,\n\t`mergeable_state` text,\n\t`auto_merge` integer,\n\t`updated_at` integer,\n\t`synced_at` integer,\n\tPRIMARY KEY(`repo_id`, `number`)\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `relations` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`type` text,\n\t`issue_identifier` text,\n\t`related_identifier` text,\n\t`updated_at` integer\n);\n--> statement-breakpoint\nCREATE INDEX IF NOT EXISTS `idx_relations_issue` ON `relations` (`issue_identifier`);--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `reviews` (\n\t`repo_id` text NOT NULL,\n\t`pr_number` integer NOT NULL,\n\t`review_id` text PRIMARY KEY NOT NULL,\n\t`user_id` text,\n\t`state` text,\n\t`submitted_at` integer\n);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS `sync_cursors` (\n\t`source` text NOT NULL,\n\t`entity` text NOT NULL,\n\t`cursor` text,\n\t`last_run_at` integer,\n\tPRIMARY KEY(`source`, `entity`)\n);\n",
82
+ "0001_short_glorian":
83
+ "ALTER TABLE `comments` ADD `author_id` text;--> statement-breakpoint\nALTER TABLE `comments` ADD `author_name` text;--> statement-breakpoint\nALTER TABLE `comments` ADD `author_avatar_url` text;--> statement-breakpoint\nALTER TABLE `comments` ADD `is_bot` integer;--> statement-breakpoint\nALTER TABLE `comments` ADD `parent_id` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `description` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `delegate_id` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `delegate_name` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `bot_actor_name` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `bot_actor_type` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `bot_actor_sub_type` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `parent_id` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `parent_identifier` text;--> statement-breakpoint\nALTER TABLE `labels` ADD `color` text;",
84
+ "0002_many_captain_marvel":
85
+ "CREATE INDEX `idx_comments_issue` ON `comments` (`issue_id`,`removed_at`,`updated_at`);--> statement-breakpoint\nCREATE INDEX `idx_issues_updated` ON `issues` (`removed_at`,`updated_at`);--> statement-breakpoint\nCREATE INDEX `idx_issues_identifier` ON `issues` (`identifier`);--> statement-breakpoint\nCREATE INDEX `idx_pulls_updated` ON `pull_requests` (`updated_at`);",
86
+ "0003_brainy_silver_samurai":
87
+ "ALTER TABLE `pull_requests` ADD `title` text;--> statement-breakpoint\nALTER TABLE `pull_requests` ADD `body` text;--> statement-breakpoint\nALTER TABLE `pull_requests` ADD `author_login` text;--> statement-breakpoint\nALTER TABLE `pull_requests` ADD `author_avatar_url` text;--> statement-breakpoint\nALTER TABLE `pull_requests` ADD `created_at` integer;--> statement-breakpoint\nALTER TABLE `pull_requests` ADD `closed_at` integer;--> statement-breakpoint\nALTER TABLE `pull_requests` ADD `comment_count` integer;--> statement-breakpoint\nALTER TABLE `pull_requests` ADD `milestone_title` text;",
88
+ "0004_fat_mindworm":
89
+ "ALTER TABLE `comments` ADD `created_at` integer;--> statement-breakpoint\nALTER TABLE `issues` ADD `started_at` integer;--> statement-breakpoint\nALTER TABLE `issues` ADD `completed_at` integer;--> statement-breakpoint\nALTER TABLE `issues` ADD `canceled_at` integer;--> statement-breakpoint\nALTER TABLE `issues` ADD `created_at` integer;--> statement-breakpoint\nALTER TABLE `issues` ADD `due_date` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `snoozed_until_at` integer;--> statement-breakpoint\nALTER TABLE `issues` ADD `url` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `priority_label` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `sort_order` real;--> statement-breakpoint\nALTER TABLE `issues` ADD `branch_name` text;--> statement-breakpoint\nCREATE INDEX `idx_issues_sort_order` ON `issues` (`sort_order`);",
90
+ "0005_blue_black_queen":
91
+ "CREATE TABLE `users` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`name` text,\n\t`display_name` text,\n\t`avatar_url` text,\n\t`type` text,\n\t`source` text,\n\t`updated_at` integer\n);\n--> statement-breakpoint\nALTER TABLE `issues` ADD `assignee_id` text;",
92
+ "0006_lively_vector":
93
+ "CREATE TABLE `initiatives` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`name` text,\n\t`description` text,\n\t`status` text,\n\t`target_date` text,\n\t`owner_id` text,\n\t`updated_at` integer\n);\n--> statement-breakpoint\nCREATE TABLE `project_initiatives` (\n\t`project_id` text NOT NULL,\n\t`initiative_id` text NOT NULL,\n\tPRIMARY KEY(`project_id`, `initiative_id`)\n);\n--> statement-breakpoint\nALTER TABLE `cycles` ADD `name` text;--> statement-breakpoint\nALTER TABLE `cycles` ADD `progress` real;--> statement-breakpoint\nALTER TABLE `cycles` ADD `completed_scope` real;--> statement-breakpoint\nALTER TABLE `cycles` ADD `scope` real;--> statement-breakpoint\nALTER TABLE `cycles` ADD `completed_issue_count` integer;--> statement-breakpoint\nALTER TABLE `cycles` ADD `issue_count` integer;--> statement-breakpoint\nALTER TABLE `projects` ADD `description` text;--> statement-breakpoint\nALTER TABLE `projects` ADD `lead_id` text;--> statement-breakpoint\nALTER TABLE `projects` ADD `target_date` text;--> statement-breakpoint\nALTER TABLE `projects` ADD `start_date` text;--> statement-breakpoint\nALTER TABLE `projects` ADD `progress` real;--> statement-breakpoint\nALTER TABLE `projects` ADD `health` text;--> statement-breakpoint\nALTER TABLE `projects` ADD `icon` text;--> statement-breakpoint\nALTER TABLE `projects` ADD `color` text;",
94
+ "0007_rapid_logan":
95
+ "CREATE TABLE `issue_history` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`issue_id` text NOT NULL,\n\t`actor_id` text,\n\t`created_at` integer,\n\t`updated_at` integer,\n\t`from_state` text,\n\t`to_state` text,\n\t`from_assignee_id` text,\n\t`to_assignee_id` text,\n\t`from_priority` integer,\n\t`to_priority` integer,\n\t`from_estimate` real,\n\t`to_estimate` real,\n\t`from_title` text,\n\t`to_title` text,\n\t`from_cycle_id` text,\n\t`to_cycle_id` text,\n\t`from_project_id` text,\n\t`to_project_id` text,\n\t`from_parent_id` text,\n\t`to_parent_id` text,\n\t`from_team_id` text,\n\t`to_team_id` text,\n\t`from_due_date` text,\n\t`to_due_date` text,\n\t`added_label_ids` text,\n\t`removed_label_ids` text,\n\t`updated_description` integer,\n\t`archived` integer,\n\t`auto_archived` integer,\n\t`auto_closed` integer,\n\t`trashed` integer,\n\t`raw` text\n);\n--> statement-breakpoint\nCREATE INDEX `idx_issue_history_issue_created` ON `issue_history` (`issue_id`,`created_at`);",
96
+ "0008_optimal_rattler":
97
+ "ALTER TABLE `issues` ADD `state_id` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `team_key` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `team_name` text;",
98
+ },
99
+ } as const;