@the-focus-ai/artifacts 1.1.0 → 1.2.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.
@@ -55,6 +55,65 @@ export class HttpArtifactApiClient {
55
55
  return response;
56
56
  }
57
57
  }
58
+ export class HttpLivingDocApiClient {
59
+ publicBaseUrl;
60
+ fetchImpl;
61
+ constructor(publicBaseUrl = defaultPublicBaseUrl, fetchImpl = fetch) {
62
+ this.publicBaseUrl = publicBaseUrl;
63
+ this.fetchImpl = fetchImpl;
64
+ }
65
+ async publishDoc(token, markdown, options = {}) {
66
+ const response = await this.request("/api/doc?action=publish", token, {
67
+ method: "POST",
68
+ headers: { "content-type": "application/json" },
69
+ body: JSON.stringify({ markdown, title: options.title }),
70
+ });
71
+ return (await response.json());
72
+ }
73
+ async pullDoc(token, opaqueId) {
74
+ const response = await this.request("/api/doc?action=pull", token, {
75
+ method: "POST",
76
+ headers: { "content-type": "application/json" },
77
+ body: JSON.stringify({ opaqueId }),
78
+ });
79
+ return (await response.json());
80
+ }
81
+ async respondDoc(token, opaqueId, body) {
82
+ const response = await this.request("/api/doc?action=respond", token, {
83
+ method: "POST",
84
+ headers: { "content-type": "application/json" },
85
+ body: JSON.stringify({ opaqueId, ...body }),
86
+ });
87
+ return (await response.json());
88
+ }
89
+ async listDocs(token) {
90
+ const response = await this.request("/api/doc?action=list", token);
91
+ const body = (await response.json());
92
+ return body.livingDocs;
93
+ }
94
+ async removeDoc(token, opaqueId) {
95
+ const response = await this.request("/api/doc?action=remove", token, {
96
+ method: "POST",
97
+ headers: { "content-type": "application/json" },
98
+ body: JSON.stringify({ opaqueId }),
99
+ });
100
+ return (await response.json());
101
+ }
102
+ async request(path, token, init = {}) {
103
+ const response = await this.fetchImpl(new URL(path, this.publicBaseUrl), {
104
+ ...init,
105
+ headers: {
106
+ ...(init.headers ?? {}),
107
+ authorization: `Bearer ${token}`,
108
+ },
109
+ });
110
+ if (!response.ok) {
111
+ const text = await response.text();
112
+ throw new Error(text || `Living Doc API request failed: ${response.status}`);
113
+ }
114
+ return response;
115
+ }
116
+ }
58
117
  export function artifactUploadForm(upload, options = {}) {
59
118
  const form = new FormData();
60
119
  form.set("metadata", JSON.stringify({
@@ -1 +1 @@
1
- {"version":3,"file":"remote-api.js","sourceRoot":"","sources":["../../src/remote-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"remote-api.js","sourceRoot":"","sources":["../../src/remote-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,159 @@
1
+ import type { ClockOptions, SqlClient } from "./publication-metadata.js";
2
+ export type LivingDocStatus = "active" | "removed";
3
+ export type CommentOrigin = "reviewer" | "agent";
4
+ export type CommentStatus = "open" | "resolved";
5
+ export type SuggestionStatus = "pending" | "accepted" | "rejected";
6
+ export interface LivingDoc {
7
+ opaqueId: string;
8
+ reviewId: string;
9
+ publisherEmail: string;
10
+ status: LivingDocStatus;
11
+ title: string | null;
12
+ currentMarkdown: string;
13
+ latestVersionNumber: number;
14
+ createdAt: Date;
15
+ updatedAt: Date;
16
+ removedAt: Date | null;
17
+ }
18
+ export interface LivingDocVersion {
19
+ id: string;
20
+ livingDocId: string;
21
+ versionNumber: number;
22
+ markdown: string;
23
+ createdAt: Date;
24
+ }
25
+ export interface LivingDocComment {
26
+ id: string;
27
+ livingDocId: string;
28
+ origin: CommentOrigin;
29
+ author: string | null;
30
+ anchorQuote: string;
31
+ anchorStart: number | null;
32
+ anchorEnd: number | null;
33
+ body: string;
34
+ parentCommentId: string | null;
35
+ status: CommentStatus;
36
+ createdAt: Date;
37
+ }
38
+ export interface LivingDocSuggestion {
39
+ id: string;
40
+ livingDocId: string;
41
+ versionNumber: number;
42
+ anchorQuote: string;
43
+ anchorStart: number | null;
44
+ anchorEnd: number | null;
45
+ replacement: string;
46
+ note: string | null;
47
+ status: SuggestionStatus;
48
+ createdAt: Date;
49
+ resolvedAt: Date | null;
50
+ }
51
+ export interface CreateLivingDocInput {
52
+ opaqueId: string;
53
+ reviewId: string;
54
+ publisherEmail: string;
55
+ currentMarkdown: string;
56
+ status?: LivingDocStatus;
57
+ title?: string | null;
58
+ }
59
+ export interface CreateCommentInput {
60
+ id: string;
61
+ livingDocId: string;
62
+ origin: CommentOrigin;
63
+ body: string;
64
+ anchorQuote: string;
65
+ anchorStart?: number | null;
66
+ anchorEnd?: number | null;
67
+ author?: string | null;
68
+ parentCommentId?: string | null;
69
+ }
70
+ export interface CreateSuggestionInput {
71
+ id: string;
72
+ livingDocId: string;
73
+ versionNumber: number;
74
+ anchorQuote: string;
75
+ replacement: string;
76
+ anchorStart?: number | null;
77
+ anchorEnd?: number | null;
78
+ note?: string | null;
79
+ }
80
+ export interface CreateVersionInput {
81
+ id: string;
82
+ livingDocId: string;
83
+ versionNumber: number;
84
+ markdown: string;
85
+ }
86
+ export interface LivingDocMetadataStore {
87
+ create(input: CreateLivingDocInput): Promise<LivingDoc>;
88
+ getByOpaqueId(opaqueId: string): Promise<LivingDoc | null>;
89
+ getByReviewId(reviewId: string): Promise<LivingDoc | null>;
90
+ updateMarkdown(opaqueId: string, markdown: string): Promise<LivingDoc | null>;
91
+ setLatestVersionNumber(opaqueId: string, versionNumber: number): Promise<LivingDoc | null>;
92
+ markRemoved(opaqueId: string): Promise<LivingDoc | null>;
93
+ listByPublisherEmail(publisherEmail: string): Promise<LivingDoc[]>;
94
+ addVersion(input: CreateVersionInput): Promise<LivingDocVersion>;
95
+ getVersion(opaqueId: string, versionNumber: number): Promise<LivingDocVersion | null>;
96
+ addComment(input: CreateCommentInput): Promise<LivingDocComment>;
97
+ listComments(opaqueId: string, options?: {
98
+ status?: CommentStatus;
99
+ }): Promise<LivingDocComment[]>;
100
+ setCommentStatus(commentId: string, status: CommentStatus): Promise<LivingDocComment | null>;
101
+ addSuggestion(input: CreateSuggestionInput): Promise<LivingDocSuggestion>;
102
+ listSuggestions(opaqueId: string, options?: {
103
+ status?: SuggestionStatus;
104
+ }): Promise<LivingDocSuggestion[]>;
105
+ setSuggestionStatus(suggestionId: string, status: SuggestionStatus): Promise<LivingDocSuggestion | null>;
106
+ }
107
+ export declare class InMemoryLivingDocMetadataStore implements LivingDocMetadataStore {
108
+ private readonly docs;
109
+ private readonly versions;
110
+ private readonly comments;
111
+ private readonly suggestions;
112
+ private readonly now;
113
+ constructor(options?: ClockOptions);
114
+ create(input: CreateLivingDocInput): Promise<LivingDoc>;
115
+ getByOpaqueId(opaqueId: string): Promise<LivingDoc | null>;
116
+ getByReviewId(reviewId: string): Promise<LivingDoc | null>;
117
+ updateMarkdown(opaqueId: string, markdown: string): Promise<LivingDoc | null>;
118
+ setLatestVersionNumber(opaqueId: string, versionNumber: number): Promise<LivingDoc | null>;
119
+ markRemoved(opaqueId: string): Promise<LivingDoc | null>;
120
+ listByPublisherEmail(publisherEmail: string): Promise<LivingDoc[]>;
121
+ addVersion(input: CreateVersionInput): Promise<LivingDocVersion>;
122
+ getVersion(opaqueId: string, versionNumber: number): Promise<LivingDocVersion | null>;
123
+ addComment(input: CreateCommentInput): Promise<LivingDocComment>;
124
+ listComments(opaqueId: string, options?: {
125
+ status?: CommentStatus;
126
+ }): Promise<LivingDocComment[]>;
127
+ setCommentStatus(commentId: string, status: CommentStatus): Promise<LivingDocComment | null>;
128
+ addSuggestion(input: CreateSuggestionInput): Promise<LivingDocSuggestion>;
129
+ listSuggestions(opaqueId: string, options?: {
130
+ status?: SuggestionStatus;
131
+ }): Promise<LivingDocSuggestion[]>;
132
+ setSuggestionStatus(suggestionId: string, status: SuggestionStatus): Promise<LivingDocSuggestion | null>;
133
+ }
134
+ export declare class PostgresLivingDocMetadataStore implements LivingDocMetadataStore {
135
+ private readonly sql;
136
+ private readonly now;
137
+ constructor(sql: SqlClient, options?: ClockOptions);
138
+ create(input: CreateLivingDocInput): Promise<LivingDoc>;
139
+ getByOpaqueId(opaqueId: string): Promise<LivingDoc | null>;
140
+ getByReviewId(reviewId: string): Promise<LivingDoc | null>;
141
+ updateMarkdown(opaqueId: string, markdown: string): Promise<LivingDoc | null>;
142
+ setLatestVersionNumber(opaqueId: string, versionNumber: number): Promise<LivingDoc | null>;
143
+ markRemoved(opaqueId: string): Promise<LivingDoc | null>;
144
+ listByPublisherEmail(publisherEmail: string): Promise<LivingDoc[]>;
145
+ addVersion(input: CreateVersionInput): Promise<LivingDocVersion>;
146
+ getVersion(opaqueId: string, versionNumber: number): Promise<LivingDocVersion | null>;
147
+ addComment(input: CreateCommentInput): Promise<LivingDocComment>;
148
+ listComments(opaqueId: string, options?: {
149
+ status?: CommentStatus;
150
+ }): Promise<LivingDocComment[]>;
151
+ setCommentStatus(commentId: string, status: CommentStatus): Promise<LivingDocComment | null>;
152
+ addSuggestion(input: CreateSuggestionInput): Promise<LivingDocSuggestion>;
153
+ listSuggestions(opaqueId: string, options?: {
154
+ status?: SuggestionStatus;
155
+ }): Promise<LivingDocSuggestion[]>;
156
+ setSuggestionStatus(suggestionId: string, status: SuggestionStatus): Promise<LivingDocSuggestion | null>;
157
+ }
158
+ export declare function createNeonLivingDocMetadataStore(databaseUrl?: string): PostgresLivingDocMetadataStore;
159
+ //# sourceMappingURL=living-doc-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"living-doc-metadata.d.ts","sourceRoot":"","sources":["../../../src/storage/living-doc-metadata.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,400 @@
1
+ import { neon } from "@neondatabase/serverless";
2
+ export class InMemoryLivingDocMetadataStore {
3
+ docs = new Map();
4
+ versions = [];
5
+ comments = [];
6
+ suggestions = [];
7
+ now;
8
+ constructor(options = {}) {
9
+ this.now = options.now ?? (() => new Date());
10
+ }
11
+ async create(input) {
12
+ const timestamp = this.now();
13
+ const doc = {
14
+ opaqueId: input.opaqueId,
15
+ reviewId: input.reviewId,
16
+ publisherEmail: input.publisherEmail,
17
+ status: input.status ?? "active",
18
+ title: input.title ?? null,
19
+ currentMarkdown: input.currentMarkdown,
20
+ latestVersionNumber: 0,
21
+ createdAt: timestamp,
22
+ updatedAt: timestamp,
23
+ removedAt: null,
24
+ };
25
+ this.docs.set(doc.opaqueId, doc);
26
+ return cloneDoc(doc);
27
+ }
28
+ async getByOpaqueId(opaqueId) {
29
+ const doc = this.docs.get(opaqueId);
30
+ return doc ? cloneDoc(doc) : null;
31
+ }
32
+ async getByReviewId(reviewId) {
33
+ const doc = [...this.docs.values()].find((d) => d.reviewId === reviewId);
34
+ return doc ? cloneDoc(doc) : null;
35
+ }
36
+ async updateMarkdown(opaqueId, markdown) {
37
+ const doc = this.docs.get(opaqueId);
38
+ if (!doc)
39
+ return null;
40
+ const next = { ...doc, currentMarkdown: markdown, updatedAt: this.now() };
41
+ this.docs.set(opaqueId, next);
42
+ return cloneDoc(next);
43
+ }
44
+ async setLatestVersionNumber(opaqueId, versionNumber) {
45
+ const doc = this.docs.get(opaqueId);
46
+ if (!doc)
47
+ return null;
48
+ const next = {
49
+ ...doc,
50
+ latestVersionNumber: versionNumber,
51
+ updatedAt: this.now(),
52
+ };
53
+ this.docs.set(opaqueId, next);
54
+ return cloneDoc(next);
55
+ }
56
+ async markRemoved(opaqueId) {
57
+ const doc = this.docs.get(opaqueId);
58
+ if (!doc)
59
+ return null;
60
+ const timestamp = this.now();
61
+ const next = {
62
+ ...doc,
63
+ status: "removed",
64
+ removedAt: timestamp,
65
+ updatedAt: timestamp,
66
+ };
67
+ this.docs.set(opaqueId, next);
68
+ return cloneDoc(next);
69
+ }
70
+ async listByPublisherEmail(publisherEmail) {
71
+ return [...this.docs.values()]
72
+ .filter((doc) => doc.publisherEmail === publisherEmail)
73
+ .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
74
+ .map(cloneDoc);
75
+ }
76
+ async addVersion(input) {
77
+ const version = {
78
+ id: input.id,
79
+ livingDocId: input.livingDocId,
80
+ versionNumber: input.versionNumber,
81
+ markdown: input.markdown,
82
+ createdAt: this.now(),
83
+ };
84
+ this.versions.push(version);
85
+ return cloneVersion(version);
86
+ }
87
+ async getVersion(opaqueId, versionNumber) {
88
+ const version = this.versions.find((v) => v.livingDocId === opaqueId && v.versionNumber === versionNumber);
89
+ return version ? cloneVersion(version) : null;
90
+ }
91
+ async addComment(input) {
92
+ const comment = {
93
+ id: input.id,
94
+ livingDocId: input.livingDocId,
95
+ origin: input.origin,
96
+ author: input.author ?? null,
97
+ anchorQuote: input.anchorQuote,
98
+ anchorStart: input.anchorStart ?? null,
99
+ anchorEnd: input.anchorEnd ?? null,
100
+ body: input.body,
101
+ parentCommentId: input.parentCommentId ?? null,
102
+ status: "open",
103
+ createdAt: this.now(),
104
+ };
105
+ this.comments.push(comment);
106
+ return cloneComment(comment);
107
+ }
108
+ async listComments(opaqueId, options = {}) {
109
+ return this.comments
110
+ .filter((comment) => comment.livingDocId === opaqueId &&
111
+ (options.status ? comment.status === options.status : true))
112
+ .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
113
+ .map(cloneComment);
114
+ }
115
+ async setCommentStatus(commentId, status) {
116
+ const comment = this.comments.find((c) => c.id === commentId);
117
+ if (!comment)
118
+ return null;
119
+ comment.status = status;
120
+ return cloneComment(comment);
121
+ }
122
+ async addSuggestion(input) {
123
+ const suggestion = {
124
+ id: input.id,
125
+ livingDocId: input.livingDocId,
126
+ versionNumber: input.versionNumber,
127
+ anchorQuote: input.anchorQuote,
128
+ anchorStart: input.anchorStart ?? null,
129
+ anchorEnd: input.anchorEnd ?? null,
130
+ replacement: input.replacement,
131
+ note: input.note ?? null,
132
+ status: "pending",
133
+ createdAt: this.now(),
134
+ resolvedAt: null,
135
+ };
136
+ this.suggestions.push(suggestion);
137
+ return cloneSuggestion(suggestion);
138
+ }
139
+ async listSuggestions(opaqueId, options = {}) {
140
+ return this.suggestions
141
+ .filter((suggestion) => suggestion.livingDocId === opaqueId &&
142
+ (options.status ? suggestion.status === options.status : true))
143
+ .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
144
+ .map(cloneSuggestion);
145
+ }
146
+ async setSuggestionStatus(suggestionId, status) {
147
+ const suggestion = this.suggestions.find((s) => s.id === suggestionId);
148
+ if (!suggestion)
149
+ return null;
150
+ suggestion.status = status;
151
+ suggestion.resolvedAt = status === "pending" ? null : this.now();
152
+ return cloneSuggestion(suggestion);
153
+ }
154
+ }
155
+ export class PostgresLivingDocMetadataStore {
156
+ sql;
157
+ now;
158
+ constructor(sql, options = {}) {
159
+ this.sql = sql;
160
+ this.now = options.now ?? (() => new Date());
161
+ }
162
+ async create(input) {
163
+ const timestamp = this.now();
164
+ const result = await this.sql.query(`
165
+ insert into living_docs (
166
+ opaque_id, review_id, publisher_email, status, title,
167
+ current_markdown, latest_version_number, created_at, updated_at
168
+ ) values ($1, $2, $3, $4, $5, $6, 0, $7, $7)
169
+ returning *
170
+ `, [
171
+ input.opaqueId,
172
+ input.reviewId,
173
+ input.publisherEmail,
174
+ input.status ?? "active",
175
+ input.title ?? null,
176
+ input.currentMarkdown,
177
+ timestamp,
178
+ ]);
179
+ return mapDocRow(result.rows[0]);
180
+ }
181
+ async getByOpaqueId(opaqueId) {
182
+ const result = await this.sql.query("select * from living_docs where opaque_id = $1", [opaqueId]);
183
+ return result.rows[0] ? mapDocRow(result.rows[0]) : null;
184
+ }
185
+ async getByReviewId(reviewId) {
186
+ const result = await this.sql.query("select * from living_docs where review_id = $1", [reviewId]);
187
+ return result.rows[0] ? mapDocRow(result.rows[0]) : null;
188
+ }
189
+ async updateMarkdown(opaqueId, markdown) {
190
+ const result = await this.sql.query(`
191
+ update living_docs
192
+ set current_markdown = $2, updated_at = $3
193
+ where opaque_id = $1
194
+ returning *
195
+ `, [opaqueId, markdown, this.now()]);
196
+ return result.rows[0] ? mapDocRow(result.rows[0]) : null;
197
+ }
198
+ async setLatestVersionNumber(opaqueId, versionNumber) {
199
+ const result = await this.sql.query(`
200
+ update living_docs
201
+ set latest_version_number = $2, updated_at = $3
202
+ where opaque_id = $1
203
+ returning *
204
+ `, [opaqueId, versionNumber, this.now()]);
205
+ return result.rows[0] ? mapDocRow(result.rows[0]) : null;
206
+ }
207
+ async markRemoved(opaqueId) {
208
+ const timestamp = this.now();
209
+ const result = await this.sql.query(`
210
+ update living_docs
211
+ set status = 'removed', removed_at = $2, updated_at = $2
212
+ where opaque_id = $1
213
+ returning *
214
+ `, [opaqueId, timestamp]);
215
+ return result.rows[0] ? mapDocRow(result.rows[0]) : null;
216
+ }
217
+ async listByPublisherEmail(publisherEmail) {
218
+ const result = await this.sql.query("select * from living_docs where publisher_email = $1 order by updated_at desc", [publisherEmail]);
219
+ return result.rows.map(mapDocRow);
220
+ }
221
+ async addVersion(input) {
222
+ const result = await this.sql.query(`
223
+ insert into living_doc_versions (
224
+ id, living_doc_id, version_number, markdown, created_at
225
+ ) values ($1, $2, $3, $4, $5)
226
+ returning *
227
+ `, [
228
+ input.id,
229
+ input.livingDocId,
230
+ input.versionNumber,
231
+ input.markdown,
232
+ this.now(),
233
+ ]);
234
+ return mapVersionRow(result.rows[0]);
235
+ }
236
+ async getVersion(opaqueId, versionNumber) {
237
+ const result = await this.sql.query("select * from living_doc_versions where living_doc_id = $1 and version_number = $2", [opaqueId, versionNumber]);
238
+ return result.rows[0] ? mapVersionRow(result.rows[0]) : null;
239
+ }
240
+ async addComment(input) {
241
+ const result = await this.sql.query(`
242
+ insert into living_doc_comments (
243
+ id, living_doc_id, origin, author, anchor_quote,
244
+ anchor_start, anchor_end, body, parent_comment_id, status, created_at
245
+ ) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, 'open', $10)
246
+ returning *
247
+ `, [
248
+ input.id,
249
+ input.livingDocId,
250
+ input.origin,
251
+ input.author ?? null,
252
+ input.anchorQuote,
253
+ input.anchorStart ?? null,
254
+ input.anchorEnd ?? null,
255
+ input.body,
256
+ input.parentCommentId ?? null,
257
+ this.now(),
258
+ ]);
259
+ return mapCommentRow(result.rows[0]);
260
+ }
261
+ async listComments(opaqueId, options = {}) {
262
+ const result = options.status
263
+ ? await this.sql.query("select * from living_doc_comments where living_doc_id = $1 and status = $2 order by created_at", [opaqueId, options.status])
264
+ : await this.sql.query("select * from living_doc_comments where living_doc_id = $1 order by created_at", [opaqueId]);
265
+ return result.rows.map(mapCommentRow);
266
+ }
267
+ async setCommentStatus(commentId, status) {
268
+ const result = await this.sql.query("update living_doc_comments set status = $2 where id = $1 returning *", [commentId, status]);
269
+ return result.rows[0] ? mapCommentRow(result.rows[0]) : null;
270
+ }
271
+ async addSuggestion(input) {
272
+ const result = await this.sql.query(`
273
+ insert into living_doc_suggestions (
274
+ id, living_doc_id, version_number, anchor_quote, anchor_start,
275
+ anchor_end, replacement, note, status, created_at
276
+ ) values ($1, $2, $3, $4, $5, $6, $7, $8, 'pending', $9)
277
+ returning *
278
+ `, [
279
+ input.id,
280
+ input.livingDocId,
281
+ input.versionNumber,
282
+ input.anchorQuote,
283
+ input.anchorStart ?? null,
284
+ input.anchorEnd ?? null,
285
+ input.replacement,
286
+ input.note ?? null,
287
+ this.now(),
288
+ ]);
289
+ return mapSuggestionRow(result.rows[0]);
290
+ }
291
+ async listSuggestions(opaqueId, options = {}) {
292
+ const result = options.status
293
+ ? await this.sql.query("select * from living_doc_suggestions where living_doc_id = $1 and status = $2 order by created_at", [opaqueId, options.status])
294
+ : await this.sql.query("select * from living_doc_suggestions where living_doc_id = $1 order by created_at", [opaqueId]);
295
+ return result.rows.map(mapSuggestionRow);
296
+ }
297
+ async setSuggestionStatus(suggestionId, status) {
298
+ const resolvedAt = status === "pending" ? null : this.now();
299
+ const result = await this.sql.query("update living_doc_suggestions set status = $2, resolved_at = $3 where id = $1 returning *", [suggestionId, status, resolvedAt]);
300
+ return result.rows[0] ? mapSuggestionRow(result.rows[0]) : null;
301
+ }
302
+ }
303
+ export function createNeonLivingDocMetadataStore(databaseUrl = requiredEnv("DATABASE_URL")) {
304
+ const sql = neon(databaseUrl);
305
+ return new PostgresLivingDocMetadataStore({
306
+ async query(text, params = []) {
307
+ const rows = (await sql.query(text, params));
308
+ return { rows };
309
+ },
310
+ });
311
+ }
312
+ function mapDocRow(row) {
313
+ if (!row)
314
+ throw new Error("Expected Living Doc row");
315
+ return {
316
+ opaqueId: row.opaque_id,
317
+ reviewId: row.review_id,
318
+ publisherEmail: row.publisher_email,
319
+ status: row.status,
320
+ title: row.title ?? null,
321
+ currentMarkdown: row.current_markdown,
322
+ latestVersionNumber: Number(row.latest_version_number),
323
+ createdAt: new Date(row.created_at),
324
+ updatedAt: new Date(row.updated_at),
325
+ removedAt: row.removed_at ? new Date(row.removed_at) : null,
326
+ };
327
+ }
328
+ function mapVersionRow(row) {
329
+ if (!row)
330
+ throw new Error("Expected Living Doc Version row");
331
+ return {
332
+ id: row.id,
333
+ livingDocId: row.living_doc_id,
334
+ versionNumber: Number(row.version_number),
335
+ markdown: row.markdown,
336
+ createdAt: new Date(row.created_at),
337
+ };
338
+ }
339
+ function mapCommentRow(row) {
340
+ if (!row)
341
+ throw new Error("Expected Living Doc Comment row");
342
+ return {
343
+ id: row.id,
344
+ livingDocId: row.living_doc_id,
345
+ origin: row.origin,
346
+ author: row.author ?? null,
347
+ anchorQuote: row.anchor_quote,
348
+ anchorStart: row.anchor_start === null ? null : Number(row.anchor_start),
349
+ anchorEnd: row.anchor_end === null ? null : Number(row.anchor_end),
350
+ body: row.body,
351
+ parentCommentId: row.parent_comment_id ?? null,
352
+ status: row.status,
353
+ createdAt: new Date(row.created_at),
354
+ };
355
+ }
356
+ function mapSuggestionRow(row) {
357
+ if (!row)
358
+ throw new Error("Expected Living Doc Suggestion row");
359
+ return {
360
+ id: row.id,
361
+ livingDocId: row.living_doc_id,
362
+ versionNumber: Number(row.version_number),
363
+ anchorQuote: row.anchor_quote,
364
+ anchorStart: row.anchor_start === null ? null : Number(row.anchor_start),
365
+ anchorEnd: row.anchor_end === null ? null : Number(row.anchor_end),
366
+ replacement: row.replacement,
367
+ note: row.note ?? null,
368
+ status: row.status,
369
+ createdAt: new Date(row.created_at),
370
+ resolvedAt: row.resolved_at ? new Date(row.resolved_at) : null,
371
+ };
372
+ }
373
+ function cloneDoc(doc) {
374
+ return {
375
+ ...doc,
376
+ createdAt: new Date(doc.createdAt),
377
+ updatedAt: new Date(doc.updatedAt),
378
+ removedAt: doc.removedAt ? new Date(doc.removedAt) : null,
379
+ };
380
+ }
381
+ function cloneVersion(version) {
382
+ return { ...version, createdAt: new Date(version.createdAt) };
383
+ }
384
+ function cloneComment(comment) {
385
+ return { ...comment, createdAt: new Date(comment.createdAt) };
386
+ }
387
+ function cloneSuggestion(suggestion) {
388
+ return {
389
+ ...suggestion,
390
+ createdAt: new Date(suggestion.createdAt),
391
+ resolvedAt: suggestion.resolvedAt ? new Date(suggestion.resolvedAt) : null,
392
+ };
393
+ }
394
+ function requiredEnv(name) {
395
+ const value = process.env[name];
396
+ if (!value)
397
+ throw new Error(`${name} is required`);
398
+ return value;
399
+ }
400
+ //# sourceMappingURL=living-doc-metadata.js.map