npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@happyvertical/smrt-comments

v0.60.14

Published

Record comments and mention hooks for SMRT applications

Readme

@happyvertical/smrt-comments

Tenant-scoped polymorphic record comments, mention notification composition, and a browser-safe RecordComments Svelte component.

Use CommentService on the server with a trusted { tenantId, userId } actor from the authenticated session and an authorizeRecord callback. The callback must verify that the record belongs to that tenant and that the actor may read or comment on it. Both create and listForRecord require that decision; request bodies cannot override the actor. CreateCommentInput contains only tenant, author, record type/id, body, mentions and an optional idempotencyKey. The service ignores extra runtime properties, generates its own UUID/slug and inserts a new row without natural-key adoption. Caller-provided identity, context, database and constructor options never reach persistence. CommentOptions remains the separate trusted model hydration type. Direct model/collection APIs are persistence primitives for trusted code, not authorization boundaries. Generated API, MCP, and CLI access is disabled.

const comments = new CommentService({
  db,
  actor: { tenantId: session.tenantId, userId: session.userId },
  authorizeRecord: access => records.authorize(access),
  participation: {
    // Supplies trusted membership and entitlement facts.
    authorize: request => entitlements.authorize(request),
  },
  mentionNotifications: createCommentMentionNotifications({
    notifications: new UserNotificationService({ db }), // smrt-messages
    canNotify: mention => records.recipientMayRead(mention),
  }),
});

participation is an optional server-side entitlement adapter. It receives trusted identity and record/action inputs and returns { allowed, member?, snapshot? }. Only literal allowed: true permits participation; members_only also requires member: true. The immutable JSON snapshot is retained on each new comment.

ParticipationService owns reusable tenant/target policy selection, bans and atomic fixed-window rate reservations. All CommentService creates invoke it, even without an entitlement adapter. Configure ParticipationPolicyCollection and ParticipationBanCollection on trusted administrative server paths under withTenant; these models expose no automatic API/CLI/MCP operations. Migrate all comments package models before deploying. A missing policy defaults to open; an exact (recordType, recordId, action) policy replaces the tenant default (blank record pair). Modes are open, pre_moderated, members_only, closed. A duplicate applicable policy fails closed. Ban targets may be blank (tenant-wide) or exact; a null expiry means indefinite. Ban decisions do not expose reasons.

maxActions: 0 disables quota; otherwise set a positive integer windowSeconds. Quota is per tenant, actor, action and selected policy scope in fixed UTC epoch windows. A tenant policy shares quota across records. The persisted counter uses one bounded atomic upsert; reservation and protected mutation commit or roll back together. Never delete active rate buckets. Policy edits affect subsequent checks, not transactions already authorized; changing a window length starts a new quota namespace. Trusted administrators own policy and bucket retention.

Reactions/polls import ParticipationService and neutral types from @happyvertical/smrt-comments/participation. Use execute(request, (tx, decision) => mutationUsing(tx)) for a new mutation. Within an existing transaction use executeInTransaction(tx, request, callback) and propagate errors so the owner rolls back. authorize checks current permission without spending quota. The consumer owns its durable idempotency binding: replay retained mutations after reauthorization, and call execute only for a genuinely new mutation. Comments already implement this composition. No subscription or journalism rules live here.

CommentService.edit retains prior bodies and rechecks record/participation permission, bans and the comment-action quota in the same transaction. Edits spend quota and retain the original post-time snapshot. report appends a moderation report, and moderate checks the host's record-level moderation permission before changing visibility. Readers receive authorized projections; hosts filter non-visible rows for their audience. ModerationQueue accepts pending/reported projections and an onmoderate callback; the browser cannot grant moderation authority.

The messages service is composed through a structural interface; there is no runtime dependency on messages. canNotify must check recipient membership and record access before exposing the comment. Notifications use a stable per-comment source reference so adapter replays deduplicate. Mentions are UUIDs, deduplicated, and limited to 50; self-mentions do not notify. No adapter means no notifications. Mention validation finishes before the single comment insert; invalid or over-limit mentions leave no saved row. The comment is saved before notification delivery. A delivery rejection leaves the comment saved. With idempotencyKey, retry create with the same key and content to recover delivery without creating another comment. Without a key, reload and retry delivery rather than creating another comment. The component warns users to refresh and check the discussion before posting again when submission rejects. Delivery and persistence are separate unless the host composes both with the same outer transaction. See idempotent creation for key binding, typed conflicts, retention and transaction requirements.

Import RecordComments from @happyvertical/smrt-comments/svelte. Pass authorized comment projections, an onsubmit(body) callback, and a contextKey containing the current tenant, actor and record identity. Change that key whenever any of those identities changes: it clears drafts/errors and ignores stale submission completion. The host owns loading, authentication, mention selection and refresh. The component renders plain text. RecordCommentsRecipe declares the comments.records model recipe in the stable comments catalog group. It intentionally has no global navigation or section: a discussion needs an already-authorized parent record, and a generic comment list would lose that context. Hosts embed it on a record detail page. Catalog help explains posting and mentions without promising a mention picker or delivery configuration. The recipe declares runtime: both: the record component runs in the browser, while persistence and authorization run on the server. Its playground surface exports RecordCommentsPlayground, a standalone sample-record preview with local-only posting. recordCommentsDemo supplies the same sample projections through demoSeed; remounting resets the preview. No notifications are sent. There is no route, settings panel or shell widget: none can supply the authorized parent-record context. No provider is declared because the host's record access and mention policies are callbacks, not a selectable external provider.

Validation: pnpm --filter @happyvertical/smrt-comments test covers SQLite and DuckDB. pnpm --filter @happyvertical/smrt-comments test:postgres adds PostgreSQL using the repository's disposable-database wrapper and ordinary CI role.

Keyed retries use a separate canonical tenant binding while preserving stored tenant and author references. UUID casing changes across trusted actor contexts reuse the same comment, including after generic receipt retention. Unkeyed legacy SQLite listing remains scoped to its original tenant spelling; keyed rows are merged by equivalent UUID tenant without folding arbitrary textual tenant IDs.

Use lowercase UUIDs for administrative policy/ban tenant, actor and target identities; request UUIDs are canonicalized before enforcement. Text identifiers retain case. Thread projections may include parentCommentId; supply onreply(parentId, body) to enable the reply composer. Host callbacks persist replies with that parent ID.

Edits to visible comments return to pending when current policy or entitlement requires pre-moderation; edits never restore hidden or deleted comments.

Reports use the separate report participation action, so hosts can configure report-specific limits without coupling them to comment quotas. Bans and entitlement checks apply to every report attempt. Only visible comments are reportable. Each actor/comment pair retains one report, including after generic receipt cleanup; retries return that report without consuming quota or replacing its original reason.

Parentless keyed-create digests preserve the original v1 shape across upgrades. After an authorized edit, replay verifies the original creation body against retained revision history and returns the current comment; edited content is not a new create payload. Revision history must be retained while the keyed comment is retained.

Mention delivery is limited to visible comments, including keyed retries. Pending, hidden and deleted bodies never reach the mention adapter. Approval does not send notifications automatically; hosts may explicitly retry the original keyed create after approval to recover delivery. Moderation uses the save lifecycle on a locked and freshly reloaded row, preserving change-feed and cache invalidation. Parent comment UUIDs are canonicalized before lookup, persistence and retry hashing. Reply-only embeds show a composer only while a reply target is selected.