@catalyst-cloud/schema 0.1.13 → 0.1.14

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.13",
3
+ "version": "0.1.14",
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",
package/src/index.ts CHANGED
@@ -41,6 +41,7 @@ import {
41
41
  pr_commits,
42
42
  pr_events,
43
43
  pr_ancillary_backfill,
44
+ push_subscriptions,
44
45
  } from "./mirror.js";
45
46
 
46
47
  export * from "./mirror.js";
@@ -112,6 +113,9 @@ export const mirrorSchema = {
112
113
  pr_commits,
113
114
  pr_events,
114
115
  pr_ancillary_backfill,
116
+ // CTC-619: a member's Web Push subscription. Rides the change feed like any other entity — a
117
+ // member-facing settings UI can reflect subscribe/unsubscribe reactively.
118
+ push_subscriptions,
115
119
  } as const;
116
120
 
117
121
  /**
@@ -157,6 +161,8 @@ export const mirrorEntityTables = {
157
161
  pr_conversation_comments,
158
162
  pr_commits,
159
163
  pr_events,
164
+ // CTC-619: a member's Web Push subscription.
165
+ push_subscriptions,
160
166
  } as const;
161
167
 
162
168
  /** The wire `entity` name of a change-feed-carried table (keys of mirrorEntityTables). */
@@ -172,6 +172,13 @@ export const MIRROR_MIGRATIONS = {
172
172
  tag: "0022_chilly_gorgon",
173
173
  breakpoints: true,
174
174
  },
175
+ {
176
+ idx: 23,
177
+ version: "6",
178
+ when: 1786935258669,
179
+ tag: "0023_ambiguous_micromax",
180
+ breakpoints: true,
181
+ },
175
182
  ],
176
183
  },
177
184
  migrations: {
@@ -219,5 +226,7 @@ export const MIRROR_MIGRATIONS = {
219
226
  "CREATE TABLE `pr_ancillary_backfill` (\n\t`repo_id` text NOT NULL,\n\t`pr_number` integer NOT NULL,\n\t`backfilled_at` integer NOT NULL,\n\tPRIMARY KEY(`repo_id`, `pr_number`)\n);\n--> statement-breakpoint\nCREATE TABLE `pr_commits` (\n\t`repo_id` text NOT NULL,\n\t`pr_number` integer NOT NULL,\n\t`sha` text NOT NULL,\n\t`message` text,\n\t`author_login` text,\n\t`author_name` text,\n\t`author_email` text,\n\t`authored_at` integer,\n\t`committed_at` integer,\n\tPRIMARY KEY(`repo_id`, `pr_number`, `sha`)\n);\n--> statement-breakpoint\nCREATE TABLE `pr_conversation_comments` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`repo_id` text NOT NULL,\n\t`pr_number` integer NOT NULL,\n\t`author_id` text,\n\t`body` text,\n\t`created_at` integer,\n\t`updated_at` integer,\n\t`removed_at` integer\n);\n--> statement-breakpoint\nCREATE INDEX `idx_pr_conversation_comments_pr` ON `pr_conversation_comments` (`repo_id`,`pr_number`);--> statement-breakpoint\nCREATE TABLE `pr_events` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`repo_id` text NOT NULL,\n\t`pr_number` integer NOT NULL,\n\t`action` text,\n\t`actor_id` text,\n\t`created_at` integer,\n\t`updated_at` integer,\n\t`raw` text\n);\n--> statement-breakpoint\nCREATE INDEX `idx_pr_events_pr` ON `pr_events` (`repo_id`,`pr_number`,`created_at`);--> statement-breakpoint\nCREATE TABLE `pr_review_comments` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`repo_id` text NOT NULL,\n\t`pr_number` integer NOT NULL,\n\t`review_id` text,\n\t`commit_id` text,\n\t`path` text,\n\t`line` integer,\n\t`diff_hunk` text,\n\t`in_reply_to_id` text,\n\t`author_id` text,\n\t`body` text,\n\t`created_at` integer,\n\t`updated_at` integer,\n\t`removed_at` integer\n);\n--> statement-breakpoint\nCREATE INDEX `idx_pr_review_comments_pr` ON `pr_review_comments` (`repo_id`,`pr_number`);",
220
227
  "0022_chilly_gorgon":
221
228
  "CREATE TABLE `workflow_states` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`team_id` text,\n\t`name` text,\n\t`type` text,\n\t`position` real,\n\t`color` text,\n\t`archived_at` integer,\n\t`updated_at` integer\n);\n--> statement-breakpoint\nCREATE INDEX `idx_workflow_states_team` ON `workflow_states` (`team_id`);--> statement-breakpoint\nALTER TABLE `issues` ADD `state_type` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `state_position` real;",
229
+ "0023_ambiguous_micromax":
230
+ "CREATE TABLE `push_subscriptions` (\n\t`endpoint` text PRIMARY KEY NOT NULL,\n\t`member_id` text NOT NULL,\n\t`p256dh` text NOT NULL,\n\t`auth` text NOT NULL,\n\t`created_at` integer,\n\t`last_used_at` integer\n);\n--> statement-breakpoint\nCREATE INDEX `idx_push_subscriptions_member` ON `push_subscriptions` (`member_id`);",
222
231
  },
223
232
  } as const;
package/src/mirror.ts CHANGED
@@ -851,3 +851,25 @@ export const pr_ancillary_backfill = sqliteTable(
851
851
  },
852
852
  (t) => [primaryKey({ columns: [t.repo_id, t.pr_number] })],
853
853
  );
854
+
855
+ // CTC-619 — a member's Web Push subscription (one browser/device). PK is the subscription `endpoint`
856
+ // itself — a push endpoint URL is unique per browser install by construction, so keying on it is what
857
+ // makes "a duplicate subscribe for the same endpoint does not create a second row" (the ticket's own
858
+ // AC) a plain upsert, no synthetic id needed. `member_id` is the Linear user id (FK-shaped to
859
+ // users.id, same as issues.assignee_id) — the ingest trigger's own correlation key. HARD delete on
860
+ // unsubscribe (no removed_at): a stale/revoked push subscription has no historical value, matching
861
+ // `users`' own "HARD-UPSERT, no soft delete" precedent for similarly overwrite-in-place data.
862
+ export const push_subscriptions = sqliteTable(
863
+ "push_subscriptions",
864
+ {
865
+ endpoint: text("endpoint").primaryKey(),
866
+ member_id: text("member_id").notNull(),
867
+ p256dh: text("p256dh").notNull(),
868
+ auth: text("auth").notNull(),
869
+ created_at: integer("created_at"),
870
+ last_used_at: integer("last_used_at"),
871
+ },
872
+ // The ingest trigger's own read pattern: "every subscription for this member" (member_id, NOT the
873
+ // PK), keyed off issues.assignee_id — the same shape idx_workflow_states_team indexes on team_id.
874
+ (t) => [index("idx_push_subscriptions_member").on(t.member_id)],
875
+ );