@amerged/ohmyhost-mcp 0.1.44 → 0.1.45

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.
@@ -8,16 +8,22 @@ import {
8
8
  DATABASE_SCHEDULE_MIN_EVERY_SECONDS,
9
9
  DATABASE_SCHEDULE_MIN_TIMEOUT_MS,
10
10
  DATABASE_SCHEDULE_STATEMENT_MAX_BYTES,
11
+ DATABASE_WATCH_OPERATIONS,
12
+ isDatabaseWatchSchema,
13
+ isDatabaseWebhookEventPattern,
11
14
  type DatabaseExecutionUsagePage,
12
15
  type DatabaseSchedule,
13
16
  type DatabaseSchedulePage,
14
17
  type DatabaseScheduleDeleted,
15
18
  type DatabaseScheduleRunAccepted,
16
19
  type DatabaseScheduleRunPage,
20
+ type DatabaseWatch,
21
+ type DatabaseWatchDeleted,
22
+ type DatabaseWatchPage,
17
23
  type DatabaseWebhookCreated,
18
24
  type DatabaseWebhookDeleted,
19
25
  type DatabaseWebhookDeliveryPage,
20
- type DatabaseWebhookEventType,
26
+ type DatabaseWebhookEventPattern,
21
27
  type DatabaseWebhookPage,
22
28
  type DatabaseWebhookReplayAccepted,
23
29
  type DatabaseWebhookTestResult,
@@ -323,11 +329,30 @@ export interface LocalMcpProductClient {
323
329
  readonly projectId: string;
324
330
  readonly environment?: DatabaseEnvironment;
325
331
  }): Promise<DatabaseWebhookPage>;
332
+ listDatabaseWatches(input: {
333
+ readonly projectId: string;
334
+ readonly environment?: DatabaseEnvironment;
335
+ }): Promise<DatabaseWatchPage>;
336
+ createDatabaseWatch(input: {
337
+ readonly projectId: string;
338
+ readonly environment: DatabaseEnvironment;
339
+ readonly schema?: string;
340
+ readonly table: string;
341
+ readonly columns: readonly string[];
342
+ readonly operations?: readonly (typeof DATABASE_WATCH_OPERATIONS)[number][];
343
+ readonly idempotencyKey: string;
344
+ }): Promise<DatabaseWatch>;
345
+ deleteDatabaseWatch(input: {
346
+ readonly projectId: string;
347
+ readonly environment: DatabaseEnvironment;
348
+ readonly watchId: string;
349
+ readonly idempotencyKey: string;
350
+ }): Promise<DatabaseWatchDeleted>;
326
351
  createDatabaseWebhook(input: {
327
352
  readonly projectId: string;
328
353
  readonly environment: DatabaseEnvironment;
329
354
  readonly url: string;
330
- readonly eventTypes: readonly DatabaseWebhookEventType[];
355
+ readonly eventTypes: readonly DatabaseWebhookEventPattern[];
331
356
  readonly headers: Readonly<Record<string, string>>;
332
357
  readonly idempotencyKey: string;
333
358
  }): Promise<DatabaseWebhookCreated>;
@@ -539,6 +564,8 @@ interface ProjectDeleteInput {
539
564
  const identifier = z.string().min(1).max(256);
540
565
  const databaseEnvironment = z.enum(["dev", "prod"]);
541
566
  const databaseWebhookId = z.string().regex(/^whk_[a-z0-9]{20}$/u);
567
+ const databaseWatchId = z.string().regex(/^pgcf_watch_[0-9a-f]{8}$/u);
568
+ const databaseIdentifier = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/u);
542
569
  const databaseDeliveryId = z.string().regex(/^dlv_[0-9a-f]{20}$/u);
543
570
  const databaseScheduleName = z.string().regex(/^[a-z][a-z0-9_-]{0,62}$/u);
544
571
  /** Every product tool accepts the saved login to act as; it is an alias, never a credential. */
@@ -1122,6 +1149,75 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
1122
1149
  client.revokeDatabaseAccess({ projectId: input.project_id, accessId: input.access_id }),
1123
1150
  destructiveAnnotations(),
1124
1151
  );
