@molecule/api-resource-comment 1.1.4 → 1.2.1

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 CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-17T20:52:27.537Z
6
+ Generated: 2026-10-20T08:23:35.474Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-resource-comment
@@ -16,7 +16,8 @@ replies, pagination, and ownership-based authorization.
16
16
  **Tables this package owns: `comments` and `comment_numbers`.** A comment row is found by its
17
17
  `comments.id` (a UUID); `comment_numbers` gives it the sequential integer many clients address it
18
18
  by, and the join between the two is `comment_numbers.commentId` = `comments.id` (declared FK,
19
- `ON DELETE CASCADE`) — one row per numbered comment, never a mapping table of your own.
19
+ `ON DELETE CASCADE`) — one row per comment created through this package, never a mapping table
20
+ of your own.
20
21
 
21
22
  > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
22
23
  > It is written to be read by coding agents as much as by people, and is generated from this
@@ -227,7 +228,9 @@ function update(req: MoleculeRequest, res: MoleculeResponse): Promise<void>
227
228
  #### `numberComment(commentId)`
228
229
 
229
230
  The comment's number, allocating the next one when it has none. Numbers are
230
- unique and increase.
231
+ unique and increase. Use it for a comment that was inserted into `comments`
232
+ without `createComment()` — every comment created through this package is
233
+ numbered already.
231
234
 
232
235
  ```typescript
233
236
  function numberComment(commentId: string): Promise<number>
@@ -241,6 +244,10 @@ function numberComment(commentId: string): Promise<number>
241
244
 
242
245
  Creates a comment and its number together: both are written, or neither.
243
246
 
247
+ This is `createComment()` under the name an integer-id API reads best — it
248
+ takes the same arguments and returns the same `NumberedComment`. Address the
249
+ result by `number` and read it back with `getCommentByNumber(number)`.
250
+
244
251
  ```typescript
