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

@domternal-pro/extension-collaboration-caret

v1.0.0

Published

Remote carets and presence for Domternal collaboration

Readme

@domternal-pro/extension-collaboration-caret

Remote carets, selections and presence for Domternal collaboration. Requires @domternal-pro/extension-collaboration.

Links

Documentation    •    Live examples

Licensing

This is a commercial package distributed through the public npm registry. A DOM-connected editor surface remains fully featured without a key for internal evaluation and shows a small evaluation badge. Guarded headless, standalone, server and worker operations have no badge surface, so they require an explicit evaluation mode or production mode with a covered DMP2 Commercial Key. Evaluation does not permit production use.

Keyless headless evaluation is explicit and remains fully functional:

import { configureProLicense } from '@domternal-pro/core';

configureProLicense({ mode: 'evaluation' });

Production uses the signed Commercial Key from the Order confirmation:

import { configureProLicense } from '@domternal-pro/core';

configureProLicense({
  mode: 'production',
  key: 'DMP2...',
});

A Commercial DMP2 Key carries exactly one signed Product Line. Every registered commercial package in an ordinary release must carry that same Product Line. A security-only release must carry it in both package metadata and its separate DMS1 certificate. Missing, conflicting or mismatched Product Line metadata remains uncovered and does not activate production. DMS1 never changes the key or grants another Product Line. Evaluation, Internal and legacy DMP1 keys do not use this Commercial Product Line field.

The signed key payload is not encrypted and may be readable in delivered browser code. Permitted embedding is expected, but the raw key must remain within the Customer scope allowed by the license. DMP2 omits Customer identity, contact details, Order reference, plan, price, separate payment and paid-through fields, and Developer limits. Commercial entitlement and per-key identifiers are random and opaque; the public status API does not expose them.

License validation runs entirely offline and never contacts Domternal: there is no online activation, license telemetry, metering, seat-counting or revocation request. A DMP2 Evaluation Key can remove visible evaluation notices only in explicitly selected evaluation mode and only through its signed UTC expiry day; it can never activate production mode. In production mode it remains visibly badged or marked. A DMP1 key is recognized only for legacy diagnostics: it leaves editor badges and marked-output notices in place and never authorizes guarded production use. A DMP2 Commercial Key does not activate before its signed not-before day. For ordinary and renewal keys that day is the Subscription start; an exceptional surviving-rights or remedy key can use the later decision or issuance day required by the license. During its paid Subscription and 14-day grace period, ordinary package coverage stops at its signed ordinaryReleaseThrough day; grace does not extend that boundary. After grace, a key without signed technical post-grace eligibility reports subscription-expired. A technically eligible key can continue coverage through ordinaryReleaseThrough, qualifying DMS1 releases and exact Section 10 remedy days, while the public status exposes a compatible derived fallbackThrough equal to ordinaryReleaseThrough. That technical marker does not prove 12 paid months or define the surviving legal scope: the Agreement and accepted Order define the legal scope, while the key and minimum durable entitlement state support later recognition of that scope, including whether it covers Qualified Builds, discontinuation final retained builds or another express surviving right. Post-expiry use is runtime-only for the exact permitted artifacts. Development, modification, rebuilding and new Applications require an active Subscription, subject only to the Agreement's narrow security, intellectual-property and replacement-key exceptions. A key-only reproduction changes no other code, dependency or feature and inherits the original build status. The contractual post-expiry Developer limit remains as a final derived value in the minimum durable entitlement state while it is relevant; supporting Order, payment and calculation records follow their separate retention periods; the runtime does not count people or expose that limit. Rights from different expiries remain attached to their own Commercial Keys. Coverage therefore uses both baked package release dates and the current UTC day reported by the runtime. Guarded headless production activates only when that Commercial Key covers every registered Pro package. Each JavaScript context that runs guarded headless work needs its own configuration. Activation is technical, not the license itself: permitted use is defined by LICENSE.md. See Installation and licensing for the complete setup.

Security-only releases use a separate signed DMS1 Security Release Certificate embedded in the package set; DMS1 does not add fields to or modify the Customer's DMP2 key. During the paid Subscription and grace, DMS1 requires a signed feature-baseline Release Date on or before ordinaryReleaseThrough; after grace, the key must also have technical post-grace eligibility. An exact Section 10 replacement date does not establish DMS1 baseline eligibility. A key without post-grace eligibility is never revived after grace. DMS1 does not renew a Subscription, move an ordinary-release boundary, change a Developer limit, unlock features or grant another Product Line. Post-expiry use is limited to the narrow security replacement of an eligible artifact, with no unrelated code, dependency or feature change, and the result inherits that artifact's status. The fixed Product Line Security Support End Date ends the obligation to issue further Security Updates, subject to mandatory law. An eligible DMS1 update signed and released during that period may remain usable afterward only within the same existing or surviving production right and limits; continued use does not make the Product Line supported or create, restore, renew or expand a right. The controlling terms are in Section 8 of LICENSE.md and the public security policy.

Constructing CollaborationCaret without an element, or with element: null, is guarded as true headless initialization. An explicitly supplied detached HTMLElement is accepted as a pending framework-wrapper mount, but it does not publish user, cursor, typing or keepalive state until it connects or detached use is otherwise allowed. Local document and selection transactions, updateUser, typing activity and presence keepalive all reclassify the live DOM at their operation boundaries. A blocked local transaction root throws before mutation. A local appendTransaction mutation derived by another plugin from a recognized inbound synchronization root is instead skipped, so it cannot discard received content while the editor is detached. When the last eligible editor on a shared Awareness instance detaches, Domternal-owned presence fields are suspended without deleting application-owned awareness metadata; an eligible connected sibling keeps the shared presence alive, and reattachment republishes it. A connected editor remains evaluable with the badge. Presence reads, the standalone avatar-strip renderer, teardown and navigation helpers remain available without a production assertion.

