web-comments 0.1.0 → 1.0.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.
@@ -0,0 +1,21 @@
1
+ import type { Connector } from "../../server";
2
+ export interface McpOptions {
3
+ /** What the server calls itself, to MCP clients and on the approval page. Default "web-comments". */
4
+ name?: string;
5
+ /** How long an access token lasts, in seconds. Default an hour. */
6
+ accessTokenTtl?: number;
7
+ /** How long a refresh token lasts unused, in seconds. Default 30 days. */
8
+ refreshTokenTtl?: number;
9
+ }
10
+ /**
11
+ * An MCP server for comments, at `{basePath}/mcp`: agents list threads, read
12
+ * one with its screenshots and the element it's on, reply, and resolve or
13
+ * reopen it. MCP clients sign in with OAuth: the person approving one is
14
+ * whoever the API already recognises in their browser, and the agent then
15
+ * acts as them. Requests the auth plugins recognise directly (a
16
+ * `bearerToken`, an Access service token) are let in too.
17
+ *
18
+ * connectors: [mcp()]
19
+ * claude mcp add --transport http comments https://app.example.com/__comments/mcp
20
+ */
21
+ export declare function mcp<Env = unknown>(options?: McpOptions): Connector<Env>;
@@ -0,0 +1,50 @@
1
+ import type { ConnectorRequestContext, ConnectorStorage, Identity } from "../../server";
2
+ export declare const SCOPE = "comments";
3
+ export interface OAuthOptions {
4
+ /** What people approving a client are told they're signing in to. */
5
+ name: string;
6
+ accessTokenTtl: number;
7
+ refreshTokenTtl: number;
8
+ }
9
+ /** What a token stands for. */
10
+ export interface Grant {
11
+ identity: Identity;
12
+ clientId: string;
13
+ clientName: string;
14
+ site: string;
15
+ resource: string;
16
+ scope: string;
17
+ }
18
+ /** Where everything is, for one request's origin. */
19
+ export declare function endpoints(base: string): {
20
+ resource: string;
21
+ issuer: string;
22
+ authorize: string;
23
+ token: string;
24
+ register: string;
25
+ revoke: string;
26
+ jwks: string;
27
+ resourceMetadata: string;
28
+ };
29
+ export declare const CORS: {
30
+ "access-control-allow-origin": string;
31
+ "access-control-allow-methods": string;
32
+ "access-control-allow-headers": string;
33
+ "access-control-expose-headers": string;
34
+ "access-control-max-age": string;
35
+ };
36
+ /** RFC 9728: which authorization server guards the MCP endpoint. */
37
+ export declare function protectedResourceMetadata(base: string): Response;
38
+ /**
39
+ * RFC 8414, also served as OpenID discovery, which is the one place under
40
+ * the API's own path a client looks. This server issues no ID tokens; the
41
+ * JWKS and ID token fields are there because OIDC discovery parsers require
42
+ * them.
43
+ */
44
+ export declare function authorizationServerMetadata(base: string, oidc: boolean): Response;
45
+ export declare function jwks(): Response;
46
+ /** The grant behind an access token, or null if it isn't a live one. */
47
+ export declare function grantOf(storage: ConnectorStorage, token: string): Promise<Grant | null>;
48
+ export declare function looksLikeOurToken(token: string): boolean;
49
+ /** Answers the OAuth paths under the connector's base; null for anything else. */
50
+ export declare function handleOAuth(request: Request, path: string, context: ConnectorRequestContext, options: OAuthOptions): Promise<Response | null>;
@@ -0,0 +1,14 @@
1
+ import type { Identity } from "../../server";
2
+ export declare function consentPage(input: {
3
+ product: string;
4
+ host: string;
5
+ app: string;
6
+ appHost: string;
7
+ who: Identity;
8
+ action: string;
9
+ consentId: string;
10
+ }): string;
11
+ export declare function messagePage(product: string, title: string, message: string, link?: {
12
+ label: string;
13
+ href: string;
14
+ }): string;
@@ -0,0 +1,102 @@
1
+ import type { Comment, Identity, ScreenshotKind, Thread, ThreadStatus } from "../shared/types";
2
+ import type { CommentsEvent } from "./events";
3
+ import type { ThreadLink } from "./store";
4
+ /**
5
+ * Takes comments somewhere else, or lets something else in: a Linear issue
6
+ * per thread, an MCP server for agents, a Slack channel, ... Connectors see
7
+ * every change as it happens, can answer requests of their own under
8
+ * `{basePath}/{name}`, and act on comments by the same rules as the API.
9
+ *
10
+ * Built in: `web-comments/connectors/linear` and `web-comments/connectors/mcp`.
11
+ */
12
+ export interface Connector<Env = unknown> {
13
+ /** Lowercase, unique: it names the connector's routes, links and stored state. */
14
+ name: string;
15
+ /**
16
+ * After every change, once the response has gone out. Changes this
17
+ * connector made itself (through `as()`) aren't sent back to it.
18
+ */
19
+ onEvent?(event: CommentsEvent, context: ConnectorContext<Env>): void | Promise<void>;
20
+ /**
21
+ * Requests under `{basePath}/{name}` (and `rootPaths`). Nobody is signed in
22
+ * for them: check the caller yourself (a webhook's signature, a token), or
23
+ * ask `context.identify()` for the visitor the API itself would see.
24
+ */
25
+ fetch?(request: Request, context: ConnectorRequestContext<Env>): Response | Promise<Response>;
26
+ /**
27
+ * Paths at the root of the origin this connector answers too, such as
28
+ * OAuth's `/.well-known/...` documents. They only arrive if the host
29
+ * sends them (see `matches()`).
30
+ */
31
+ rootPaths?(basePath: string): string[];
32
+ }
33
+ export interface ConnectorContext<Env = unknown> {
34
+ env: Env;
35
+ site: string;
36
+ /** Where the API is mounted, e.g. "/__comments". */
37
+ basePath: string;
38
+ /**
39
+ * Acts on comments as someone, by the API's rules (only authors delete
40
+ * their comments, and so on). Changes are announced like the API's own,
41
+ * with this connector as their `source`.
42
+ */
43
+ as(actor: Identity): CommentsActions;
44
+ /** Links between threads and what this connector keeps elsewhere. */
45
+ links: {
46
+ find(externalId: string): Promise<ThreadLink | null>;
47
+ forThread(threadId: string): Promise<ThreadLink[]>;
48
+ save(link: {
49
+ externalId: string;
50
+ threadId: string;
51
+ commentId?: string | null;
52
+ url?: string | null;
53
+ title?: string | null;
54
+ }): Promise<void>;
55
+ };
56
+ /** State of this connector's own, by key (kept apart from other connectors'). */
57
+ storage: ConnectorStorage;
58
+ /** A thread's screenshot, straight from the asset store. */
59
+ screenshot(thread: Thread, kind: ScreenshotKind): Promise<{
60
+ body: ArrayBuffer;
61
+ contentType: string;
62
+ } | null>;
63
+ /** The page the thread is on, with the thread open. */
64
+ threadUrl(thread: Thread): string;
65
+ /** Keeps the request alive for work that finishes after the response. */
66
+ waitUntil(work: Promise<unknown>): void;
67
+ }
68
+ export interface ConnectorRequestContext<Env = unknown> extends ConnectorContext<Env> {
69
+ /** This connector's own address: `{origin}{basePath}/{name}`. */
70
+ url: string;
71
+ /**
72
+ * Who a request is from by the API's own sign-in (its auth plugins and
73
+ * `authorize`), or null: e.g. the person approving an OAuth client.
74
+ */
75
+ identify(request: Request): Promise<Identity | null>;
76
+ /** Where the auth plugins send someone to sign in, if they know. */
77
+ loginUrl(request: Request): string | undefined;
78
+ }
79
+ export interface ConnectorStorage {
80
+ get(key: string): Promise<string | null>;
81
+ set(key: string, value: string, options?: {
82
+ ttlSeconds?: number;
83
+ }): Promise<void>;
84
+ delete(key: string): Promise<void>;
85
+ }
86
+ /** What a connector may do with comments, as whoever it acts for. */
87
+ export interface CommentsActions {
88
+ /** Threads on one page (oldest first), or the site's most recent (newest first). */
89
+ listThreads(query?: {
90
+ page?: string;
91
+ status?: ThreadStatus;
92
+ limit?: number;
93
+ }): Promise<Thread[]>;
94
+ getThread(id: string): Promise<Thread | null>;
95
+ reply(threadId: string, body: string): Promise<{
96
+ thread: Thread;
97
+ comment: Comment;
98
+ }>;
99
+ setStatus(threadId: string, status: ThreadStatus): Promise<Thread>;
100
+ /** Deleting the comment that opens a thread deletes the thread. */
101
+ deleteComment(commentId: string): Promise<void>;
102
+ }
@@ -0,0 +1,31 @@
1
+ import type { Comment, Identity, Thread } from "../shared/types";
2
+ /**
3
+ * A change, as integrations hear about it. `source` names the connector that
4
+ * made it, for changes that came in through one (a reply from Linear); the
5
+ * API's own have none.
6
+ */
7
+ export type CommentsEvent = ({
8
+ type: "thread.created";
9
+ thread: Thread;
10
+ actor: Identity;
11
+ } | {
12
+ type: "comment.created";
13
+ thread: Thread;
14
+ comment: Comment;
15
+ actor: Identity;
16
+ } | {
17
+ type: "thread.resolved" | "thread.reopened" | "thread.moved";
18
+ thread: Thread;
19
+ actor: Identity;
20
+ } | {
21
+ type: "comment.deleted";
22
+ threadId: string;
23
+ commentId: string;
24
+ actor: Identity;
25
+ } | {
26
+ type: "thread.deleted";
27
+ threadId: string;
28
+ actor: Identity;
29
+ }) & {
30
+ source?: string;
31
+ };
@@ -1,32 +1,15 @@
1
- import type { Comment, Identity, Thread } from "../shared/types";
1
+ import { Hono } from "hono";
2
+ import type { Identity } from "../shared/types";
2
3
  import { type AuthPlugin } from "./auth";