245
252
  function createNumberedComment(
246
253
  resourceType: string,
@@ -259,7 +266,9 @@ function createNumberedComment(
259
266
 
260
267
  #### `getCommentByNumber(number)`
261
268
 
262
- The comment a number belongs to.
269
+ The comment a number belongs to — the lookup behind an integer-addressed
270
+ route such as `DELETE /comments/:id`: find the comment here, then pass its
271
+ `id` to `deleteComment()`/`updateComment()`.
263
272
 
264
273
  ```typescript
265
274
  function getCommentByNumber(number: number): Promise<NumberedComment | null>
@@ -272,7 +281,11 @@ function getCommentByNumber(number: number): Promise<NumberedComment | null>
272
281
  #### `commentNumbers(commentIds)`
273
282
 
274
283
  The numbers of the given comments, for listing them with their numbers.
275
- Comments without a number are left out of the map.
284
+
285
+ Every comment created through this package is numbered, so a comment missing
286
+ from the map was inserted directly into `comments` (or predates numbering by
287
+ default); give it one with `numberComment(id)`. Never read a missing entry as
288
+ `0` — `0` is not a comment id, and an integer id of `0` cannot be found again.
276
289
 
277
290
  ```typescript
278
291
  function commentNumbers(commentIds: readonly string[]): Promise<Map<string, number>>
@@ -284,7 +297,18 @@ function commentNumbers(commentIds: readonly string[]): Promise<Map<string, numb
284
297
 
285
298
  #### `createComment(resourceType, resourceId, userId, data)`
286
299
 
287
- Creates a new comment on a resource.
300
+ Creates a new comment on a resource, with its sequential number.
301
+
302
+ The comment row and its `comment_numbers` row are written together: the
303
+ returned comment carries `number` (1, 2, 3, …) beside its UUID `id`. An API
304
+ whose clients address a comment by an integer (RealWorld's `comment.id`,
305
+ `DELETE /comments/:id`) returns `number` as that id and reads it back with
306
+ `getCommentByNumber(number)` — never hand-build an id from the UUID.
307
+ `createNumberedComment()` is this same create under the name that path reads
308
+ best; `numberComment(id)` numbers a row that was inserted without this
309
+ function.
310
+
311
+ Both `__setup__` SQL files are required (`comments`, `comment_numbers`).
288
312
 
289
313
  ```typescript
290
314
  function createComment(
@@ -292,7 +316,7 @@ function createComment(
292
316
  resourceId: string,
293
317
  userId: string,
294
318
  data: CreateCommentInput,
295
- ): Promise<Comment>
319
+ ): Promise<NumberedComment>
296
320
  ```
297
321
 
298
322
  - `resourceType` — The type of resource being commented on.
@@ -300,13 +324,17 @@ function createComment(
300
324
  - `userId` — The ID of the commenting user.
301
325
  - `data` — The comment creation input.
302
326
 
303
- **Returns:** The created comment.
327
+ **Returns:** The created comment with its sequential `number`.
304
328
 
305
329
  #### `getCommentsByResource(resourceType, resourceId, options)`
306
330
 
307
331
  Retrieves paginated comments for a resource, ordered by creation date descending.
308
332
  Only returns top-level comments (no replies).
309
333
 
334
+ The rows carry each comment's UUID `id`; for an API that addresses comments by an
335
+ integer, map a page's ids with `commentNumbers()` — every comment `createComment()`
336
+ wrote is in that map, and a missing entry (a raw insert) is never `0`.
337
+
310
338
  ```typescript
311
339
  function getCommentsByResource(
312
340
  resourceType: string,
@@ -329,7 +357,7 @@ Retrieves a single comment by ID.
329
357
  function getCommentById(commentId: string): Promise<Comment | null>
330
358
  ```
331
359
 
332
- - `commentId` — The comment ID to look up.
360
+ - `commentId` — The comment's UUID. For the sequential integer an integer-addressed API uses, call `getCommentByNumber(number)` instead.
333
361
 
334
362
  **Returns:** The comment or `null` if not found.
335
363
 
@@ -345,7 +373,7 @@ function updateComment(
345
373
  ): Promise<Comment | null>
346
374
  ```
347
375
 
348
- - `commentId` — The comment ID to update.
376
+ - `commentId` — The comment's UUID. For an integer number, resolve it with `getCommentByNumber(number)` first.
349
377
  - `userId` — The requesting user's ID (must match comment owner).
350
378
  - `data` — The update input.
351
379
 
@@ -359,7 +387,7 @@ Deletes a comment. Only the comment owner can delete.
359
387
  function deleteComment(commentId: string, userId: string): Promise<boolean>
360
388
  ```
361
389
 
362
- - `commentId` — The comment ID to delete.
390
+ - `commentId` — The comment's UUID. To delete by the integer number an API like RealWorld exposes (`DELETE /comments/:id`), look the row up with `getCommentByNumber(number)` and pass its `id`.
363
391
  - `userId` — The requesting user's ID (must match comment owner).
364
392
 
365
393
  **Returns:** `true` if deleted, `false` if not found or unauthorized.
@@ -501,13 +529,17 @@ Peer dependencies:
501
529
  - **A scaffold that lists this package already has these tables** — its `__setup__` SQL is
502
530
  copied into the app at create time and replayed on every boot, so do not copy it into
503
531
  `api/migrations/` yourself; only a package added after the scaffold needs that.
504
- `src/__setup__/comments.sql` ships with this package and must exist in the target
505
- database before use (scaffolded apps apply it automatically; existing apps must apply it
506
- first).
507
- - **Integer comment ids (RealWorld's `comment.id`, most public API specs):** keep the
508
- UUID and give each comment a sequential number — `createNumberedComment()` creates both
509
- in one transaction, `getCommentByNumber(n)` finds it, `commentNumbers(ids)` maps a list.
510
- Apply `src/__setup__/comment_numbers.sql` too. Never build your own id-mapping table.
532
+ Both SQL files this package ships — `src/__setup__/comments.sql` and
533
+ `src/__setup__/comment_numbers.sql` — must exist in the target database before use
534
+ (scaffolded apps apply both automatically; existing apps must apply both first).
535
+ - **Integer comment ids — every comment is numbered by default.** `createComment()`
536
+ writes the comment and its sequential `number` (1, 2, 3, …) together and returns both;
537
+ `createNumberedComment()` is the same create under the name an integer-id API reads best.
538
+ For an API whose clients address a comment by an integer (RealWorld's `comment.id`,
539
+ `DELETE /comments/:id`), return `number` as the id and read it back with
540
+ `getCommentByNumber(n)` — never hand-build an id from the UUID, and never read a missing
541
+ `commentNumbers(ids)` entry as `0` (a row inserted without `createComment()` has no number;
542
+ `numberComment(id)` gives it one). Never build your own id-mapping table.
511
543
  - **Reads are PUBLIC by default.** `list`, `read`, `replies`, and `count` ship
512
544
  with no auth middleware (comment threads on public content). If the
513
545
  commented resources are private in your app, add an authorizer that checks
package/dist/index.d.ts CHANGED
@@ -7,7 +7,8 @@
7
7
  * **Tables this package owns: `comments` and `comment_numbers`.** A comment row is found by its
8
8
  * `comments.id` (a UUID); `comment_numbers` gives it the sequential integer many clients address it
9
9
  * by, and the join between the two is `comment_numbers.commentId` = `comments.id` (declared FK,
10
- * `ON DELETE CASCADE`) — one row per numbered comment, never a mapping table of your own.
10
+ * `ON DELETE CASCADE`) — one row per comment created through this package, never a mapping table
11
+ * of your own.
11
12
  *
12
13
  * @module
13
14
  * @example
@@ -33,13 +34,17 @@
33
34
  * - **A scaffold that lists this package already has these tables** — its `__setup__` SQL is
34
35
  * copied into the app at create time and replayed on every boot, so do not copy it into
35
36
  * `api/migrations/` yourself; only a package added after the scaffold needs that.
36
- * `src/__setup__/comments.sql` ships with this package and must exist in the target
37
- * database before use (scaffolded apps apply it automatically; existing apps must apply it
38
- * first).
39
- * - **Integer comment ids (RealWorld's `comment.id`, most public API specs):** keep the
40
- * UUID and give each comment a sequential number — `createNumberedComment()` creates both
41
- * in one transaction, `getCommentByNumber(n)` finds it, `commentNumbers(ids)` maps a list.
42
- * Apply `src/__setup__/comment_numbers.sql` too. Never build your own id-mapping table.
37
+ * Both SQL files this package ships — `src/__setup__/comments.sql` and
38
+ * `src/__setup__/comment_numbers.sql` — must exist in the target database before use
39
+ * (scaffolded apps apply both automatically; existing apps must apply both first).
40
+ * - **Integer comment ids — every comment is numbered by default.** `createComment()`
41
+ * writes the comment and its sequential `number` (1, 2, 3, …) together and returns both;
42
+ * `createNumberedComment()` is the same create under the name an integer-id API reads best.
43
+ * For an API whose clients address a comment by an integer (RealWorld's `comment.id`,
44
+ * `DELETE /comments/:id`), return `number` as the id and read it back with
45
+ * `getCommentByNumber(n)` — never hand-build an id from the UUID, and never read a missing
46
+ * `commentNumbers(ids)` entry as `0` (a row inserted without `createComment()` has no number;
47
+ * `numberComment(id)` gives it one). Never build your own id-mapping table.
43
48
  * - **Reads are PUBLIC by default.** `list`, `read`, `replies`, and `count` ship
44
49
  * with no auth middleware (comment threads on public content). If the
45
50
  * commented resources are private in your app, add an authorizer that checks
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AAEH,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA"}
package/dist/index.js CHANGED
@@ -7,7 +7,8 @@
7
7
  * **Tables this package owns: `comments` and `comment_numbers`.** A comment row is found by its
8
8
  * `comments.id` (a UUID); `comment_numbers` gives it the sequential integer many clients address it
9
9
  * by, and the join between the two is `comment_numbers.commentId` = `comments.id` (declared FK,
10
- * `ON DELETE CASCADE`) — one row per numbered comment, never a mapping table of your own.
10
+ * `ON DELETE CASCADE`) — one row per comment created through this package, never a mapping table
11
+ * of your own.
11
12
  *
12
13
  * @module
13
14
  * @example
@@ -33,13 +34,17 @@
33
34
  * - **A scaffold that lists this package already has these tables** — its `__setup__` SQL is
34
35
  * copied into the app at create time and replayed on every boot, so do not copy it into
35
36
  * `api/migrations/` yourself; only a package added after the scaffold needs that.
36
- * `src/__setup__/comments.sql` ships with this package and must exist in the target
37
- * database before use (scaffolded apps apply it automatically; existing apps must apply it
38
- * first).
39
- * - **Integer comment ids (RealWorld's `comment.id`, most public API specs):** keep the
40
- * UUID and give each comment a sequential number — `createNumberedComment()` creates both
41
- * in one transaction, `getCommentByNumber(n)` finds it, `commentNumbers(ids)` maps a list.
42
- * Apply `src/__setup__/comment_numbers.sql` too. Never build your own id-mapping table.
37
+ * Both SQL files this package ships — `src/__setup__/comments.sql` and
38
+ * `src/__setup__/comment_numbers.sql` — must exist in the target database before use
39
+ * (scaffolded apps apply both automatically; existing apps must apply both first).
40
+ * - **Integer comment ids — every comment is numbered by default.** `createComment()`
41
+ * writes the comment and its sequential `number` (1, 2, 3, …) together and returns both;
42
+ * `createNumberedComment()` is the same create under the name an integer-id API reads best.
43
+ * For an API whose clients address a comment by an integer (RealWorld's `comment.id`,
44
+ * `DELETE /comments/:id`), return `number` as the id and read it back with
45
+ * `getCommentByNumber(n)` — never hand-build an id from the UUID, and never read a missing
46
+ * `commentNumbers(ids)` entry as `0` (a row inserted without `createComment()` has no number;
47
+ * `numberComment(id)` gives it one). Never build your own id-mapping table.
43
48
  * - **Reads are PUBLIC by default.** `list`, `read`, `replies`, and `count` ship
44
49
  * with no auth middleware (comment threads on public content). If the
45
50
  * commented resources are private in your app, add an authorizer that checks
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Number allocation for comments — the `comment_numbers` rows behind
3
+ * `NumberedComment.number`.
4
+ *
5
+ * Internal to this package: `createComment()` calls `allocateCommentNumber()`
6
+ * so every comment it writes has a number, and `numbers.ts` re-exposes the
7
+ * public surface (`numberComment`, `commentNumbers`, …). The allocation needs
8
+ * no interactive transaction — the unique key on `number` is what makes a
9
+ * concurrent writer lose, and the next attempt reads the new highest number —
10
+ * so it also works on a database bond without transactions (Cloudflare D1).
11
+ *
12
+ * @module
13
+ */
14
+ /**
15
+ * The comment's number, allocating the next one when it has none. Numbers are
16
+ * unique and increase. A concurrent writer that took the number first fails
17
+ * the insert on the unique key, and the next attempt reads the highest number
18
+ * again. Inside a caller's transaction the work runs once — a failed statement
19
+ * can poison it (Postgres) — and the caller decides.
20
+ *
21
+ * @param commentId - The comment's UUID.
22
+ * @returns Its sequential number.
23
+ * @throws {Error} When no number could be allocated after several attempts.
24
+ */
25
+ export declare function allocateCommentNumber(commentId: string): Promise<number>;
26
+ /**
27
+ * The numbers of the given comments, in one query.
28
+ *
29
+ * Every comment this package creates has a number, so a comment missing from
30
+ * the map was inserted into `comments` without `createComment()` (a raw insert,
31
+ * or a row from before this package numbered by default). Such a row has NO
32
+ * number — `numberComment(id)` gives it one. Never read a missing entry as `0`:
33
+ * `0` is not a comment id.
34
+ *
35
+ * @param commentIds - The comments' UUIDs.
36
+ * @returns Each numbered comment's UUID mapped to its number.
37
+ */
38
+ export declare function numbersForComments(commentIds: readonly string[]): Promise<Map<string, number>>;
39
+ //# sourceMappingURL=numbering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numbering.d.ts","sourceRoot":"","sources":["../src/numbering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAqCH;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW9E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAM9B"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Number allocation for comments — the `comment_numbers` rows behind
3
+ * `NumberedComment.number`.
4
+ *
5
+ * Internal to this package: `createComment()` calls `allocateCommentNumber()`
6
+ * so every comment it writes has a number, and `numbers.ts` re-exposes the
7
+ * public surface (`numberComment`, `commentNumbers`, …). The allocation needs
8
+ * no interactive transaction — the unique key on `number` is what makes a
9
+ * concurrent writer lose, and the next attempt reads the new highest number —
10
+ * so it also works on a database bond without transactions (Cloudflare D1).
11
+ *
12
+ * @module
13
+ */
14
+ import { create as dbCreate, findMany, findOne, getActiveTransaction } from '@molecule/api-database';
15
+ /** The table the numbers live in. */
16
+ const NUMBERS = 'comment_numbers';
17
+ /** How many times allocation is redone when a concurrent writer took the number first. */
18
+ const MAX_ATTEMPTS = 5;
19
+ /**
20
+ * Allocates the next number for a comment that has none. Each call is one
21
+ * read and, when the comment is unnumbered, one insert.
22
+ *
23
+ * @param commentId - The comment's UUID.
24
+ * @returns Its number.
25
+ */
26
+ async function allocate(commentId) {
27
+ const existing = await findOne(NUMBERS, [
28
+ { field: 'commentId', operator: '=', value: commentId },
29
+ ]);
30
+ if (existing)
31
+ return Number(existing.number);
32
+ const [last] = await findMany(NUMBERS, {
33
+ orderBy: [{ field: 'number', direction: 'desc' }],
34
+ limit: 1,
35
+ });
36
+ const number = (last ? Number(last.number) : 0) + 1;
37
+ await dbCreate(NUMBERS, { number, commentId });
38
+ return number;
39
+ }
40
+ /**
41
+ * The comment's number, allocating the next one when it has none. Numbers are
42
+ * unique and increase. A concurrent writer that took the number first fails
43
+ * the insert on the unique key, and the next attempt reads the highest number
44
+ * again. Inside a caller's transaction the work runs once — a failed statement
45
+ * can poison it (Postgres) — and the caller decides.
46
+ *
47
+ * @param commentId - The comment's UUID.
48
+ * @returns Its sequential number.
49
+ * @throws {Error} When no number could be allocated after several attempts.
50
+ */
51
+ export async function allocateCommentNumber(commentId) {
52
+ if (getActiveTransaction())
53
+ return allocate(commentId);
54
+ for (let attempt = 1;; attempt++) {
55
+ try {
56
+ return await allocate(commentId);
57
+ }
58
+ catch (error) {
59
+ if (attempt >= MAX_ATTEMPTS) {
60
+ throw new Error(`Could not number comment ${commentId}`, { cause: error });
61
+ }
62
+ }
63
+ }
64
+ }
65
+ /**
66
+ * The numbers of the given comments, in one query.
67
+ *
68
+ * Every comment this package creates has a number, so a comment missing from
69
+ * the map was inserted into `comments` without `createComment()` (a raw insert,
70
+ * or a row from before this package numbered by default). Such a row has NO
71
+ * number — `numberComment(id)` gives it one. Never read a missing entry as `0`:
72
+ * `0` is not a comment id.
73
+ *
74
+ * @param commentIds - The comments' UUIDs.
75
+ * @returns Each numbered comment's UUID mapped to its number.
76
+ */
77
+ export async function numbersForComments(commentIds) {
78
+ if (commentIds.length === 0)
79
+ return new Map();
80
+ const rows = await findMany(NUMBERS, {
81
+ where: [{ field: 'commentId', operator: 'in', value: [...commentIds] }],
82
+ });
83
+ return new Map(rows.map((r) => [r.commentId, Number(r.number)]));
84
+ }
package/dist/numbers.d.ts CHANGED
@@ -4,6 +4,13 @@
4
4
  * API specs). The comment keeps its UUID; the number lives in
5
5
  * `comment_numbers` and is allocated once, at most one per comment.
6
6
  *
7
+ * **Numbering is the default create path:** `createComment()` writes the
8
+ * comment and its number together, so every comment this package creates is
9
+ * numbered, and a numbered API never has to remember a second call.
10
+ * `createNumberedComment()` is the same create, kept as the name that path
11
+ * reads best. `numberComment(id)` numbers a row that was inserted without it
12
+ * (a raw insert, or a row from before numbering was the default).
13
+ *
7
14
  * @module
8
15
  */
9
16
  import type { Comment, CreateCommentInput } from './types.js';
@@ -14,7 +21,9 @@ export type NumberedComment = Comment & {
14
21
  };
15
22
  /**
16
23
  * The comment's number, allocating the next one when it has none. Numbers are
17
- * unique and increase.
24
+ * unique and increase. Use it for a comment that was inserted into `comments`
25
+ * without `createComment()` — every comment created through this package is
26
+ * numbered already.
18
27
  *
19
28
  * @param commentId - The comment's UUID.
20
29
  * @returns Its sequential number.
@@ -24,6 +33,10 @@ export declare function numberComment(commentId: string): Promise<number>;
24
33
  /**
25
34
  * Creates a comment and its number together: both are written, or neither.
26
35
  *
36
+ * This is `createComment()` under the name an integer-id API reads best — it
37
+ * takes the same arguments and returns the same `NumberedComment`. Address the
38
+ * result by `number` and read it back with `getCommentByNumber(number)`.
39
+ *
27
40
  * @param resourceType - The type of resource being commented on.
28
41
  * @param resourceId - The ID of the resource being commented on.
29
42
  * @param userId - The ID of the commenting user.
@@ -32,7 +45,9 @@ export declare function numberComment(commentId: string): Promise<number>;
32
45
  */
33
46
  export declare function createNumberedComment(resourceType: string, resourceId: string, userId: string, data: CreateCommentInput): Promise<NumberedComment>;
34
47
  /**
35
- * The comment a number belongs to.
48
+ * The comment a number belongs to — the lookup behind an integer-addressed
49
+ * route such as `DELETE /comments/:id`: find the comment here, then pass its
50
+ * `id` to `deleteComment()`/`updateComment()`.
36
51
  *
37
52
  * @param number - The comment's sequential number.
38
53
  * @returns The comment with its number, or `null` when no comment has it.
@@ -40,7 +55,11 @@ export declare function createNumberedComment(resourceType: string, resourceId:
40
55
  export declare function getCommentByNumber(number: number): Promise<NumberedComment | null>;
41
56
  /**
42
57
  * The numbers of the given comments, for listing them with their numbers.
43
- * Comments without a number are left out of the map.
58
+ *
59
+ * Every comment created through this package is numbered, so a comment missing
60
+ * from the map was inserted directly into `comments` (or predates numbering by
61
+ * default); give it one with `numberComment(id)`. Never read a missing entry as
62
+ * `0` — `0` is not a comment id, and an integer id of `0` cannot be found again.
44
63
  *
45
64
  * @param commentIds - The comments' UUIDs.
46
65
  * @returns Each numbered comment's UUID mapped to its number.
@@ -1 +1 @@
1
- {"version":3,"file":"numbers.d.ts","sourceRoot":"","sources":["../src/numbers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAI7D,qDAAqD;AACrD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG;IACtC,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAuDD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,eAAe,CAAC,CAK1B;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQxF;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAMhG"}
1
+ {"version":3,"file":"numbers.d.ts","sourceRoot":"","sources":["../src/numbers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAK7D,qDAAqD;AACrD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG;IACtC,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAQD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQxF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAEhG"}
package/dist/numbers.js CHANGED
@@ -4,73 +4,40 @@
4
4
  * API specs). The comment keeps its UUID; the number lives in
5
5
  * `comment_numbers` and is allocated once, at most one per comment.
6
6
  *
7
+ * **Numbering is the default create path:** `createComment()` writes the
8
+ * comment and its number together, so every comment this package creates is
9
+ * numbered, and a numbered API never has to remember a second call.
10
+ * `createNumberedComment()` is the same create, kept as the name that path
11
+ * reads best. `numberComment(id)` numbers a row that was inserted without it
12
+ * (a raw insert, or a row from before numbering was the default).
13
+ *
7
14
  * @module
8
15
  */
9
- import { create as dbCreate, findMany, findOne, getActiveTransaction, withTransaction, } from '@molecule/api-database';
16
+ import { findOne } from '@molecule/api-database';
17
+ import { allocateCommentNumber, numbersForComments } from './numbering.js';
10
18
  import { createComment, getCommentById } from './service.js';
19
+ /** The table the numbers live in. */
11
20
  const NUMBERS = 'comment_numbers';
12
- /** How many times the work is redone when a concurrent writer took the number first. */
13
- const MAX_ATTEMPTS = 5;
14
- /**
15
- * Runs `work` in its own transaction, again when it fails: a concurrent
16
- * writer that took the same number fails the unique key, and the next
17
- * attempt allocates the next number. Inside a caller's transaction a failed
18
- * statement can poison it (Postgres), so there the work runs once and the
19
- * caller decides.
20
- *
21
- * @param what - What is being numbered, for the error.
22
- * @param work - The allocation.
23
- * @returns What `work` resolves to.
24
- */
25
- async function withNumberRetry(what, work) {
26
- if (getActiveTransaction())
27
- return work();
28
- for (let attempt = 1;; attempt++) {
29
- try {
30
- return await withTransaction(work);
31
- }
32
- catch (error) {
33
- if (attempt >= MAX_ATTEMPTS)
34
- throw new Error(`Could not number ${what}`, { cause: error });
35
- // Rolled back; a concurrent writer most likely took the number (its
36
- // unique key failed). The next attempt reads the new highest number.
37
- }
38
- }
39
- }
40
- /**
41
- * Allocates the next number for a comment that has none (no transaction of its own).
42
- *
43
- * @param commentId - The comment's UUID.
44
- * @returns Its number.
45
- */
46
- async function allocate(commentId) {
47
- const existing = await findOne(NUMBERS, [
48
- { field: 'commentId', operator: '=', value: commentId },
49
- ]);
50
- if (existing)
51
- return Number(existing.number);
52
- const [last] = await findMany(NUMBERS, {
53
- orderBy: [{ field: 'number', direction: 'desc' }],
54
- limit: 1,
55
- });
56
- const number = (last ? Number(last.number) : 0) + 1;
57
- await dbCreate(NUMBERS, { number, commentId });
58
- return number;
59
- }
60
21
  /**
61
22
  * The comment's number, allocating the next one when it has none. Numbers are
62
- * unique and increase.
23
+ * unique and increase. Use it for a comment that was inserted into `comments`
24
+ * without `createComment()` — every comment created through this package is
25
+ * numbered already.
63
26
  *
64
27
  * @param commentId - The comment's UUID.
65
28
  * @returns Its sequential number.
66
29
  * @throws {Error} When no number could be allocated after several attempts.
67
30
  */
68
31
  export async function numberComment(commentId) {
69
- return withNumberRetry(`comment ${commentId}`, () => allocate(commentId));
32
+ return allocateCommentNumber(commentId);
70
33
  }
71
34
  /**
72
35
  * Creates a comment and its number together: both are written, or neither.
73
36
  *
37
+ * This is `createComment()` under the name an integer-id API reads best — it
38
+ * takes the same arguments and returns the same `NumberedComment`. Address the
39
+ * result by `number` and read it back with `getCommentByNumber(number)`.
40
+ *
74
41
  * @param resourceType - The type of resource being commented on.
75
42
  * @param resourceId - The ID of the resource being commented on.
76
43
  * @param userId - The ID of the commenting user.
@@ -78,13 +45,12 @@ export async function numberComment(commentId) {
78
45
  * @returns The created comment with its number.
79
46
  */
80
47
  export async function createNumberedComment(resourceType, resourceId, userId, data) {
81
- return withNumberRetry('a new comment', async () => {
82
- const comment = await createComment(resourceType, resourceId, userId, data);
83
- return { ...comment, number: await allocate(comment.id) };
84
- });
48
+ return createComment(resourceType, resourceId, userId, data);
85
49
  }
86
50
  /**
87
- * The comment a number belongs to.
51
+ * The comment a number belongs to — the lookup behind an integer-addressed
52
+ * route such as `DELETE /comments/:id`: find the comment here, then pass its
53
+ * `id` to `deleteComment()`/`updateComment()`.
88
54
  *
89
55
  * @param number - The comment's sequential number.
90
56
  * @returns The comment with its number, or `null` when no comment has it.
@@ -102,16 +68,15 @@ export async function getCommentByNumber(number) {
102
68
  }
103
69
  /**
104
70
  * The numbers of the given comments, for listing them with their numbers.
105
- * Comments without a number are left out of the map.
71
+ *
72
+ * Every comment created through this package is numbered, so a comment missing
73
+ * from the map was inserted directly into `comments` (or predates numbering by
74
+ * default); give it one with `numberComment(id)`. Never read a missing entry as
75
+ * `0` — `0` is not a comment id, and an integer id of `0` cannot be found again.
106
76
  *
107
77
  * @param commentIds - The comments' UUIDs.
108
78
  * @returns Each numbered comment's UUID mapped to its number.
109
79
  */
110
80
  export async function commentNumbers(commentIds) {
111
- if (commentIds.length === 0)
112
- return new Map();
113
- const rows = await findMany(NUMBERS, {
114
- where: [{ field: 'commentId', operator: 'in', value: [...commentIds] }],
115
- });
116
- return new Map(rows.map((r) => [r.commentId, Number(r.number)]));
81
+ return numbersForComments(commentIds);
117
82
  }
package/dist/service.d.ts CHANGED
@@ -6,21 +6,39 @@
6
6
  *
7
7
  * @module
8
8
  */
9
+ import type { NumberedComment } from './numbers.js';
9
10
  import type { Comment, CreateCommentInput, PaginatedResult, PaginationOptions, UpdateCommentInput } from './types.js';
10
11
  /**
11
- * Creates a new comment on a resource.
12
+ * Creates a new comment on a resource, with its sequential number.
13
+ *
14
+ * The comment row and its `comment_numbers` row are written together: the
15
+ * returned comment carries `number` (1, 2, 3, …) beside its UUID `id`. An API
16
+ * whose clients address a comment by an integer (RealWorld's `comment.id`,
17
+ * `DELETE /comments/:id`) returns `number` as that id and reads it back with
18
+ * `getCommentByNumber(number)` — never hand-build an id from the UUID.
19
+ * `createNumberedComment()` is this same create under the name that path reads
20
+ * best; `numberComment(id)` numbers a row that was inserted without this
21
+ * function.
22
+ *
23
+ * Both `__setup__` SQL files are required (`comments`, `comment_numbers`).
12
24
  *
13
25
  * @param resourceType - The type of resource being commented on.
14
26
  * @param resourceId - The ID of the resource being commented on.
15
27
  * @param userId - The ID of the commenting user.
16
28
  * @param data - The comment creation input.
17
- * @returns The created comment.
29
+ * @returns The created comment with its sequential `number`.
30
+ * @throws {Error} When the comment could not be numbered; the comment row is
31
+ * removed again so the caller is not told a create failed while it exists.
18
32
  */
19
- export declare function createComment(resourceType: string, resourceId: string, userId: string, data: CreateCommentInput): Promise<Comment>;
33
+ export declare function createComment(resourceType: string, resourceId: string, userId: string, data: CreateCommentInput): Promise<NumberedComment>;
20
34
  /**
21
35
  * Retrieves paginated comments for a resource, ordered by creation date descending.
22
36
  * Only returns top-level comments (no replies).
23
37
  *
38
+ * The rows carry each comment's UUID `id`; for an API that addresses comments by an
39
+ * integer, map a page's ids with `commentNumbers()` — every comment `createComment()`
40
+ * wrote is in that map, and a missing entry (a raw insert) is never `0`.
41
+ *
24
42
  * @param resourceType - The type of resource.
25
43
  * @param resourceId - The ID of the resource.
26
44
  * @param options - Pagination options.
@@ -30,14 +48,16 @@ export declare function getCommentsByResource(resourceType: string, resourceId:
30
48
  /**
31
49
  * Retrieves a single comment by ID.
32
50
  *
33
- * @param commentId - The comment ID to look up.
51
+ * @param commentId - The comment's UUID. For the sequential integer an
52
+ * integer-addressed API uses, call `getCommentByNumber(number)` instead.
34
53
  * @returns The comment or `null` if not found.
35
54
  */
36
55
  export declare function getCommentById(commentId: string): Promise<Comment | null>;
37
56
  /**
38
57
  * Updates a comment. Only the comment owner can update.
39
58
  *
40
- * @param commentId - The comment ID to update.
59
+ * @param commentId - The comment's UUID. For an integer number, resolve it with
60
+ * `getCommentByNumber(number)` first.
41
61
  * @param userId - The requesting user's ID (must match comment owner).
42
62
  * @param data - The update input.
43
63
  * @returns The updated comment or `null` if not found or unauthorized.
@@ -46,7 +66,9 @@ export declare function updateComment(commentId: string, userId: string, data: U
46
66
  /**
47
67
  * Deletes a comment. Only the comment owner can delete.
48
68
  *
49
- * @param commentId - The comment ID to delete.
69
+ * @param commentId - The comment's UUID. To delete by the integer number an
70
+ * API like RealWorld exposes (`DELETE /comments/:id`), look the row up with
71
+ * `getCommentByNumber(number)` and pass its `id`.
50
72
  * @param userId - The requesting user's ID (must match comment owner).
51
73
  * @returns `true` if deleted, `false` if not found or unauthorized.
52
74
  */
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,OAAO,KAAK,EACV,OAAO,EACP,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAInB;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAqBnC;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAE/E;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAczB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMvF;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAiBnC;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK/F"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,KAAK,EACV,OAAO,EACP,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAInB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,eAAe,CAAC,CAyB1B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAqBnC;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAE/E;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAczB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMvF;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAiBnC;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK/F"}
package/dist/service.js CHANGED
@@ -7,15 +7,29 @@
7
7
  * @module
8
8
  */
9
9
  import { count, create as dbCreate, deleteById, findById, findMany, updateById, } from '@molecule/api-database';
10
+ import { allocateCommentNumber } from './numbering.js';
10
11
  const TABLE = 'comments';
11
12
  /**
12
- * Creates a new comment on a resource.
13
+ * Creates a new comment on a resource, with its sequential number.
14
+ *
15
+ * The comment row and its `comment_numbers` row are written together: the
16
+ * returned comment carries `number` (1, 2, 3, …) beside its UUID `id`. An API
17
+ * whose clients address a comment by an integer (RealWorld's `comment.id`,
18
+ * `DELETE /comments/:id`) returns `number` as that id and reads it back with
19
+ * `getCommentByNumber(number)` — never hand-build an id from the UUID.
20
+ * `createNumberedComment()` is this same create under the name that path reads
21
+ * best; `numberComment(id)` numbers a row that was inserted without this
22
+ * function.
23
+ *
24
+ * Both `__setup__` SQL files are required (`comments`, `comment_numbers`).
13
25
  *
14
26
  * @param resourceType - The type of resource being commented on.
15
27
  * @param resourceId - The ID of the resource being commented on.
16
28
  * @param userId - The ID of the commenting user.
17
29
  * @param data - The comment creation input.
18
- * @returns The created comment.
30
+ * @returns The created comment with its sequential `number`.
31
+ * @throws {Error} When the comment could not be numbered; the comment row is
32
+ * removed again so the caller is not told a create failed while it exists.
19
33
  */
20
34
  export async function createComment(resourceType, resourceId, userId, data) {
21
35
  const result = await dbCreate(TABLE, {
@@ -26,12 +40,29 @@ export async function createComment(resourceType, resourceId, userId, data) {
26
40
  body: data.body,
27
41
  editedAt: null,
28
42
  });
29
- return result.data;
43
+ const comment = result.data;
44
+ try {
45
+ return { ...comment, number: await allocateCommentNumber(comment.id) };
46
+ }
47
+ catch (error) {
48
+ // The caller is told the create failed, so the row must not stay behind.
49
+ try {
50
+ await deleteById(TABLE, comment.id);
51
+ }
52
+ catch (cleanupError) {
53
+ throw new AggregateError([error, cleanupError], `Could not number comment ${comment.id}, and could not remove the unnumbered row`, { cause: cleanupError });
54
+ }
55
+ throw error;
56
+ }
30
57
  }
31
58
  /**
32
59
  * Retrieves paginated comments for a resource, ordered by creation date descending.
33
60
  * Only returns top-level comments (no replies).
34
61
  *
62
+ * The rows carry each comment's UUID `id`; for an API that addresses comments by an
63
+ * integer, map a page's ids with `commentNumbers()` — every comment `createComment()`
64
+ * wrote is in that map, and a missing entry (a raw insert) is never `0`.
65
+ *
35
66
  * @param resourceType - The type of resource.
36
67
  * @param resourceId - The ID of the resource.
37
68
  * @param options - Pagination options.
@@ -59,7 +90,8 @@ export async function getCommentsByResource(resourceType, resourceId, options =
59
90
  /**
60
91
  * Retrieves a single comment by ID.
61
92
  *
62
- * @param commentId - The comment ID to look up.
93
+ * @param commentId - The comment's UUID. For the sequential integer an
94
+ * integer-addressed API uses, call `getCommentByNumber(number)` instead.
63
95
  * @returns The comment or `null` if not found.
64
96
  */
65
97
  export async function getCommentById(commentId) {
@@ -68,7 +100,8 @@ export async function getCommentById(commentId) {
68
100
  /**
69
101
  * Updates a comment. Only the comment owner can update.
70
102
  *
71
- * @param commentId - The comment ID to update.
103
+ * @param commentId - The comment's UUID. For an integer number, resolve it with
104
+ * `getCommentByNumber(number)` first.
72
105
  * @param userId - The requesting user's ID (must match comment owner).
73
106
  * @param data - The update input.
74
107
  * @returns The updated comment or `null` if not found or unauthorized.
@@ -89,7 +122,9 @@ export async function updateComment(commentId, userId, data) {
89
122
  /**
90
123
  * Deletes a comment. Only the comment owner can delete.
91
124
  *
92
- * @param commentId - The comment ID to delete.
125
+ * @param commentId - The comment's UUID. To delete by the integer number an
126
+ * API like RealWorld exposes (`DELETE /comments/:id`), look the row up with
127
+ * `getCommentByNumber(number)` and pass its `id`.
93
128
  * @param userId - The requesting user's ID (must match comment owner).
94
129
  * @returns `true` if deleted, `false` if not found or unauthorized.
95
130
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-resource-comment",
3
- "version": "1.1.4",
3
+ "version": "1.2.1",
4
4
  "description": "Threaded comments on any resource with polymorphic attachment and pagination",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "license": "Apache-2.0",
31
31
  "devDependencies": {
32
32
  "@molecule/api-database": "1.1.3",
33
- "@molecule/api-database-sqlite": "1.2.6",
33
+ "@molecule/api-database-sqlite": "1.3.0",
34
34
  "@types/node": "26.1.2",
35
35
  "typescript": "6.0.3",
36
36
  "vitest": "4.1.11",
@@ -1,7 +1,8 @@
1
1
  -- Sequential integer numbers for comments, for APIs whose clients address a
2
- -- comment by an integer id (RealWorld's `comment.id` is an integer). Opt-in:
3
- -- written by numberComment() / createNumberedComment(); comments without a
4
- -- number keep working by their UUID.
2
+ -- comment by an integer id (RealWorld's `comment.id` is an integer).
3
+ -- createComment() / createNumberedComment() write this row with the comment;
4
+ -- numberComment(id) numbers a row that was inserted without them. The comment
5
+ -- keeps its UUID either way.
5
6
  CREATE TABLE IF NOT EXISTS "comment_numbers" (
6
7
  "number" INTEGER PRIMARY KEY,
7
8
  "commentId" UUID NOT NULL UNIQUE REFERENCES "comments"("id") ON DELETE CASCADE