convex-feedback 0.1.0 → 0.1.2

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.
Files changed (36) hide show
  1. package/README.md +351 -92
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/client/api.d.ts +267 -68
  4. package/dist/client/api.d.ts.map +1 -1
  5. package/dist/client/config.d.ts +168 -29
  6. package/dist/client/config.d.ts.map +1 -1
  7. package/dist/client/config.js.map +1 -1
  8. package/dist/client/index.d.ts +167 -172
  9. package/dist/client/index.d.ts.map +1 -1
  10. package/dist/client/index.js +75 -14
  11. package/dist/client/index.js.map +1 -1
  12. package/dist/component/_generated/component.d.ts +2 -2
  13. package/dist/component/_generated/component.d.ts.map +1 -1
  14. package/dist/component/comments.d.ts +12 -12
  15. package/dist/component/comments.d.ts.map +1 -1
  16. package/dist/component/comments.js +43 -60
  17. package/dist/component/comments.js.map +1 -1
  18. package/dist/component/entries.d.ts +30 -30
  19. package/dist/component/entries.d.ts.map +1 -1
  20. package/dist/component/entries.js +216 -97
  21. package/dist/component/entries.js.map +1 -1
  22. package/dist/component/model.d.ts +151 -0
  23. package/dist/component/model.d.ts.map +1 -1
  24. package/dist/react/index.d.ts +178 -56
  25. package/dist/react/index.d.ts.map +1 -1
  26. package/dist/react/index.js +70 -4
  27. package/dist/react/index.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/client/api.ts +326 -36
  30. package/src/client/config.ts +191 -29
  31. package/src/client/index.ts +437 -16
  32. package/src/component/_generated/component.ts +2 -2
  33. package/src/component/comments.ts +43 -61
  34. package/src/component/entries.ts +300 -123
  35. package/src/component/model.ts +158 -0
  36. package/src/react/index.ts +217 -9
package/src/client/api.ts CHANGED
@@ -14,113 +14,403 @@ import type {
14
14
  SimilarEntriesResult,
15
15
  } from "../component/model.js";
16
16
 
