web-comments 0.0.1 → 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.
- package/README.md +263 -6
- package/dist/auth/bearer-token.js +43 -0
- package/dist/auth/cloudflare-access.js +90 -0
- package/dist/auth/dev-user.js +21 -0
- package/dist/auth/session-endpoint.js +47 -0
- package/dist/chunks/chunk-644PTNSO.js +101 -0
- package/dist/chunks/chunk-7K7K7M5O.js +101 -0
- package/dist/chunks/chunk-RRSB326L.js +720 -0
- package/dist/chunks/chunk-T4WN3STA.js +239 -0
- package/dist/chunks/chunk-WSFCRVEQ.js +9 -0
- package/dist/chunks/chunk-WY5E2C47.js +99 -0
- package/dist/connectors/linear.js +318 -0
- package/dist/connectors/mcp.js +890 -0
- package/dist/index.js +52 -0
- package/dist/react.js +19 -0
- package/dist/server.js +11 -0
- package/dist/store/d1.js +16 -0
- package/dist/store/drizzle/mysql.js +17 -0
- package/dist/store/drizzle/pg.js +17 -0
- package/dist/store/drizzle/sqlite.js +17 -0
- package/dist/store/drizzle.js +10 -0
- package/dist/store/memory.js +109 -0
- package/dist/store/r2.js +25 -0
- package/dist/store/s3.js +43 -0
- package/dist/types/auth/bearer-token.d.ts +13 -0
- package/dist/types/auth/cloudflare-access.d.ts +33 -0
- package/dist/types/auth/dev-user.d.ts +6 -0
- package/dist/types/auth/session-endpoint.d.ts +25 -0
- package/dist/types/connectors/linear/index.d.ts +46 -0
- package/dist/types/connectors/mcp/index.d.ts +21 -0
- package/dist/types/connectors/mcp/oauth.d.ts +50 -0
- package/dist/types/connectors/mcp/pages.d.ts +14 -0
- package/dist/types/react.d.ts +8 -0
- package/dist/types/server/auth.d.ts +16 -0
- package/dist/types/server/connector.d.ts +102 -0
- package/dist/types/server/events.d.ts +31 -0
- package/dist/types/server/index.d.ts +50 -0
- package/dist/types/server/service.d.ts +53 -0
- package/dist/types/server/store.d.ts +85 -0
- package/dist/types/server/validate.d.ts +20 -0
- package/dist/types/shared/types.d.ts +117 -0
- package/dist/types/store/d1.d.ts +9 -0
- package/dist/types/store/drizzle/index.d.ts +22 -0
- package/dist/types/store/drizzle/mysql.d.ts +700 -0
- package/dist/types/store/drizzle/pg.d.ts +659 -0
- package/dist/types/store/drizzle/sqlite.d.ts +700 -0
- package/dist/types/store/memory.d.ts +10 -0
- package/dist/types/store/r2.d.ts +20 -0
- package/dist/types/store/s3.d.ts +21 -0
- package/dist/types/widget/anchor.d.ts +9 -0
- package/dist/types/widget/api.d.ts +43 -0
- package/dist/types/widget/app.d.ts +29 -0
- package/dist/types/widget/capture.d.ts +33 -0
- package/dist/types/widget/format.d.ts +15 -0
- package/dist/types/widget/index.d.ts +41 -0
- package/dist/types/widget/styles.d.ts +6 -0
- package/dist/types/widget/thread.d.ts +32 -0
- package/dist/types/widget/tracker.d.ts +68 -0
- package/dist/widget.global.js +52 -0
- package/package.json +144 -6
- package/types/widget.global.d.ts +5 -0
- package/index.d.ts +0 -2
- package/index.js +0 -6
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type { Identity } from "../shared/types";
|
|
3
|
+
import { type AuthPlugin } from "./auth";
|
|
4
|
+
import type { Connector } from "./connector";
|
|
5
|
+
import type { CommentsEvent } from "./events";
|
|
6
|
+
import type { AssetStore, CommentsStore } from "./store";
|
|
7
|
+
export type * from "../shared/types";
|
|
8
|
+
export type { AuthPlugin } from "./auth";
|
|
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";
|
|
13
|
+
export interface CommentsOptions<Env extends object> {
|
|
14
|
+
/** Tried in order; the first plugin that recognises the request wins. */
|
|
15
|
+
auth: AuthPlugin<Env>[];
|
|
16
|
+
/** Where comments are kept, e.g. `(env) => d1Store(env.DB)`. Any CommentsStore works. */
|
|
17
|
+
store: (env: Env) => CommentsStore;
|
|
18
|
+
/** Where screenshots are kept, e.g. `(env) => r2Assets(env.SCREENSHOTS)`. Without it, threads keep none. */
|
|
19
|
+
assets?: (env: Env) => AssetStore;
|
|
20
|
+
/** Who may use comments once identified. Everyone identified, by default. */
|
|
21
|
+
authorize?: (who: Identity, request: Request, env: Env) => boolean | Promise<boolean>;
|
|
22
|
+
/** Where the API is mounted. Default "/__comments". */
|
|
23
|
+
basePath?: string;
|
|
24
|
+
/** Keeps several sites apart in one store. Default "default". */
|
|
25
|
+
site?: (request: Request, env: Env) => string;
|
|
26
|
+
/** Origins other than the API's own that may call it with credentials. */
|
|
27
|
+
allowedOrigins?: string[];
|
|
28
|
+
/** The widget bundle (dist/widget.global.js) to serve at `{basePath}/widget.js`. */
|
|
29
|
+
widgetScript?: string;
|
|
30
|
+
/** Largest screenshot accepted, in bytes. Default 5 MB. */
|
|
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>[];
|
|
36
|
+
/** Called after every change, for integrations. Errors are logged, never returned. */
|
|
37
|
+
onEvent?: (event: CommentsEvent, env: Env) => void | Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
interface ExecutionContextLike {
|
|
40
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
41
|
+
}
|
|
42
|
+
export declare function createComments<Env extends object = object>(options: CommentsOptions<Env>): {
|
|
43
|
+
app: Hono<{
|
|
44
|
+
Bindings: Env;
|
|
45
|
+
}, import("hono/types").BlankSchema, "/">;
|
|
46
|
+
/** Answers requests under basePath (and connectors' root paths); anything else gets a 404. */
|
|
47
|
+
fetch(request: Request, env: Env, ctx?: ExecutionContextLike): Promise<Response>;
|
|
48
|
+
/** Whether a request belongs here, to route it from a larger worker. */
|
|
49
|
+
matches(request: Request): boolean;
|
|
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>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Comment, Identity, ScreenshotKind, Thread, ThreadStatus } from "../shared/types";
|
|
2
|
+
/**
|
|
3
|
+
* Where comments are kept. The service owns the rules (who may do what, ids,
|
|
4
|
+
* timestamps, what deleting the first comment means); a store only saves and
|
|
5
|
+
* loads. Implement it over anything: the built-in ones are Drizzle (SQLite/D1,
|
|
6
|
+
* Postgres, MySQL) and an in-memory store.
|
|
7
|
+
*
|
|
8
|
+
* Every lookup is scoped to a site, so one store can serve several.
|
|
9
|
+
*/
|
|
10
|
+
export interface CommentsStore {
|
|
11
|
+
/** Gets storage ready (e.g. creates tables). Called once before first use; optional. */
|
|
12
|
+
setup?(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Threads with their comments (oldest first). With a page: that page's
|
|
15
|
+
* threads, oldest first. Without: the site's most recent, newest first.
|
|
16
|
+
*/
|
|
17
|
+
listThreads(site: string, query: {
|
|
18
|
+
page?: string;
|
|
19
|
+
limit: number;
|
|
20
|
+
}): Promise<StoredThread[]>;
|
|
21
|
+
getThread(site: string, id: string): Promise<StoredThread | null>;
|
|
22
|
+
/** Saves a new thread together with the comment that opens it. */
|
|
23
|
+
insertThread(thread: StoredThread, first: Comment): Promise<void>;
|
|
24
|
+
insertComment(site: string, comment: Comment): Promise<void>;
|
|
25
|
+
updateThread(site: string, id: string, changes: ThreadChanges): Promise<void>;
|
|
26
|
+
getComment(site: string, id: string): Promise<Comment | null>;
|
|
27
|
+
deleteComment(site: string, id: string): Promise<void>;
|
|
28
|
+
/** Deletes a thread with all its comments and links. */
|
|
29
|
+
deleteThread(site: string, id: string): Promise<void>;
|
|
30
|
+
/** Remembers who has been here (for @mentions); optional. */
|
|
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;
|
|
58
|
+
}
|
|
59
|
+
/** A screenshot as stored: the asset key, not a URL. */
|
|
60
|
+
export interface StoredScreenshot {
|
|
61
|
+
key: string;
|
|
62
|
+
contentType: string;
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
}
|
|
66
|
+
/** A thread as a store keeps it. */
|
|
67
|
+
export interface StoredThread extends Omit<Thread, "screenshots" | "links"> {
|
|
68
|
+
site: string;
|
|
69
|
+
screenshots: Partial<Record<ScreenshotKind, StoredScreenshot>>;
|
|
70
|
+
}
|
|
71
|
+
export type ThreadChanges = Partial<Pick<StoredThread, "status" | "anchor" | "screenshots" | "updatedAt" | "resolvedAt" | "resolvedBy">>;
|
|
72
|
+
export type { ThreadStatus };
|
|
73
|
+
/**
|
|
74
|
+
* Where screenshots are kept. Implement it over anything that can hold bytes
|
|
75
|
+
* under a key; the built-ins are R2, S3 (and S3-compatible) and in-memory.
|
|
76
|
+
* The API serves them to people it has identified, so the bucket can stay private.
|
|
77
|
+
*/
|
|
78
|
+
export interface AssetStore {
|
|
79
|
+
put(key: string, body: ArrayBuffer, contentType: string): Promise<void>;
|
|
80
|
+
get(key: string): Promise<{
|
|
81
|
+
body: ReadableStream | ArrayBuffer;
|
|
82
|
+
contentType: string;
|
|
83
|
+
} | null>;
|
|
84
|
+
delete?(key: string): Promise<void>;
|
|
85
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Anchor, CreateThreadInput, ScreenshotKind, ThreadContext, ThreadStatus } from "../shared/types";
|
|
2
|
+
export declare class InvalidInput extends Error {
|
|
3
|
+
}
|
|
4
|
+
export declare function commentBody(value: unknown): string;
|
|
5
|
+
export declare function reply(value: unknown): string;
|
|
6
|
+
export declare function anchor(value: unknown): Anchor;
|
|
7
|
+
export declare function context(value: unknown): ThreadContext;
|
|
8
|
+
export declare const SCREENSHOT_KINDS: readonly ScreenshotKind[];
|
|
9
|
+
/** A new thread: what's said and where, plus the size of each screenshot sent with it. */
|
|
10
|
+
export declare function createThread(value: unknown): CreateThreadInput & {
|
|
11
|
+
screenshots: Partial<Record<ScreenshotKind, {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}>>;
|
|
15
|
+
};
|
|
16
|
+
/** A thread change: resolve or reopen it, move its pin, or both. */
|
|
17
|
+
export declare function threadUpdate(value: unknown): {
|
|
18
|
+
status?: ThreadStatus;
|
|
19
|
+
anchor?: Anchor;
|
|
20
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** Who is making a request, as an auth plugin reports it. */
|
|
2
|
+
export interface Identity {
|
|
3
|
+
/** Stable and prefixed by its source, e.g. "access:<sub>" or "token:<name>". */
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
email?: string;
|
|
7
|
+
avatarUrl?: string;
|
|
8
|
+
kind: "human" | "agent";
|
|
9
|
+
}
|
|
10
|
+
/** The author of a comment as it was when the comment was written. */
|
|
11
|
+
export interface Author {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
avatarUrl?: string;
|
|
15
|
+
kind: "human" | "agent";
|
|
16
|
+
}
|
|
17
|
+
/** Where a comment sits on the page. */
|
|
18
|
+
export interface Anchor {
|
|
19
|
+
/** CSS selector of the element the comment was dropped on. */
|
|
20
|
+
selector: string;
|
|
21
|
+
/** Start of the element's text when the comment was made, to find it again if the selector breaks. */
|
|
22
|
+
text?: string;
|
|
23
|
+
/** Lowercase tag name of the element. */
|
|
24
|
+
tag: string;
|
|
25
|
+
/** Point inside the element's box, as fractions of its width and height (0–1). */
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
/** Viewport width when the comment was made, for context. */
|
|
29
|
+
viewportWidth: number;
|
|
30
|
+
}
|
|
31
|
+
/** What the commenter was looking at, recorded with the first comment. */
|
|
32
|
+
export interface ThreadContext {
|
|
33
|
+
element: {
|
|
34
|
+
/** The element's outerHTML, cut to a few kilobytes. */
|
|
35
|
+
html: string;
|
|
36
|
+
/** Its box in the viewport when the comment was made, in CSS pixels. */
|
|
37
|
+
rect: {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
page: {
|
|
45
|
+
title: string;
|
|
46
|
+
viewport: {
|
|
47
|
+
width: number;
|
|
48
|
+
height: number;
|
|
49
|
+
};
|
|
50
|
+
scroll: {
|
|
51
|
+
x: number;
|
|
52
|
+
y: number;
|
|
53
|
+
};
|
|
54
|
+
devicePixelRatio: number;
|
|
55
|
+
userAgent: string;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Which screenshots a thread keeps: the element, and the viewport around it. */
|
|
59
|
+
export type ScreenshotKind = "element" | "page";
|
|
60
|
+
export interface Screenshot {
|
|
61
|
+
/** Where the API serves it (it checks who is asking first). */
|
|
62
|
+
url: string;
|
|
63
|
+
contentType: string;
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
}
|
|
67
|
+
export type ThreadStatus = "open" | "resolved";
|
|
68
|
+
export interface Comment {
|
|
69
|
+
id: string;
|
|
70
|
+
threadId: string;
|
|
71
|
+
body: string;
|
|
72
|
+
author: Author;
|
|
73
|
+
createdAt: string;
|
|
74
|
+
}
|
|
75
|
+
export interface Thread {
|
|
76
|
+
id: string;
|
|
77
|
+
/** The page key the widget filed it under (by default the path). */
|
|
78
|
+
page: string;
|
|
79
|
+
/** Full address the comment was made on. */
|
|
80
|
+
url: string;
|
|
81
|
+
anchor: Anchor;
|
|
82
|
+
/** Null for threads made by clients that send none. */
|
|
83
|
+
context: ThreadContext | null;
|
|
84
|
+
screenshots: Partial<Record<ScreenshotKind, Screenshot>>;
|
|
85
|
+
status: ThreadStatus;
|
|
86
|
+
author: Author;
|
|
87
|
+
createdAt: string;
|
|
88
|
+
updatedAt: string;
|
|
89
|
+
resolvedAt: string | null;
|
|
90
|
+
resolvedBy: Author | null;
|
|
91
|
+
/** Oldest first. The first comment opens the thread. */
|
|
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;
|
|
102
|
+
}
|
|
103
|
+
export interface ApiError {
|
|
104
|
+
error: {
|
|
105
|
+
code: string;
|
|
106
|
+
message: string;
|
|
107
|
+
};
|
|
108
|
+
/** Present on 401 when an auth plugin knows where to sign in. */
|
|
109
|
+
loginUrl?: string;
|
|
110
|
+
}
|
|
111
|
+
export interface CreateThreadInput {
|
|
112
|
+
page: string;
|
|
113
|
+
url: string;
|
|
114
|
+
anchor: Anchor;
|
|
115
|
+
context?: ThreadContext;
|
|
116
|
+
body: string;
|
|
117
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { drizzle } from "drizzle-orm/d1";
|
|
2
|
+
import type { CommentsStore } from "../server/store";
|
|
3
|
+
/**
|
|
4
|
+
* Comments in a Cloudflare D1 database, creating the tables on first use:
|
|
5
|
+
* `store: (env) => d1Store(env.DB)`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function d1Store(binding: Parameters<typeof drizzle>[0], options?: {
|
|
8
|
+
createTables?: boolean;
|
|
9
|
+
}): CommentsStore;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CommentsStore } from "../../server/store";
|
|
2
|
+
export type Dialect = "sqlite" | "pg" | "mysql";
|
|
3
|
+
export interface DrizzleStoreOptions {
|
|
4
|
+
/**
|
|
5
|
+
* A Drizzle database from any driver of the dialect: `drizzle(env.DB)` for
|
|
6
|
+
* D1, `drizzle(client)` for libSQL, PGlite, Neon, postgres.js,
|
|
7
|
+
* PlanetScale, mysql2, ...
|
|
8
|
+
*/
|
|
9
|
+
db: unknown;
|
|
10
|
+
dialect: Dialect;
|
|
11
|
+
/**
|
|
12
|
+
* Create the tables on first use if they're missing. Default true; turn it
|
|
13
|
+
* off when you migrate them yourself (the schemas are exported from
|
|
14
|
+
* web-comments/store/drizzle/{sqlite,pg,mysql}).
|
|
15
|
+
*/
|
|
16
|
+
createTables?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A CommentsStore over Drizzle, for SQLite (D1, libSQL/Turso, ...), Postgres
|
|
20
|
+
* (PlanetScale Postgres, Neon, Supabase, ...) and MySQL (PlanetScale, ...).
|
|
21
|
+
*/
|
|
22
|
+
export declare function drizzleStore(options: DrizzleStoreOptions): CommentsStore;
|