4
+ import type { Connector } from "./connector";
5
+ import type { CommentsEvent } from "./events";
3
6
  import type { AssetStore, CommentsStore } from "./store";
4
7
  export type * from "../shared/types";
5
8
  export type { AuthPlugin } from "./auth";
6
- export type { AssetStore, CommentsStore, StoredScreenshot, StoredThread, ThreadChanges, } from "./store";
7
- export type CommentsEvent = {
8
- type: "thread.created";
9
- thread: Thread;
10
- actor: Identity;
11
- } | {
12
- type: "comment.created";
13
- thread: Thread;
14
- comment: Comment;
15
- actor: Identity;
16
- } | {
17
- type: "thread.resolved" | "thread.reopened" | "thread.moved";
18
- thread: Thread;
19
- actor: Identity;
20
- } | {
21
- type: "comment.deleted";
22
- threadId: string;
23
- commentId: string;
24
- actor: Identity;
25
- } | {
26
- type: "thread.deleted";
27
- threadId: string;
28
- actor: Identity;
29
- };
9
+ export type { CommentsActions, Connector, ConnectorContext, ConnectorRequestContext, ConnectorStorage, } from "./connector";
10
+ export type { CommentsEvent } from "./events";
11
+ export { CommentsError, newId } from "./service";
12
+ export type { AssetStore, CommentsStore, StoredScreenshot, StoredThread, ThreadChanges, ThreadLink, } from "./store";
30
13
  export interface CommentsOptions<Env extends object> {
31
14
  /** Tried in order; the first plugin that recognises the request wins. */
32
15
  auth: AuthPlugin<Env>[];
@@ -46,26 +29,22 @@ export interface CommentsOptions<Env extends object> {
46
29
  widgetScript?: string;
47
30
  /** Largest screenshot accepted, in bytes. Default 5 MB. */
48
31
  maxScreenshotBytes?: number;
32
+ /** Query parameter of thread links, as the widget's `linkParam`. Default "comment". */
33
+ linkParam?: string;
34
+ /** Integrations: Linear, MCP, or your own. See `Connector`. */
35
+ connectors?: Connector<Env>[];
49
36
  /** Called after every change, for integrations. Errors are logged, never returned. */
50
37
  onEvent?: (event: CommentsEvent, env: Env) => void | Promise<void>;
51
38
  }
52
39
  interface ExecutionContextLike {
53
40
  waitUntil(promise: Promise<unknown>): void;
54
41
  }
55
- type Vars = {
56
- who: Identity;
57
- site: string;
58
- store: CommentsStore;
59
- assets: AssetStore | null;
60
- };
61
- export declare function newId(): string;
62
42
  export declare function createComments<Env extends object = object>(options: CommentsOptions<Env>): {
63
- app: import("hono/hono-base").HonoBase<{
43
+ app: Hono<{
64
44
  Bindings: Env;
65
- Variables: Vars;
66
- }, import("hono/types").BlankSchema, string, string>;
67
- /** Answers requests under basePath; anything else gets a 404. */
45
+ }, import("hono/types").BlankSchema, "/">;
46
+ /** Answers requests under basePath (and connectors' root paths); anything else gets a 404. */
68
47
  fetch(request: Request, env: Env, ctx?: ExecutionContextLike): Promise<Response>;
69
- /** Whether a request belongs to the comments API, to route it from a larger worker. */
48
+ /** Whether a request belongs here, to route it from a larger worker. */
70
49
  matches(request: Request): boolean;
71
50
  };
@@ -0,0 +1,53 @@
1
+ import type { Author, Comment, Identity, ScreenshotKind, Thread, ThreadStatus } from "../shared/types";
2
+ import type { CommentsEvent } from "./events";
3
+ import type { AssetStore, CommentsStore } from "./store";
4
+ import * as validate from "./validate";
5
+ /** A failure the caller should hear about as it is (404, 403, ...). */
6
+ export declare class CommentsError extends Error {
7
+ readonly status: 400 | 403 | 404 | 413 | 415 | 501;
8
+ readonly code: string;
9
+ constructor(status: 400 | 403 | 404 | 413 | 415 | 501, code: string, message: string);
10
+ }
11
+ /** One request's view of the comments: its site, where things are kept, and where changes go. */
12
+ export interface Scope {
13
+ site: string;
14
+ store: CommentsStore;
15
+ assets: AssetStore | null;
16
+ announce(event: CommentsEvent): void;
17
+ waitUntil(work: Promise<unknown>): void;
18
+ }
19
+ export declare function newId(): string;
20
+ /**
21
+ * Now, as ISO 8601, never the same as or earlier than the last time handed
22
+ * out here: comments are ordered by time, and two written in the same
23
+ * millisecond must still come out in the order they were made.
24
+ */
25
+ export declare function now(): string;
26
+ export declare function authorOf(who: Identity): Author;
27
+ /**
28
+ * The rules, whoever asks: the HTTP API and connectors both go through here,
29
+ * so a reply from Linear or an agent obeys what a reply from the widget does.
30
+ */
31
+ export declare function createService(options: {
32
+ basePath: string;
33
+ maxScreenshotBytes: number;
34
+ }): {
35
+ list: (scope: Scope, query?: {
36
+ page?: string;
37
+ status?: ThreadStatus;
38
+ limit?: number;
39
+ }) => Promise<Thread[]>;
40
+ get: (scope: Scope, id: string) => Promise<Thread | null>;
41
+ create: (scope: Scope, actor: Identity, input: ReturnType<typeof validate.createThread>, files?: Partial<Record<ScreenshotKind, File>>) => Promise<Thread>;
42
+ reply: (scope: Scope, actor: Identity, threadId: string, body: string) => Promise<{
43
+ thread: Thread;
44
+ comment: Comment;
45
+ }>;
46
+ update: (scope: Scope, actor: Identity, threadId: string, change: ReturnType<typeof validate.threadUpdate>) => Promise<Thread>;
47
+ deleteComment: (scope: Scope, actor: Identity, commentId: string) => Promise<{
48
+ deleted: "thread" | "comment";
49
+ threadId: string;
50
+ }>;
51
+ assetKey: (scope: Scope, path: string) => string | null;
52
+ };
53
+ export type Service = ReturnType<typeof createService>;
@@ -25,10 +25,36 @@ export interface CommentsStore {
25
25
  updateThread(site: string, id: string, changes: ThreadChanges): Promise<void>;
26
26
  getComment(site: string, id: string): Promise<Comment | null>;
27
27
  deleteComment(site: string, id: string): Promise<void>;
28
- /** Deletes a thread and all its comments. */
28
+ /** Deletes a thread with all its comments and links. */
29
29
  deleteThread(site: string, id: string): Promise<void>;
30
30
  /** Remembers who has been here (for @mentions); optional. */
31
31
  saveUser?(who: Identity, seenAt: string): Promise<void>;
32
+ /** Records a link between a thread (or one of its comments) and something elsewhere; replaces one with the same connector and external id. */
33
+ saveLink?(site: string, link: ThreadLink): Promise<void>;
34
+ findLink?(site: string, connector: string, externalId: string): Promise<ThreadLink | null>;
35
+ /** Every link of these threads. */
36
+ listLinks?(site: string, threadIds: string[]): Promise<ThreadLink[]>;
37
+ /** A connector's own state (tokens, settings), by key. Null once it has expired. */
38
+ getValue?(key: string): Promise<string | null>;
39
+ /** `expiresAt`: ISO 8601, or null to keep it. */
40
+ setValue?(key: string, value: string, expiresAt: string | null): Promise<void>;
41
+ deleteValue?(key: string): Promise<void>;
42
+ }
43
+ /**
44
+ * A thread, or one comment in it, tied to something a connector keeps
45
+ * elsewhere: the Linear issue a thread became, the Linear comment a reply was
46
+ * mirrored to. Links with a URL and no comment show on the thread.
47
+ */
48
+ export interface ThreadLink {
49
+ connector: string;
50
+ /** The other system's id, unique per connector. */
51
+ externalId: string;
52
+ threadId: string;
53
+ commentId: string | null;
54
+ url: string | null;
55
+ /** How to show it, e.g. "ENG-123". */
56
+ title: string | null;
57
+ createdAt: string;
32
58
  }
33
59
  /** A screenshot as stored: the asset key, not a URL. */
34
60
  export interface StoredScreenshot {
@@ -38,7 +64,7 @@ export interface StoredScreenshot {
38
64
  height: number;
39
65
  }
40
66
  /** A thread as a store keeps it. */
41
- export interface StoredThread extends Omit<Thread, "screenshots"> {
67
+ export interface StoredThread extends Omit<Thread, "screenshots" | "links"> {
42
68
  site: string;
43
69
  screenshots: Partial<Record<ScreenshotKind, StoredScreenshot>>;
44
70
  }
@@ -90,6 +90,15 @@ export interface Thread {
90
90
  resolvedBy: Author | null;
91
91
  /** Oldest first. The first comment opens the thread. */
92
92
  comments: Comment[];
93
+ /** Where connectors took it, e.g. the Linear issue it became. */
94
+ links: ThreadLinkSummary[];
95
+ }
96
+ export interface ThreadLinkSummary {
97
+ /** The connector that made it, e.g. "linear". */
98
+ connector: string;
99
+ url: string;
100
+ /** How to show it, e.g. "ENG-123". */
101
+ title: string | null;
93
102
  }
94
103
  export interface ApiError {
95
104
  error: {
@@ -502,5 +502,199 @@ export declare const users: import("drizzle-orm/mysql-core").MySqlTableWithColum
502
502
  dialect: 'mysql';
503
503
  isAlias: false;
504
504
  }>;
505
+ /** Threads and comments tied to what connectors keep elsewhere (a Linear issue, ...). */
506
+ export declare const links: import("drizzle-orm/mysql-core").MySqlTableWithColumns<{
507
+ name: "wc_links";
508
+ schema: undefined;
509
+ columns: {
510
+ site: import("drizzle-orm/mysql-core").MySqlColumn<{
511
+ name: string;
512
+ tableName: "wc_links";
513
+ dataType: "string";
514
+ data: string;
515
+ driverParam: string | number;
516
+ notNull: true;
517
+ hasDefault: false;
518
+ isPrimaryKey: false;
519
+ isAutoincrement: false;
520
+ hasRuntimeDefault: false;
521
+ enumValues: [string, ...string[]];
522
+ baseColumn: never;
523
+ identity: undefined;
524
+ generated: undefined;
525
+ }, {}>;
526
+ connector: import("drizzle-orm/mysql-core").MySqlColumn<{
527
+ name: string;
528
+ tableName: "wc_links";
529
+ dataType: "string";
530
+ data: string;
531
+ driverParam: string | number;
532
+ notNull: true;
533
+ hasDefault: false;
534
+ isPrimaryKey: false;
535
+ isAutoincrement: false;
536
+ hasRuntimeDefault: false;
537
+ enumValues: [string, ...string[]];
538
+ baseColumn: never;
539
+ identity: undefined;
540
+ generated: undefined;
541
+ }, {}>;
542
+ externalId: import("drizzle-orm/mysql-core").MySqlColumn<{
543
+ name: string;
544
+ tableName: "wc_links";
545
+ dataType: "string";
546
+ data: string;
547
+ driverParam: string | number;
548
+ notNull: true;
549
+ hasDefault: false;
550
+ isPrimaryKey: false;
551
+ isAutoincrement: false;
552
+ hasRuntimeDefault: false;
553
+ enumValues: [string, ...string[]];
554
+ baseColumn: never;
555
+ identity: undefined;
556
+ generated: undefined;
557
+ }, {}>;
558
+ threadId: import("drizzle-orm/mysql-core").MySqlColumn<{
559
+ name: string;
560
+ tableName: "wc_links";
561
+ dataType: "string";
562
+ data: string;
563
+ driverParam: string | number;
564
+ notNull: true;
565
+ hasDefault: false;
566
+ isPrimaryKey: false;
567
+ isAutoincrement: false;
568
+ hasRuntimeDefault: false;
569
+ enumValues: [string, ...string[]];
570
+ baseColumn: never;
571
+ identity: undefined;
572
+ generated: undefined;
573
+ }, {}>;
574
+ commentId: import("drizzle-orm/mysql-core").MySqlColumn<{
575
+ name: string;
576
+ tableName: "wc_links";
577
+ dataType: "string";
578
+ data: string;
579
+ driverParam: string | number;
580
+ notNull: false;
581
+ hasDefault: false;
582
+ isPrimaryKey: false;
583
+ isAutoincrement: false;
584
+ hasRuntimeDefault: false;
585
+ enumValues: [string, ...string[]];
586
+ baseColumn: never;
587
+ identity: undefined;
588
+ generated: undefined;
589
+ }, {}>;
590
+ url: import("drizzle-orm/mysql-core").MySqlColumn<{
591
+ name: string;
592
+ tableName: "wc_links";
593
+ dataType: "string";
594
+ data: string;
595
+ driverParam: string;
596
+ notNull: false;
597
+ hasDefault: false;
598
+ isPrimaryKey: false;
599
+ isAutoincrement: false;
600
+ hasRuntimeDefault: false;
601
+ enumValues: [string, ...string[]];
602
+ baseColumn: never;
603
+ identity: undefined;
604
+ generated: undefined;
605
+ }, {}>;
606
+ title: import("drizzle-orm/mysql-core").MySqlColumn<{
607
+ name: string;
608
+ tableName: "wc_links";
609
+ dataType: "string";
610
+ data: string;
611
+ driverParam: string | number;
612
+ notNull: false;
613
+ hasDefault: false;
614
+ isPrimaryKey: false;
615
+ isAutoincrement: false;
616
+ hasRuntimeDefault: false;
617
+ enumValues: [string, ...string[]];
618
+ baseColumn: never;
619
+ identity: undefined;
620
+ generated: undefined;
621
+ }, {}>;
622
+ createdAt: import("drizzle-orm/mysql-core").MySqlColumn<{
623
+ name: string;
624
+ tableName: "wc_links";
625
+ dataType: "string";
626
+ data: string;
627
+ driverParam: string | number;
628
+ notNull: true;
629
+ hasDefault: false;
630
+ isPrimaryKey: false;
631
+ isAutoincrement: false;
632
+ hasRuntimeDefault: false;
633
+ enumValues: [string, ...string[]];
634
+ baseColumn: never;
635
+ identity: undefined;
636
+ generated: undefined;
637
+ }, {}>;
638
+ };
639
+ dialect: 'mysql';
640
+ isAlias: false;
641
+ }>;
642
+ /** Connectors' own state (OAuth clients and tokens, ...), by name. */
643
+ export declare const values: import("drizzle-orm/mysql-core").MySqlTableWithColumns<{
644
+ name: "wc_values";
645
+ schema: undefined;
646
+ columns: {
647
+ name: import("drizzle-orm/mysql-core").MySqlColumn<{
648
+ name: string;
649
+ tableName: "wc_values";
650
+ dataType: "string";
651
+ data: string;
652
+ driverParam: string | number;
653
+ notNull: true;
654
+ hasDefault: false;
655
+ isPrimaryKey: true;
656
+ isAutoincrement: false;
657
+ hasRuntimeDefault: false;
658
+ enumValues: [string, ...string[]];
659
+ baseColumn: never;
660
+ identity: undefined;
661
+ generated: undefined;
662
+ }, {}>;
663
+ value: import("drizzle-orm/mysql-core").MySqlColumn<{
664
+ name: string;
665
+ tableName: "wc_values";
666
+ dataType: "string";
667
+ data: string;
668
+ driverParam: string;
669
+ notNull: true;
670
+ hasDefault: false;
671
+ isPrimaryKey: false;
672
+ isAutoincrement: false;
673
+ hasRuntimeDefault: false;
674
+ enumValues: [string, ...string[]];
675
+ baseColumn: never;
676
+ identity: undefined;
677
+ generated: undefined;
678
+ }, {}>;
679
+ expiresAt: import("drizzle-orm/mysql-core").MySqlColumn<{
680
+ name: string;
681
+ tableName: "wc_values";
682
+ dataType: "string";
683
+ data: string;
684
+ driverParam: string | number;
685
+ notNull: false;
686
+ hasDefault: false;
687
+ isPrimaryKey: false;
688
+ isAutoincrement: false;
689
+ hasRuntimeDefault: false;
690
+ enumValues: [string, ...string[]];
691
+ baseColumn: never;
692
+ identity: undefined;
693
+ generated: undefined;
694
+ }, {}>;
695
+ };
696
+ dialect: 'mysql';
697
+ isAlias: false;
698
+ }>;
505
699
  /** The same tables as SQL, each statement safe to run again (MySQL has no CREATE INDEX IF NOT EXISTS, so indexes are inline). */
506
700
  export declare const createTablesSql: string[];