convex-feedback 0.1.0 → 0.1.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/src/client/api.ts CHANGED
@@ -14,113 +14,398 @@ 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
+ */
17
292
  export interface FeedbackPublicApi<
18
293
  Name extends string | undefined = string | undefined,
19
294
  > {
295
+ /** Returns a cursor-paginated entry list. */
20
296
  listEntries: FunctionReference<
21
297
  "query",
22
298
  "public",
23
- {
24
- paginationOpts: PaginationOptions;
25
- kind?: EntryKind;
26
- status?: EntryStatus;
27
- sort?: EntrySort;
28
- },
299
+ ListEntriesArgs,
29
300
  PaginationResult<FeedbackEntry>,
30
301
  Name
31
302
  >;
303
+
304
+ /** Returns one entry or `null` when it does not exist. */
32
305
  getEntry: FunctionReference<
33
306
  "query",
34
307
  "public",
35
- { entryId: string },
308
+ GetEntryArgs,
36
309
  FeedbackEntry | null,
37
310
  Name
38
311
  >;
312
+
313
+ /** Performs full-text entry search. */
39
314
  searchEntries: FunctionReference<
40
315
  "query",
41
316
  "public",
42
- {
43
- searchQuery: string;
44
- kind?: EntryKind;
45
- status?: EntryStatus;
46
- limit?: number;
47
- },
317
+ SearchEntriesArgs,
48
318
  FeedbackEntry[],
49
319
  Name
50
320
  >;
321
+
322
+ /** Finds exact and likely duplicate entries. */
51
323
  findSimilarEntries: FunctionReference<
52
324
  "query",
53
325
  "public",
54
- { title: string; body: string; kind?: EntryKind; limit?: number },
326
+ FindSimilarEntriesArgs,
55
327
  SimilarEntriesResult,
56
328
  Name
57
329
  >;
330
+
331
+ /** Creates an entry and returns its public identifier. */
58
332
  createEntry: FunctionReference<
59
333
  "mutation",
60
334
  "public",
61
- { kind: EntryKind; title: string; body: string },
335
+ CreateEntryArgs,
62
336
  string,
63
337
  Name
64
338
  >;
339
+
340
+ /** Replaces an entry's editable content. */
65
341
  updateEntry: FunctionReference<
66
342
  "mutation",
67
343
  "public",
68
- { entryId: string; title: string; body: string },
344
+ UpdateEntryArgs,
69
345
  null,
70
346
  Name
71
347
  >;
348
+
349
+ /** Changes an entry's workflow status. */
72
350
  setEntryStatus: FunctionReference<
73
351
  "mutation",
74
352
  "public",
75
- { entryId: string; status: EntryStatus },
353
+ SetEntryStatusArgs,
76
354
  null,
77
355
  Name
78
356
  >;
357
+
358
+ /** Sets the current actor's entry-upvote state. */
79
359
  setEntryUpvote: FunctionReference<
80
360
  "mutation",
81
361
  "public",
82
- { entryId: string; desiredState: boolean },
83
- { active: boolean; upvoteCount: number },
362
+ SetEntryUpvoteArgs,
363
+ SetEntryUpvoteResult,
84
364
  Name
85
365
  >;
366
+
367
+ /** Returns one paginated level of comments or replies. */
86
368
  listComments: FunctionReference<
87
369
  "query",
88
370
  "public",
89
- {
90
- paginationOpts: PaginationOptions;
91
- entryId: string;
92
- parentCommentId?: string;
93
- sort?: CommentSort;
94
- },
371
+ ListCommentsArgs,
95
372
  PaginationResult<FeedbackComment>,
96
373
  Name
97
374
  >;
375
+
376
+ /** Creates a comment or reply and returns its identifier. */
98
377
  createComment: FunctionReference<
99
378
  "mutation",
100
379
  "public",
101
- { entryId: string; parentCommentId?: string; body: string },
380
+ CreateCommentArgs,
102
381
  string,
103
382
  Name
104
383
  >;
384
+
385
+ /** Replaces a comment's editable body. */
105
386
  updateComment: FunctionReference<
106
387
  "mutation",
107
388
  "public",
108
- { commentId: string; body: string },
389
+ UpdateCommentArgs,
109
390
  null,
110
391
  Name
111
392
  >;
393
+
394
+ /** Soft-deletes a comment while preserving thread structure. */
112
395
  deleteComment: FunctionReference<
113
396
  "mutation",
114
397
  "public",
115
- { commentId: string },
398
+ DeleteCommentArgs,
116
399
  null,
117
400
  Name
118
401
  >;
402
+
403
+ /** Sets the current actor's comment-like state. */
119
404
  setCommentLike: FunctionReference<
120
405
  "mutation",
121
406
  "public",
122
- { commentId: string; desiredState: boolean },
123
- { active: boolean; likeCount: number },
407
+ SetCommentLikeArgs,
408
+ SetCommentLikeResult,
124
409
  Name
125
410
  >;
126
411
  }
@@ -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 = {