17
+ /**
18
+ * Arguments for cursor-paginated entry listing.
19
+ */
20
+ export type ListEntriesArgs = {
21
+ /**
22
+ * Convex cursor-pagination options.
23
+ *
24
+ * React consumers normally do not construct this directly; `useEntries`
25
+ * manages it through `usePaginatedQuery`.
26
+ */
27
+ paginationOpts: PaginationOptions;
28
+
29
+ /**
30
+ * Entry kinds to include.
31
+ *
32
+ * Filtering is performed by Convex before pagination. Omit this field to
33
+ * include every kind.
34
+ *
35
+ * Must contain at least one kind when provided.
36
+ */
37
+ kinds?: EntryKind[];
38
+
39
+ /** Restricts entries to this workflow status. */
40
+ status?: EntryStatus;
41
+
42
+ /**
43
+ * Server-side ordering strategy.
44
+ *
45
+ * When omitted, `config.entries.defaultSort` is used.
46
+ */
47
+ sort?: EntrySort;
48
+ };
49
+
50
+ /**
51
+ * Arguments for retrieving one entry.
52
+ */
53
+ export type GetEntryArgs = {
54
+ /** Identifier returned by the component for the requested entry. */
55
+ entryId: string;
56
+ };
57
+
58
+ /**
59
+ * Arguments for full-text entry search.
60
+ */
61
+ export type SearchEntriesArgs = {
62
+ /** Full-text query matched against the entry's indexed search text. */
63
+ searchQuery: string;
64
+
65
+ /**
66
+ * Entry kinds to include.
67
+ *
68
+ * Filtering occurs inside the Convex query rather than after results reach
69
+ * the client.
70
+ */
71
+ kinds?: EntryKind[];
72
+
73
+ /** Optional workflow-status restriction. */
74
+ status?: EntryStatus;
75
+
76
+ /**
77
+ * Maximum number of results to return.
78
+ *
79
+ * When omitted, `config.search.defaultLimit` is used. The server clamps the
80
+ * value to `config.search.maxLimit`.
81
+ */
82
+ limit?: number;
83
+ };
84
+
85
+ /**
86
+ * Arguments for exact/similar duplicate detection.
87
+ */
88
+ export type FindSimilarEntriesArgs = {
89
+ /**
90
+ * Proposed entry title.
91
+ *
92
+ * Its normalized form is used for exact duplicate detection.
93
+ */
94
+ title: string;
95
+
96
+ /**
97
+ * Proposed entry body.
98
+ *
99
+ * Combined with the title for the full-text similarity search.
100
+ */
101
+ body: string;
102
+
103
+ /**
104
+ * Restricts duplicate detection to one entry kind.
105
+ *
106
+ * Omit to search every kind.
107
+ */
108
+ kind?: EntryKind;
109
+
110
+ /**
111
+ * Maximum combined number of suggestions returned.
112
+ *
113
+ * Exact normalized-title matches consume this limit first. Only remaining
114
+ * slots are available to full-text matches.
115
+ *
116
+ * Therefore:
117
+ *
118
+ * `result.exact.length + result.similar.length <= limit`
119
+ *
120
+ * When omitted, `config.search.duplicateSuggestionLimit` is used.
121
+ */
122
+ limit?: number;
123
+ };
124
+
125
+ /**
126
+ * Arguments for creating an entry.
127
+ */
128
+ export type CreateEntryArgs = {
129
+ /** Category of entry to create. */
130
+ kind: EntryKind;
131
+
132
+ /** Entry title. */
133
+ title: string;
134
+
135
+ /** Entry description/body. */
136
+ body: string;
137
+ };
138
+
139
+ /**
140
+ * Arguments for editing an existing entry.
141
+ */
142
+ export type UpdateEntryArgs = {
143
+ /** Entry to update. */
144
+ entryId: string;
145
+
146
+ /** Complete replacement title. */
147
+ title: string;
148
+
149
+ /** Complete replacement body. */
150
+ body: string;
151
+ };
152
+
153
+ /**
154
+ * Arguments for changing an entry workflow status.
155
+ */
156
+ export type SetEntryStatusArgs = {
157
+ /** Entry whose status should change. */
158
+ entryId: string;
159
+
160
+ /** Desired workflow status. */
161
+ status: EntryStatus;
162
+ };
163
+
164
+ /**
165
+ * Arguments for setting the current actor's entry-upvote state.
166
+ */
167
+ export type SetEntryUpvoteArgs = {
168
+ /** Entry whose upvote state should change. */
169
+ entryId: string;
170
+
171
+ /**
172
+ * Desired final state.
173
+ *
174
+ * `true` ensures the current actor has an upvote.
175
+ * `false` ensures the current actor does not have an upvote.
176
+ *
177
+ * This is intentionally state-setting rather than toggle semantics, making
178
+ * the mutation idempotent and safe to retry.
179
+ */
180
+ desiredState: boolean;
181
+ };
182
+
183
+ /**
184
+ * Authoritative entry-upvote state returned after a mutation.
185
+ */
186
+ export type SetEntryUpvoteResult = {
187
+ /** Final upvote state for the current actor. */
188
+ active: boolean;
189
+
190
+ /** Updated total number of entry upvotes. */
191
+ upvoteCount: number;
192
+ };
193
+
194
+ /**
195
+ * Arguments for cursor-paginated comment/reply listing.
196
+ */
197
+ export type ListCommentsArgs = {
198
+ /** Convex cursor-pagination options. */
199
+ paginationOpts: PaginationOptions;
200
+
201
+ /** Entry whose conversation should be queried. */
202
+ entryId: string;
203
+
204
+ /**
205
+ * Direct parent comment.
206
+ *
207
+ * Omit to query top-level comments. When supplied, only direct children of
208
+ * this comment are returned; deeper descendants are not loaded.
209
+ */
210
+ parentCommentId?: string;
211
+
212
+ /**
213
+ * Server-side comment ordering strategy.
214
+ *
215
+ * When omitted, `config.comments.defaultSort` is used.
216
+ */
217
+ sort?: CommentSort;
218
+ };
219
+
220
+ /**
221
+ * Arguments for creating a top-level comment or reply.
222
+ */
223
+ export type CreateCommentArgs = {
224
+ /** Entry the comment belongs to. */
225
+ entryId: string;
226
+
227
+ /**
228
+ * Direct parent comment.
229
+ *
230
+ * Omit to create a top-level comment.
231
+ */
232
+ parentCommentId?: string;
233
+
234
+ /** Comment/reply text. */
235
+ body: string;
236
+ };
237
+
238
+ /**
239
+ * Arguments for editing a comment.
240
+ */
241
+ export type UpdateCommentArgs = {
242
+ /** Comment to edit. */
243
+ commentId: string;
244
+
245
+ /** Complete replacement body. */
246
+ body: string;
247
+ };
248
+
249
+ /**
250
+ * Arguments for soft-deleting a comment.
251
+ */
252
+ export type DeleteCommentArgs = {
253
+ /** Comment to soft-delete. */
254
+ commentId: string;
255
+ };
256
+
257
+ /**
258
+ * Arguments for setting the current actor's comment-like state.
259
+ */
260
+ export type SetCommentLikeArgs = {
261
+ /** Comment whose like state should change. */
262
+ commentId: string;
263
+
264
+ /**
265
+ * Desired final state.
266
+ *
267
+ * `true` ensures the actor likes the comment.
268
+ * `false` ensures the actor does not like the comment.
269
+ *
270
+ * This is idempotent rather than toggle-based.
271
+ */
272
+ desiredState: boolean;
273
+ };
274
+
275
+ /**
276
+ * Authoritative comment-like state returned after a mutation.
277
+ */
278
+ export type SetCommentLikeResult = {
279
+ /** Final like state for the current actor. */
280
+ active: boolean;
281
+
282
+ /** Updated total number of comment likes. */
283
+ likeCount: number;
284
+ };
285
+
286
+ /**
287
+ * Public Convex API exposed by `exposeFeedbackApi`.
288
+ *
289
+ * The host application exposes these wrappers from its own Convex deployment
290
+ * after resolving authentication and configuration.
291
+ *
292
+ * @typeParam RateLimitResult A validated rejection value returned by mutations
293
+ * when non-throwing rate limiting is configured. The default `never` preserves
294
+ * the original success-only mutation results.
295
+ */
17
296
  export interface FeedbackPublicApi<