1152
+ registerProductTool(
1153
+ server,
1154
+ dependencies,
1155
+ "database_watch_list",
1156
+ "List this project's database watches of one Dev or Prod environment (dev when omitted). Each watch shows its table, columns, operations and the event type data.<topic> it publishes. Listing wakes a sleeping database.",
1157
+ z.object({ project_id: identifier, environment: databaseEnvironment.optional() }),
1158
+ (client, input) =>
1159
+ client.listDatabaseWatches({
1160
+ projectId: input.project_id,
1161
+ ...(input.environment === undefined ? {} : { environment: input.environment }),
1162
+ }),
1163
+ );
1164
+ registerProductTool(
1165
+ server,
1166
+ dependencies,
1167
+ "database_watch_create",
1168
+ "Publish row changes of one table of a Dev or Prod database as data events: only the listed columns (at most 32) of each insert, update or delete are published, with the event type data.<topic> returned by this call. Pass that event type to database_webhook_create. The table must exist as an ordinary table of the application, created by a deployed migration; a sleeping database wakes. Rows written before the watch exists are not published, and a published column must not be dropped while the watch exists. Requires confirmed: true and an idempotency key. Refused with project_budget_exceeded while the Stop budget is reached.",
1169
+ z.object({
1170
+ project_id: identifier,
1171
+ environment: databaseEnvironment,
1172
+ schema: databaseIdentifier.refine(isDatabaseWatchSchema).optional(),
1173
+ table: databaseIdentifier,
1174
+ columns: z
1175
+ .array(databaseIdentifier)
1176
+ .min(1)
1177
+ .max(32)
1178
+ .refine((columns) => new Set(columns).size === columns.length),
1179
+ operations: z
1180
+ .array(z.enum(DATABASE_WATCH_OPERATIONS))
1181
+ .min(1)
1182
+ .max(3)
1183
+ .refine((operations) => new Set(operations).size === operations.length)
1184
+ .optional(),
1185
+ idempotency_key: idempotencyKey,
1186
+ confirmed: z.literal(true),
1187
+ }),
1188
+ (client, input) =>
1189
+ client.createDatabaseWatch({
1190
+ projectId: input.project_id,
1191
+ environment: input.environment,
1192
+ ...(input.schema === undefined ? {} : { schema: input.schema }),
1193
+ table: input.table,
1194
+ columns: input.columns,
1195
+ ...(input.operations === undefined ? {} : { operations: input.operations }),
1196
+ idempotencyKey: input.idempotency_key,
1197
+ }),
1198
+ mutationAnnotations(),
1199
+ );
1200
+ registerProductTool(
1201
+ server,
1202
+ dependencies,
1203
+ "database_watch_delete",
1204
+ "Remove the capture trigger of one database watch: its table publishes no more data events. Destructive: requires confirmed: true and an idempotency key. Deleting an already deleted watch answers not found.",
1205
+ z.object({
1206
+ project_id: identifier,
1207
+ environment: databaseEnvironment,
1208
+ watch_id: databaseWatchId,
1209
+ idempotency_key: idempotencyKey,
1210
+ confirmed: z.literal(true),
1211
+ }),
1212
+ (client, input) =>
1213
+ client.deleteDatabaseWatch({
1214
+ projectId: input.project_id,
1215
+ environment: input.environment,
1216
+ watchId: input.watch_id,
1217
+ idempotencyKey: input.idempotency_key,
1218
+ }),
1219
+ destructiveAnnotations(),
1220
+ );
1125
1221
  registerProductTool(
1126
1222
  server,
1127
1223
  dependencies,
@@ -1138,7 +1234,7 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
1138
1234
  server,
1139
1235
  dependencies,
1140
1236
  "database_webhook_create",
1141
- "Subscribe one HTTPS receiver to database lifecycle events (database.created, ready, woke, slept, suspended, resized, deleted) of one Dev or Prod database. The signing secret is RETURNED EXACTLY ONCE: store it in a secret manager and never write it into files, notes, source, commit messages or later messages. Deliveries are signed, retried and dead-lettered when retries stop. At most five webhooks exist per project environment. Requires confirmed: true and an idempotency key; the same key with the same request returns the stored answer.",
1237
+ "Subscribe one HTTPS receiver to database lifecycle events (database.created, ready, woke, slept, suspended, resized, deleted) of one Dev or Prod database, or to data events: data.<topic> for the rows of one watched table (the event type database_watch_create returns) or data.* for every watched table; a bare * never matches data events. The signing secret is RETURNED EXACTLY ONCE: store it in a secret manager and never write it into files, notes, source, commit messages or later messages. Deliveries are signed, retried and dead-lettered when retries stop. At most five webhooks exist per project environment. Requires confirmed: true and an idempotency key; the same key with the same request returns the stored answer.",
1142
1238
  z.object({
1143
1239
  project_id: identifier,
1144
1240
  environment: databaseEnvironment,
@@ -1146,7 +1242,18 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
1146
1242
  .string()
1147
1243
  .max(2048)
1148
1244
  .regex(/^https:\/\//u),
1149
- event_types: z.array(z.enum(DATABASE_WEBHOOK_EVENT_TYPES)).min(1).max(16),
1245
+ event_types: z
1246
+ .array(
1247
+ z.union([
1248
+ z.enum(DATABASE_WEBHOOK_EVENT_TYPES),
1249
+ z
1250
+ .string()
1251
+ .regex(/^data\.(?:\*|[a-z][a-z0-9_]{0,47})$/u)
1252
+ .refine(isDatabaseWebhookEventPattern),
1253
+ ]),
1254
+ )
1255
+ .min(1)
1256
+ .max(16),
1150
1257
  headers: z.record(z.string(), z.string()).optional(),
1151
1258
  idempotency_key: idempotencyKey,
1152
1259
  confirmed: z.literal(true),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohmyhost/product-cli",
3
- "version": "0.1.44",
3
+ "version": "0.1.45",
4
4
  "private": true,
5
5
  "ohmyhost": {
6
6
  "deployment": "production",
@@ -419,8 +419,14 @@ const HELP_USAGE = Object.freeze({
419
419
  "ohmyhost database access list --project ULID [--environment dev|prod] --json",
420
420
  "database access revoke":
421
421
  "ohmyhost database access revoke --project ULID --access ULID --yes --json",
422
+ "database watch create":
423
+ "ohmyhost database watch create --project ULID --environment dev|prod --table NAME [--schema public] --columns COL[,COL] [--operations insert,update,delete] --idempotency-key KEY --yes --json (publishes only the listed columns, at most 32, of each row change as the event type data.<topic> printed in this answer; the table must exist as an ordinary table of the application; a sleeping database wakes; next, subscribe with 'ohmyhost database webhook create ... --event-types data.<topic>')",
424
+ "database watch list":
425
+ "ohmyhost database watch list --project ULID [--environment dev|prod] --json (dev when omitted; a sleeping database wakes)",
426
+ "database watch delete":
427
+ "ohmyhost database watch delete --project ULID --environment dev|prod --watch ID --idempotency-key KEY --yes --json",
422
428
  "database webhook create":
423
- "ohmyhost database webhook create --project ULID --environment dev|prod --url HTTPS_URL --event-types TYPE[,TYPE] [--headers-file PATH] --idempotency-key KEY --yes --json (the signing secret is shown once in this answer; types: database.created, database.ready, database.woke, database.slept, database.suspended, database.resized, database.deleted; the headers file is a UTF-8 JSON object of at most 8 KiB)",
429
+ "ohmyhost database webhook create --project ULID --environment dev|prod --url HTTPS_URL --event-types TYPE[,TYPE] [--headers-file PATH] --idempotency-key KEY --yes --json (the signing secret is shown once in this answer; types: database.created, database.ready, database.woke, database.slept, database.suspended, database.resized, database.deleted, data.<topic> for the rows of one watched table printed by database watch create, or data.* for every watched table; a bare * is not accepted and does not include data events; the headers file is a UTF-8 JSON object of at most 8 KiB)",
424
430
  "database webhook list":
425
431
  "ohmyhost database webhook list --project ULID [--environment dev|prod] --json (dev when omitted; the secret is never listed)",
426
432
  "database webhook get":
@@ -1191,6 +1197,12 @@ export class ProductCli {
1191
1197
  return await this.listDatabaseAccess(command, signal);
1192
1198
  if (command.kind === "database-access-revoke")
1193
1199
  return await this.revokeDatabaseAccess(command, signal);
1200
+ if (command.kind === "database-watch-create")
1201
+ return await this.createDatabaseWatch(command, signal);
1202
+ if (command.kind === "database-watch-list")
1203
+ return await this.listDatabaseWatches(command, signal);
1204
+ if (command.kind === "database-watch-delete")
1205
+ return await this.deleteDatabaseWatch(command, signal);
1194
1206
  if (command.kind === "database-webhook-create")
1195
1207
  return await this.createDatabaseWebhook(command, signal);
1196
1208
  if (command.kind === "database-webhook-list")
@@ -1952,6 +1964,51 @@ export class ProductCli {
1952
1964
  return success("database access revoke", { status: "succeeded", access });
1953
1965
  }
1954
1966
 
1967
+ /** The answer names the event type to subscribe to; the watch already carries the columns and the topic. */
1968
+ private async createDatabaseWatch(
1969
+ command: Extract<ProductCliCommand, { kind: "database-watch-create" }>,
1970
+ signal: AbortSignal,
1971
+ ): Promise<CliResult> {
1972
+ const api = await this.authenticatedApi(command.credentialStore, signal);
1973
+ const watch = await api.createProjectDatabaseWatch({
1974
+ projectId: command.projectId,
1975
+ environment: command.environment,
1976
+ idempotencyKey: command.idempotencyKey,
1977
+ request: command.request,
1978
+ });
1979
+ return success("database watch create", {
1980
+ status: "succeeded",
1981
+ watch,
1982
+ next_action: `Run '${this.dependencies.commandPrefix} database webhook create --project ${command.projectId} --environment ${command.environment} --url HTTPS_URL --event-types ${watch.event_type} --idempotency-key KEY --yes --json' to receive ${watch.event_type} events at an HTTPS receiver.`,
1983
+ });
1984
+ }
1985
+
1986
+ private async listDatabaseWatches(
1987
+ command: Extract<ProductCliCommand, { kind: "database-watch-list" }>,
1988
+ signal: AbortSignal,
1989
+ ): Promise<CliResult> {
1990
+ const api = await this.authenticatedApi(command.credentialStore, signal);
1991
+ const page = await api.listProjectDatabaseWatches({
1992
+ projectId: command.projectId,
1993
+ environment: command.environment,
1994
+ });
1995
+ return success("database watch list", { status: "succeeded", watch: page.items });
1996
+ }
1997
+
1998
+ private async deleteDatabaseWatch(
1999
+ command: Extract<ProductCliCommand, { kind: "database-watch-delete" }>,
2000
+ signal: AbortSignal,
2001
+ ): Promise<CliResult> {
2002
+ const api = await this.authenticatedApi(command.credentialStore, signal);
2003
+ const deleted = await api.deleteProjectDatabaseWatch({
2004
+ projectId: command.projectId,
2005
+ environment: command.environment,
2006
+ idempotencyKey: command.idempotencyKey,
2007
+ watchId: command.watchId,
2008
+ });
2009
+ return success("database watch delete", { status: "succeeded", deleted });
2010
+ }
2011
+
1955
2012
  /** The signing secret is returned by create only; every other answer carries no secret. */
1956
2013
  private async createDatabaseWebhook(
1957
2014
  command: Extract<ProductCliCommand, { kind: "database-webhook-create" }>,
@@ -3526,6 +3583,9 @@ const parsedCommandName = (command: ProductCliCommand): CommandLabel => {
3526
3583
  if (command.kind === "database-access-list") return "database access list";
3527
3584
  if (command.kind === "database-access-revoke") return "database access revoke";
3528
3585
  if (command.kind === "database-psql") return "database psql";
3586
+ if (command.kind === "database-watch-create") return "database watch create";
3587
+ if (command.kind === "database-watch-list") return "database watch list";
3588
+ if (command.kind === "database-watch-delete") return "database watch delete";
3529
3589
  if (command.kind === "database-webhook-create") return "database webhook create";
3530
3590
  if (command.kind === "database-webhook-list") return "database webhook list";
3531
3591
  if (command.kind === "database-webhook-get") return "database webhook get";
@@ -6,9 +6,12 @@ import {
6
6
  parseDatabaseListLimit,
7
7
  parseDatabaseScheduleName,
8
8
  parseDatabaseUsageWindow,
9
+ parseDatabaseWatchCreateRequest,
10
+ parseDatabaseWatchId,
9
11
  parseDatabaseWebhookCreateRequest,
10
12
  parseDatabaseWebhookId,
11
- type DatabaseWebhookEventType,
13
+ type DatabaseWatchRequest,
14
+ type DatabaseWebhookEventPattern,
12
15
  } from "@ohmyhost/contracts/database-events";
13
16
  import {
14
17
  databaseSizeForMemory,
@@ -404,12 +407,36 @@ export type ProductCliCommand =
404
407
  readonly yes: true;
405
408
  readonly credentialStore: "native";
406
409
  }
410
+ | {
411
+ readonly kind: "database-watch-create";
412
+ readonly projectId: string;
413
+ readonly environment: "dev" | "prod";
414
+ readonly request: DatabaseWatchRequest;
415
+ readonly idempotencyKey: string;
416
+ readonly yes: true;
417
+ readonly credentialStore: "native";
418
+ }
419
+ | {
420
+ readonly kind: "database-watch-list";
421
+ readonly projectId: string;
422
+ readonly environment: "dev" | "prod";
423
+ readonly credentialStore: "native";
424
+ }
425
+ | {
426
+ readonly kind: "database-watch-delete";
427
+ readonly projectId: string;
428
+ readonly environment: "dev" | "prod";
429
+ readonly watchId: string;
430
+ readonly idempotencyKey: string;
431
+ readonly yes: true;
432
+ readonly credentialStore: "native";
433
+ }
407
434
  | {
408
435
  readonly kind: "database-webhook-create";
409
436
  readonly projectId: string;
410
437
  readonly environment: "dev" | "prod";
411
438
  readonly url: string;
412
- readonly eventTypes: readonly DatabaseWebhookEventType[];
439
+ readonly eventTypes: readonly DatabaseWebhookEventPattern[];
413
440
  readonly headersFile: string | null;
414
441
  readonly idempotencyKey: string;
415
442
  readonly yes: true;
@@ -1003,6 +1030,9 @@ export type RecognizedCommandLabel =
1003
1030
  | "database access create"
1004
1031
  | "database access list"
1005
1032
  | "database access revoke"
1033
+ | "database watch create"
1034
+ | "database watch list"
1035
+ | "database watch delete"
1006
1036
  | "database webhook create"
1007
1037
  | "database webhook list"
1008
1038
  | "database webhook get"
@@ -1174,6 +1204,11 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
1174
1204
  if (values[1] === "list") return "database access list";
1175
1205
  if (values[1] === "revoke") return "database access revoke";
1176
1206
  }
1207
+ if (value === "database" && values[0] === "watch") {
1208
+ if (values[1] === "create") return "database watch create";
1209
+ if (values[1] === "list") return "database watch list";
1210
+ if (values[1] === "delete") return "database watch delete";
1211
+ }
1177
1212
  if (value === "database" && values[0] === "webhook") {
1178
1213
  if (values[1] === "create") return "database webhook create";
1179
1214
  if (values[1] === "list") return "database webhook list";
@@ -2063,6 +2098,92 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
2063
2098
  }
2064
2099
  throw new InvalidCommandError();
2065
2100
  }
2101
+ if (first === "database" && values[0] === "watch") {
2102
+ values.shift();
2103
+ const subcommand = values.shift();
2104
+ if (subcommand === "create") {
2105
+ if (!yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
2106
+ const options = parseOptionalOptions(values, [
2107
+ "--project",
2108
+ "--environment",
2109
+ "--table",
2110
+ "--schema",
2111
+ "--columns",
2112
+ "--operations",
2113
+ "--idempotency-key",
2114
+ ]);
2115
+ const projectId = options["--project"],
2116
+ environment = options["--environment"],
2117
+ table = options["--table"],
2118
+ schema = options["--schema"],
2119
+ columns = options["--columns"],
2120
+ operations = options["--operations"],
2121
+ idempotencyKey = options["--idempotency-key"];
2122
+ if (
2123
+ !isValue(projectId) ||
2124
+ !isValue(idempotencyKey) ||
2125
+ !isValue(table) ||
2126
+ !isValue(columns) ||
2127
+ (environment !== "dev" && environment !== "prod")
2128
+ )
2129
+ throw new InvalidCommandError();
2130
+ const request = contractValue(() =>
2131
+ parseDatabaseWatchCreateRequest({
2132
+ schema,
2133
+ table,
2134
+ columns: columns.split(","),
2135
+ ...(operations === undefined ? {} : { operations: operations.split(",") }),
2136
+ }),
2137
+ );
2138
+ return {
2139
+ kind: "database-watch-create",
2140
+ projectId,
2141
+ environment,
2142
+ request,
2143
+ idempotencyKey,
2144
+ yes: true,
2145
+ credentialStore,
2146
+ };
2147
+ }
2148
+ if (subcommand === "list") {
2149
+ if (yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
2150
+ const options = parseOptionalOptions(values, ["--project", "--environment"]);
2151
+ const projectId = options["--project"],
2152
+ environment = options["--environment"] ?? "dev";
2153
+ if (!isValue(projectId) || (environment !== "dev" && environment !== "prod"))
2154
+ throw new InvalidCommandError();
2155
+ return { kind: "database-watch-list", projectId, environment, credentialStore };
2156
+ }
2157
+ if (subcommand === "delete") {
2158
+ if (!yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
2159
+ const options = parseOptionalOptions(values, [
2160
+ "--project",
2161
+ "--environment",
2162
+ "--watch",
2163
+ "--idempotency-key",
2164
+ ]);
2165
+ const projectId = options["--project"],
2166
+ environment = options["--environment"],
2167
+ watchId = options["--watch"],
2168
+ idempotencyKey = options["--idempotency-key"];
2169
+ if (
2170
+ !isValue(projectId) ||
2171
+ !isValue(idempotencyKey) ||
2172
+ (environment !== "dev" && environment !== "prod")
2173
+ )
2174
+ throw new InvalidCommandError();
2175
+ return {
2176
+ kind: "database-watch-delete",
2177
+ projectId,
2178
+ environment,
2179
+ watchId: contractValue(() => parseDatabaseWatchId(watchId)),
2180
+ idempotencyKey,
2181
+ yes: true,
2182
+ credentialStore,
2183
+ };
2184
+ }
2185
+ throw new InvalidCommandError();
2186
+ }
2066
2187
  if (first === "database" && values[0] === "webhook") {
2067
2188
  values.shift();
2068
2189
  const subcommand = values.shift();
@@ -33,6 +33,9 @@ import {
33
33
  assertDatabaseWebhookDeliveryPage,
34
34
  assertDatabaseWebhookPage,
35
35
  assertDatabaseWebhookReplayAccepted,
36
+ assertDatabaseWatch,
37
+ assertDatabaseWatchDeleted,
38
+ assertDatabaseWatchPage,
36
39
  assertDatabaseWebhookTest,
37
40
  type DatabaseExecutionUsagePage,
38
41
  type DatabaseSchedule,
@@ -45,7 +48,11 @@ import {
45
48
  type DatabaseWebhookCreated,
46
49
  type DatabaseWebhookDeleted,
47
50
  type DatabaseWebhookDeliveryPage,
48
- type DatabaseWebhookEventType,
51
+ type DatabaseWatch,
52
+ type DatabaseWatchDeleted,
53
+ type DatabaseWatchPage,
54
+ type DatabaseWatchRequest,
55
+ type DatabaseWebhookEventPattern,
49
56
  type DatabaseWebhookPage,
50
57
  type DatabaseWebhookReplayAccepted,
51
58
  type DatabaseWebhookTestResult,
@@ -152,6 +159,9 @@ import {
152
159
  getProjectDatabaseWebhook,
153
160
  deleteProjectDatabaseWebhook,
154
161
  testProjectDatabaseWebhook,
162
+ createProjectDatabaseWatch,
163
+ listProjectDatabaseWatches,
164
+ deleteProjectDatabaseWatch,
155
165
  listProjectDatabaseWebhookDeliveries,
156
166
  replayProjectDatabaseWebhookDelivery,
157
167
  setProjectDatabaseSchedule,
@@ -328,7 +338,7 @@ export interface ProductApi {
328
338
  readonly environment: "dev" | "prod";
329
339
  readonly idempotencyKey: string;
330
340
  readonly url: string;
331
- readonly eventTypes: readonly DatabaseWebhookEventType[];
341
+ readonly eventTypes: readonly DatabaseWebhookEventPattern[];
332
342
  readonly headers: Readonly<Record<string, string>>;
333
343
  }): Promise<DatabaseWebhookCreated>;
334
344
  listProjectDatabaseWebhooks(input: {
@@ -350,6 +360,22 @@ export interface ProductApi {
350
360
  readonly environment: "dev" | "prod";
351
361
  readonly webhookId: string;
352
362
  }): Promise<DatabaseWebhookTestResult>;
363
+ createProjectDatabaseWatch(input: {
364
+ readonly projectId: string;
365
+ readonly environment: "dev" | "prod";
366
+ readonly idempotencyKey: string;
367
+ readonly request: DatabaseWatchRequest;
368
+ }): Promise<DatabaseWatch>;
369
+ listProjectDatabaseWatches(input: {
370
+ readonly projectId: string;
371
+ readonly environment: "dev" | "prod";
372
+ }): Promise<DatabaseWatchPage>;
373
+ deleteProjectDatabaseWatch(input: {
374
+ readonly projectId: string;
375
+ readonly environment: "dev" | "prod";
376
+ readonly idempotencyKey: string;
377
+ readonly watchId: string;
378
+ }): Promise<DatabaseWatchDeleted>;
353
379
  listProjectDatabaseWebhookDeliveries(input: {
354
380
  readonly projectId: string;
355
381
  readonly environment: "dev" | "prod";
@@ -566,6 +592,7 @@ export interface ProductApi {
566
592
 
567
593
  /** A webhook answer that names another environment, webhook or delivery than the request did. */
568
594
  const WEBHOOK_SCOPE_MISMATCH = "Webhook response scope mismatch";
595
+ const WATCH_SCOPE_MISMATCH = "Watch response scope mismatch";
569
596
  /** A schedule answer that names another environment or schedule than the request did. */
570
597
  const SCHEDULE_SCOPE_MISMATCH = "Schedule response scope mismatch";
571
598
 
@@ -889,6 +916,63 @@ export class GeneratedSdkProductApi implements ProductApi {
889
916
  return value;
890
917
  }
891
918
 
919
+ public async createProjectDatabaseWatch(
920
+ input: Parameters<ProductApi["createProjectDatabaseWatch"]>[0],
921
+ ): Promise<DatabaseWatch> {
922
+ const value = await createProjectDatabaseWatch(
923
+ {
924
+ project_id: input.projectId,
925
+ "Idempotency-Key": input.idempotencyKey,
926
+ databaseWatchCreateRequest: {
927
+ environment: input.environment,
928
+ schema: input.request.schema,
929
+ table: input.request.table,
930
+ columns: [...input.request.columns],
931
+ operations: [...input.request.operations],
932
+ },
933
+ },
934
+ { client: this.#client },
935
+ );
936
+ assertDatabaseWatch(value);
937
+ if (
938
+ value.environment !== input.environment ||
939
+ value.schema !== input.request.schema ||
940
+ value.table !== input.request.table
941
+ )
942
+ throw new TypeError(WATCH_SCOPE_MISMATCH);
943
+ return value;
944
+ }
945
+
946
+ public async listProjectDatabaseWatches(
947
+ input: Parameters<ProductApi["listProjectDatabaseWatches"]>[0],
948
+ ): Promise<DatabaseWatchPage> {
949
+ const value = await listProjectDatabaseWatches(
950
+ { project_id: input.projectId, environment: input.environment },
951
+ { client: this.#client },
952
+ );
953
+ assertDatabaseWatchPage(value);
954
+ if (value.items.some((item) => item.environment !== input.environment))
955
+ throw new TypeError(WATCH_SCOPE_MISMATCH);
956
+ return value;
957
+ }
958
+
959
+ public async deleteProjectDatabaseWatch(
960
+ input: Parameters<ProductApi["deleteProjectDatabaseWatch"]>[0],
961
+ ): Promise<DatabaseWatchDeleted> {
962
+ const value = await deleteProjectDatabaseWatch(
963
+ {
964
+ project_id: input.projectId,
965
+ environment: input.environment,
966
+ "Idempotency-Key": input.idempotencyKey,
967
+ watch_id: input.watchId,
968
+ },
969
+ { client: this.#client },
970
+ );
971
+ assertDatabaseWatchDeleted(value);
972
+ if (value.id !== input.watchId) throw new TypeError(WATCH_SCOPE_MISMATCH);
973
+ return value;
974
+ }
975
+
892
976
  /** The signing secret exists only in this answer; the CLI prints it once and stores nothing. */
893
977
  public async createProjectDatabaseWebhook(
894
978
  input: Parameters<ProductApi["createProjectDatabaseWebhook"]>[0],