Inspected version: 0.1.0 (private) · Part of the veap-repo monorepo (packages/veap-commentable).
@veap/commentable adds polymorphic threaded comments (and reviews) to any @veap/database model. Comments live in a shared comments table and attach to any model through a commentable morph relation.
Entry points#
The manifest exports ., ./migrations, ./ui and ./package.json.
Models#
Comment— thecommentsmodel (static table = "comments",morphAlias = "comment"). Registered inMorphMapascomment.commentable()—morphTo("commentable"), the owning entity.author()—belongsTo(User, "author_id")(optional authenticated author).parent()—belongsTo(Comment, "parent_id").replies()—hasMany(Comment, "parent_id").reply({ content, authorId?, authorName?, authorEmail?, status? })— creates a child reply on the same commentable.
CommentFactory/createCommentFactory()— a faker-backed factory withforCommentable(target, type?),pending(),approved()andspam()states.
Status values: pending, approved, spam, rejected, trash. The default for new comments is approved.
Mixins and base classes#
CommentableModel— a base class extendingModel.Commentable(Base)— the mixin form.ICommentableModel/CommentableClass— typing helpers.
Instance API#
| Method | Behavior |
|---|---|
comments() | The morphMany(Comment, "commentable") relation. |
approvedComments() | Comments with status = "approved". |
addComment({ content, authorId?, authorName?, authorEmail?, parentId?, status? }) | Creates and saves a comment on this model. |
addComment resolves the polymorphic type from static morphAlias (falling back to static table) and sets commentable_type/commentable_id before saving.
Query builder#
CommentableQueryBuilder extends ModelQueryBuilder:
whereHasComments()— models that have at least one comment.withComments()— eager-loadscomments.
Migrations#
The /migrations entry point creates the comments table (id, commentable_type, commentable_id, author_id, author_name, author_email, parent_id, content, status defaulting to approved, timestamps) with lookup indexes on (commentable_type, commentable_id), parent_id and status. down() preserves the shared table.
UI (./ui)#
CommentSection— renders a threaded list. Props:comments: CommentItem[](each with optional nestedreplies),onAddComment(data),allowReplies?andclassName?.CommentForm— the submit form. Props:parentId?,onSubmit(data),onCancel?,placeholder?,submitLabel?,showAuthorFields?andclassName?.
Both are client components and accept already-loaded comments; they do not fetch data themselves.
Runtime and caveats#
addComment/replycallsave()directly on the new comment rather than going through a transaction. If your application policy requires all writes to run insidetransaction()from@veap/database, wrap the call yourself.- A moderation
statusis data, not authorization: enforce who may comment or moderate with your own server-side checks. TreatauthorName/authorEmailinput as untrusted guest data. - Comments render as plain strings/components; sanitize or render Markdown safely on your own if users may post formatted content.