Install

pnpm add @domternal-pro/core @domternal-pro/extension-collaboration @domternal-pro/extension-collaboration-caret y-protocols

Works with any provider exposing a y-protocols awareness instance: Hocuspocus, y-websocket, y-sweet, or a bare { awareness } object. The same single-instance rule as for the collaboration package applies to yjs, y-prosemirror, y-protocols and the prosemirror-* packages.

One copy of y-prosemirror, Yjs and the core

This package finds the collaboration binding by the object identity of ySyncPluginKey, so a second copy of y-prosemirror does not error, it misses: every remote caret silently stops rendering. A second yjs gives two Awareness hierarchies, and a second @domternal/core means this extension is not the class your editor's manager binds.

All three are checked at construction and throw with a message naming both packages and the fix. Keep y-prosemirror inside ~1.3.7, the range all three collaboration packages declare; an unsatisfied peer is how npm and yarn end up nesting a second copy. Full dedupe recipe per package manager and bundler: https://domternal.dev/v1/guides/single-prosemirror-copy/

Usage

import { Collaboration } from '@domternal-pro/extension-collaboration';
import { CollaborationCaret } from '@domternal-pro/extension-collaboration-caret';
import '@domternal-pro/core/panel.css';
import '@domternal-pro/extension-collaboration-caret/caret.css';

const editor = new Editor({
  extensions: [
    StarterKit.configure({ history: false }),
    Collaboration.configure({ document: provider.document }),
    CollaborationCaret.configure({
      provider,
      user: { name: 'Ana', color: '#2563eb' },
    }),
  ],
});

The CSS import ships the default caret, label and selection styles (dm-collab-caret, dm-collab-caret-label, dm-collab-selection). The chip itself is built to the shared pill's class contract, whose rules live in @domternal-pro/core/panel.css, so import that stylesheet too; once per app is enough.

Options

| Option | Default | Description | | --- | --- | --- | | provider | null | Anything exposing a y-protocols awareness instance. Required. | | user | { name: 'Anonymous', color: '#ffa500' } | Local identity. color must be a 6-digit hex color (#RRGGBB). | | render | built-in | Custom caret DOM: (user, clientId) => HTMLElement. | | selectionRender | built-in | Custom selection decoration: (user, clientId) => DecorationAttrs. | | onPresenceChange | null | Called with the deduped user list whenever awareness changes. | | cursorStateField | 'cursor' | Awareness field carrying this editor's cursor. Give each editor its own field when several editors share one provider. | | presenceChip | true | Mount the built-in presence chip in the editor frame's top-right corner. Disable to build a custom surface on onPresenceChange. | | presenceChipLabel | count => "N online" | Text next to the chip's avatars. | | presenceChipHideForPanels | ['versionHistory'] | Ids the chip fades out for, matched against the host's data-dm-banner-open; a docked panel of the same name only slides it aside. | | presenceChipActions | true | Quick-action icon buttons at the chip's tail (version history, comments, Ask AI, export), each only when its extension is registered. |

Presence chip and connection status

The chip shows avatars, a live "N online" label with join/left narration, typing signals, and an activity roster with per-person Jump. It slides aside for docked panels (comments, version history) and disappears while a preview banner named in presenceChipHideForPanels is up: version history stamps data-dm-banner-open on the dock host for the whole preview, not only while its panel is docked.

The tail comes from builtInPanelActions in @domternal-pro/core: version history and comments toggle their panels, Ask AI opens the prompt panel (omitted on a read-only editor, where the command refuses), and Export offers Word and PDF, plus Print when the free Print extension is registered. In a Notion-style layout with no toolbar it is the only pointer route to them.

Pair it with the CollaborationStatus extension (@domternal-pro/extension-collaboration/status) on the same editor. The chip takes its connection knowledge from that extension's collaborationStatus event: the status dot, the offline/reconnect toasts, and the stale-room freeze that keeps a socket drop from flapping everyone out and back in all exist only with a status source. Without it the chip still renders presence, but a connection blip will visibly empty and refill the room.

Commands and storage

  • editor.commands.updateUser({ name, color }) updates the local identity and broadcasts it. editor.can().updateUser(...) validates without publishing.
  • editor.storage.collaborationCaret.users holds the connected users, deduped by identity (one person with N tabs occupies N clientIDs), capped at 100 entries.

Remote input sanitization

Awareness states arrive unvalidated from any connected peer, so the extension sanitizes them before every render sink and the presence list: an invalid remote color falls back to a neutral tint (a raw one would be CSS injection through the selection style), names truncate at 64 characters, and malformed states are dropped entirely. Custom render/selectionRender functions receive the sanitized user. Extra fields beyond name/color pass through untouched and must be treated as untrusted.

Presence strip

renderPresenceStrip(host, users, { max }) renders the connected-users avatar stack into host (replacing its content), with a "+N" overflow chip. Initials and tints come from the shared @domternal-pro/core avatar builder, so a user wears the same color here as in comment rows and version entries. Feed it from onPresenceChange or storage.users; geometry ships in caret.css (.dm-presence-strip).

Support

Report a defect that reproduces with synthetic content on the public tracker, using the Pro bug report template. Anything involving your own documents, your data or a collaboration update goes to email instead, and security vulnerabilities go to email only.

The Support and reporting issues policy explains where confirmed defects may be documented and how material notices are handled. Public channels are not a complete record of internal reports, investigations or other issues. Security vulnerabilities are handled through coordinated private disclosure.