18
297
  Name extends string | undefined = string | undefined,
298
+ RateLimitResult = never,
19
299
  > {
300
+ /** Returns a cursor-paginated entry list. */
20
301
  listEntries: FunctionReference<
21
302
  "query",
22
303
  "public",
23
- {
24
- paginationOpts: PaginationOptions;
25
- kind?: EntryKind;
26
- status?: EntryStatus;
27
- sort?: EntrySort;
28
- },
304
+ ListEntriesArgs,
29
305
  PaginationResult<FeedbackEntry>,
30
306
  Name
31
307
  >;
308
+
309
+ /** Returns one entry or `null` when it does not exist. */
32
310
  getEntry: FunctionReference<
33
311
  "query",
34
312
  "public",
35
- { entryId: string },
313
+ GetEntryArgs,
36
314
  FeedbackEntry | null,
37
315
  Name
38
316
  >;
317
+
318
+ /** Performs full-text entry search. */
39
319
  searchEntries: FunctionReference<
40
320
  "query",
41
321
  "public",
42
- {
43
- searchQuery: string;
44
- kind?: EntryKind;
45
- status?: EntryStatus;
46
- limit?: number;
47
- },
322
+ SearchEntriesArgs,
48
323
  FeedbackEntry[],
49
324
  Name
50
325
  >;
326
+
327
+ /** Finds exact and likely duplicate entries. */
51
328
  findSimilarEntries: FunctionReference<
52
329
  "query",
53
330
  "public",
54
- { title: string; body: string; kind?: EntryKind; limit?: number },
331
+ FindSimilarEntriesArgs,
55
332
  SimilarEntriesResult,
56
333
  Name
57
334
  >;
335
+
336
+ /** Creates an entry, returning its identifier or a rate-limit rejection. */
58
337
  createEntry: FunctionReference<
59
338
  "mutation",
60
339
  "public",
61
- { kind: EntryKind; title: string; body: string },
62
- string,
340
+ CreateEntryArgs,
341
+ string | RateLimitResult,
63
342
  Name
64
343
  >;
344
+
345
+ /** Replaces entry content, or returns a configured rate-limit rejection. */
65
346
  updateEntry: FunctionReference<
66
347
  "mutation",
67
348
  "public",
68
- { entryId: string; title: string; body: string },
69
- null,
349
+ UpdateEntryArgs,
350
+ null | RateLimitResult,
70
351
  Name
71
352
  >;
353
+
354
+ /** Changes entry status, or returns a configured rate-limit rejection. */
72
355
  setEntryStatus: FunctionReference<
73
356
  "mutation",
74
357
  "public",
75
- { entryId: string; status: EntryStatus },
76
- null,
358
+ SetEntryStatusArgs,
359
+ null | RateLimitResult,
77
360
  Name
78
361
  >;
362
+
363
+ /** Sets entry-upvote state, or returns a configured rate-limit rejection. */
79
364
  setEntryUpvote: FunctionReference<
80
365
  "mutation",
81
366
  "public",
82
- { entryId: string; desiredState: boolean },
83
- { active: boolean; upvoteCount: number },
367
+ SetEntryUpvoteArgs,
368
+ SetEntryUpvoteResult | RateLimitResult,
84
369
  Name
85
370
  >;
371
+
372
+ /** Returns one paginated level of comments or replies. */
86
373
  listComments: FunctionReference<
87
374
  "query",
88
375
  "public",
89
- {
90
- paginationOpts: PaginationOptions;
91
- entryId: string;
92
- parentCommentId?: string;
93
- sort?: CommentSort;
94
- },
376
+ ListCommentsArgs,
95
377
  PaginationResult<FeedbackComment>,
96
378
  Name
97
379
  >;
380
+
381
+ /** Creates a comment/reply, returning its ID or a rate-limit rejection. */
98
382
  createComment: FunctionReference<
99
383
  "mutation",
100
384
  "public",
101
- { entryId: string; parentCommentId?: string; body: string },
102
- string,
385
+ CreateCommentArgs,
386
+ string | RateLimitResult,
103
387
  Name
104
388
  >;
389
+
390
+ /** Replaces comment content, or returns a configured rate-limit rejection. */
105
391
  updateComment: FunctionReference<
106
392
  "mutation",
107
393
  "public",
108
- { commentId: string; body: string },
109
- null,
394
+ UpdateCommentArgs,
395
+ null | RateLimitResult,
110
396
  Name
111
397
  >;
398
+
399
+ /** Soft-deletes a comment, or returns a configured rate-limit rejection. */
112
400
  deleteComment: FunctionReference<
113
401
  "mutation",
114
402
  "public",
115
- { commentId: string },
116
- null,
403
+ DeleteCommentArgs,
404
+ null | RateLimitResult,
117
405
  Name
118
406
  >;
407
+
408
+ /** Sets comment-like state, or returns a configured rate-limit rejection. */
119
409
  setCommentLike: FunctionReference<
120
410
  "mutation",
121
411
  "public",
122
- { commentId: string; desiredState: boolean },
123
- { active: boolean; likeCount: number },
412
+ SetCommentLikeArgs,
413
+ SetCommentLikeResult | RateLimitResult,
124
414
  Name
125
415
  >;
126
416
  }
