@wulperstd/editor-core
v2.0.0
Published
Tiptap extension/command layer bound to @wulperstd/schema's block set.
Downloads
548
Readme
@wulperstd/editor-core
Tiptap extension/command layer bound to @wulperstd/schema's block set. It
ships the node/mark extensions, editor-behavior bundle, and authoring
utilities (paste detection/parsing, MDX serialization, a headless
slash-command layer) needed to build a Tiptap editor for this block schema,
without depending on any UI framework (@tiptap/react, @tiptap/vue-3,
react, vue, ...) at import time.
Install
pnpm add @wulperstd/editor-core @tiptap/core @tiptap/pm @floating-ui/dom@tiptap/core, @tiptap/pm, and @floating-ui/dom are required peer
dependencies, not bundled ones: they are singletons — a second copy of
@tiptap/core/@tiptap/pm in your dependency tree breaks instanceof
checks and plugin identity — so install them alongside this package rather
than relying on a transitive copy. Every other Tiptap extension this package
uses internally (@tiptap/extension-details, @tiptap/extension-link,
@tiptap/extension-list, @tiptap/extension-table, @tiptap/extensions,
@tiptap/suggestion) is bundled and re-exported through blockExtensions, so
you never install those directly.
ESM-only: this package is published as "type": "module" with no
CommonJS entry in its exports map. It cannot be require()d from a
CommonJS-only consumer — import it from ESM (or an async import()) instead.
Usage
import { Editor } from '@tiptap/core';
import { blockExtensions, behaviorExtensions } from '@wulperstd/editor-core';
const editor = new Editor({
extensions: [...blockExtensions, ...behaviorExtensions()],
});blockExtensions is the full node/mark extension set matching
@wulperstd/schema's block set. behaviorExtensions(options?) bundles
the editor-behavior extensions (UndoRedo, Dropcursor, Gapcursor,
TrailingNode, Placeholder, ListKeymap) — both, and each extension
individually, are also available as named exports.
Authoring utilities
Named exports outside blockExtensions (none of these are node/mark
extensions, so none affect the schema extension-coverage check):
serializeMdx— serializes editor content to MDX.detectPasteKind,mdxToHtml,PasteParser,pastePluginKey— paste-source detection and parsing.createSlashCommand,filterSlashItems,slashActions— a headless slash-command suggestion layer.classifyEmbedSrc,isCuratedEmbedSrc,toEmbedSrc— the embed-service matching/classification helpers behindembedExtension; the curated service list itself is supplied by your app viaembedExtension'sservicesoption.
Security: CardLinkResolver
cardLinkExtension.configure({ resolver }) accepts a host-supplied
CardLinkResolver:
type CardLinkResolver = (href: string) => Promise<CardLinkMetadata | null>;No package in this repository ships an implementation — you write and
register this function, and it performs SSRF-exposed network I/O. href is
attacker-controllable: whoever can type in the editor chooses it, and the
request is issued by your own backend, from inside your network. The obvious
implementation — fetch(href), parse the og: tags — is the vulnerable one:
a pasted http://169.254.169.254/latest/meta-data/... makes your backend
read its own cloud credentials and persist them into a card's description.
Your implementation must, at minimum:
- Accept only
http/https, declining every other scheme before any access is attempted. - Block private, loopback, link-local, and other reserved IP ranges,
explicitly including the cloud metadata address
169.254.169.254, before connecting to a resolved address. - Resolve the hostname once and connect to that validated IP, and repeat both the range validation and the pinned connection independently for every redirect hop — a hostname's resolution can change between the moment it is validated and the moment the connection is made (DNS rebinding).
- Cap redirect depth, request timeout, and total response size, and validate
the response
Content-Typebefore parsing it for metadata. The specific limits are left to your deployment: choose values that suit it and make each one configurable rather than hardcoded.
Resolve to null — do not throw — when the URL legitimately has no
discoverable preview data; throw or reject only for an actual I/O or parse
failure. This package enforces no timeout of its own, so a timeout is your
resolver's obligation and surfaces as a rejection. Either way, the node is
left unpatched and marked failed; a partial CardLinkMetadata populating
only some fields is a valid non-null result.
These obligations are normative (see specs/card-link-resolution/spec.md in
this repository's internal planning); this README is the only consumer-facing
place they are documented, since openspec/ does not ship to npm.