@@ -5,39 +5,201 @@ import type {
5
5
  EntryStatus,
6
6
  } from "../component/model.js";
7
7
 
8
+ /**
9
+ * Entry-related component configuration.
10
+ */
11
+ export interface FeedbackEntriesConfig {
12
+ /**
13
+ * Entry kinds that may be created through this component instance.
14
+ *
15
+ * @default ["feedback", "feature_request", "bug_report"]
16
+ */
17
+ enabledKinds: readonly EntryKind[];
18
+
19
+ /**
20
+ * Status assigned to newly created entries.
21
+ *
22
+ * @default "open"
23
+ */
24
+ defaultStatus: EntryStatus;
25
+
26
+ /**
27
+ * Default server-side entry ordering when callers do not specify `sort`.
28
+ *
29
+ * @default "top"
30
+ */
31
+ defaultSort: EntrySort;
32
+
33
+ /**
34
+ * Maximum number of entries a caller may request in one pagination batch.
35
+ *
36
+ * Larger requested page sizes are clamped to this value.
37
+ *
38
+ * @default 50
39
+ */
40
+ maxPageSize: number;
41
+
42
+ /**
43
+ * Whether entry authors may edit their own title and body.
44
+ *
45
+ * Moderators are governed separately by the resolved actor permissions.
46
+ *
47
+ * @default true
48
+ */
49
+ editableByAuthor: boolean;
50
+ }
51
+
52
+ /**
53
+ * Comment and reply configuration.
54
+ */
55
+ export interface FeedbackCommentsConfig {
56
+ /**
57
+ * Maximum allowed nesting depth.
58
+ *
59
+ * Top-level comments have depth `0`. Attempts to create replies deeper than
60
+ * this limit are rejected by the component.
61
+ *
62
+ * @default 5
63
+ */
64
+ maxDepth: number;
65
+
66
+ /**
67
+ * Maximum number of comments or replies that may be requested in one
68
+ * pagination batch.
69
+ *
70
+ * @default 50
71
+ */
72
+ maxPageSize: number;
73
+
74
+ /**
75
+ * Default server-side comment ordering.
76
+ *
77
+ * @default "top"
78
+ */
79
+ defaultSort: CommentSort;
80
+
81
+ /**
82
+ * Whether comment authors may edit their own comments.
83
+ *
84
+ * @default true
85
+ */
86
+ editableByAuthor: boolean;
87
+
88
+ /**
89
+ * Whether comment authors may soft-delete their own comments.
90
+ *
91
+ * Deletion preserves the document so nested replies retain their structure.
92
+ *
93
+ * @default true
94
+ */
95
+ deletableByAuthor: boolean;
96
+ }
97
+
98
+ /**
99
+ * Full-text search and duplicate-detection configuration.
100
+ */
101
+ export interface FeedbackSearchConfig {
102
+ /**
103
+ * Whether similar/duplicate suggestions are enabled.
104
+ *
105
+ * When disabled, duplicate-search queries return empty `exact` and
106
+ * `similar` arrays.
107
+ *
108
+ * @default true
109
+ */
110
+ duplicateSuggestions: boolean;
111
+
112
+ /**
113
+ * Default combined result limit for duplicate suggestions when the caller
114
+ * does not provide `limit`.
115
+ *
116
+ * Exact matches consume this limit before similar matches.
117
+ *
118
+ * @default 5
119
+ */
120
+ duplicateSuggestionLimit: number;
121
+
122
+ /**
123
+ * Default result limit for normal full-text entry search.
124
+ *
125
+ * @default 20
126
+ */
127
+ defaultLimit: number;
128
+
129
+ /**
130
+ * Maximum allowed result limit for search and duplicate queries.
131
+ *
132
+ * Larger requested limits are clamped to this value.
133
+ *
134
+ * @default 50
135
+ */
136
+ maxLimit: number;
137
+ }
138
+
139
+ /**
140
+ * Maximum lengths accepted for user-generated content.
141
+ */
142
+ export interface FeedbackContentLimits {
143
+ /**
144
+ * Maximum entry-title length.
145
+ *
146
+ * @default 160
147
+ */
148
+ titleLength: number;
149
+
150
+ /**
151
+ * Maximum entry-body length.
152
+ *
153
+ * @default 10000
154
+ */
155
+ bodyLength: number;
156
+
157
+ /**
158
+ * Maximum comment/reply length.
159
+ *
160
+ * @default 5000
161
+ */
162
+ commentLength: number;
163
+ }
164
+
165
+ /**
166
+ * Complete server-side configuration for a feedback component instance.
167
+ *
168
+ * Configuration is supplied by the host application and is not persisted in
169
+ * an additional component table.
170
+ */
8
171
  export interface FeedbackConfig {
9
- entries: {
10
- enabledKinds: readonly EntryKind[];
11
- defaultStatus: EntryStatus;
12
- defaultSort: EntrySort;
13
- maxPageSize: number;
14
- editableByAuthor: boolean;
15
- };
16
- comments: {
17
- maxDepth: number;
18
- maxPageSize: number;
19
- defaultSort: CommentSort;
20
- editableByAuthor: boolean;
21
- deletableByAuthor: boolean;
22
- };
23
- search: {
24
- duplicateSuggestions: boolean;
25
- duplicateSuggestionLimit: number;
26
- defaultLimit: number;
27
- maxLimit: number;
28
- };
29
- limits: {
30
- titleLength: number;
31
- bodyLength: number;
32
- commentLength: number;
33
- };
172
+ /** Entry creation, ordering, pagination, and editing rules. */
173
+ entries: FeedbackEntriesConfig;
174
+
175
+ /** Comment nesting, ordering, pagination, and editing rules. */
176
+ comments: FeedbackCommentsConfig;
177
+
178
+ /** Search and duplicate-detection behavior. */
179
+ search: FeedbackSearchConfig;
180
+
181
+ /** User-generated content limits. */
182
+ limits: FeedbackContentLimits;
34
183
  }
35
184
 
185
+ /**
186
+ * Partial configuration accepted by `createFeedbackConfig` and
187
+ * `exposeFeedbackApi`.
188
+ *
189
+ * Missing values fall back to `defaultFeedbackConfig`.
190
+ */
36
191
  export interface FeedbackConfigOverrides {
37
- entries?: Partial<FeedbackConfig["entries"]>;
38
- comments?: Partial<FeedbackConfig["comments"]>;
39
- search?: Partial<FeedbackConfig["search"]>;
40
- limits?: Partial<FeedbackConfig["limits"]>;
192
+ /** Overrides for entry configuration. */
193
+ entries?: Partial<FeedbackEntriesConfig>;
194
+
195
+ /** Overrides for comment configuration. */
196
+ comments?: Partial<FeedbackCommentsConfig>;
197
+
198
+ /** Overrides for search configuration. */
199
+ search?: Partial<FeedbackSearchConfig>;
200
+
201
+ /** Overrides for content limits. */
202
+ limits?: Partial<FeedbackContentLimits>;
41
203
  }
42
204
 
43
205
  export const defaultFeedbackConfig: FeedbackConfig = {