@stesura/core/utils
Framework-free helpers a host needs: unit conversion, exact line heights, URL safety, upload validation and randomUUID. No ProseMirror dependency.
Functions & values
cmToInches
Parameters:| Name | Type | Default |
|---|---|---|
cm | string | number |
cmToPoints
Rounded to whole points.
| Name | Type | Default |
|---|---|---|
cm | string | number |
cmToPx
Exact, quantized (quantizePx).
cmToTwips
Rounded to whole twips.
| Name | Type | Default |
|---|---|---|
cm | string | number |
cssUnitToPixels
A CSS length in px. baseSize (px) resolves em and %; rem is fixed at
16px and viewport units assume 1920×1080. Unitless or unknown units count as
px; unparseable input returns baseSize.
| Name | Type | Default |
|---|---|---|
value | string | number | |
baseSize | number |
cssUnitToPoints
A CSS length in points, snapped to 0.5pt. Handles px, pt, cm, mm and in; anything else counts as px, and unparseable input returns 10.
| Name | Type | Default |
|---|---|---|
value | string | number |
DISPLAY_UNIT_LABELS
The abbreviation shown beside a display-unit value. Not localised: these are the SI and typographic symbols, which do not translate.
| Name | Type | Default |
|---|---|---|
cm | string | |
inches | string | |
pt | string | |
px | string |
displayUnitToPx
A value in the user’s display unit, as quantized px: 1.25cm stores 47.24409
and exports as Word’s 709 twips. Editing surfaces store px and show
displayUnits, so the pair lives here rather than in each picker.
| Name | Type | Default |
|---|---|---|
unit | DisplayUnit | |
value | number |
diffLayoutManifests
How two layouts differ: the pages that start on a different block (a page only one of them has counts too) and the first of them, 0-based; null when they match.
| Name | Type | Default |
|---|---|---|
expected | ExportLayoutManifest | |
actual | ExportLayoutManifest |
EXPORT_LOSSES_HEADER
The PDF response header carrying ExportLosses: the render succeeds
without what it couldn’t load, and the exporting user should know.
formatDisplayUnit
How a display unit’s value is rendered in an input: points to 1dp, else 2dp.
| Name | Type | Default |
|---|---|---|
value | number | |
unit | DisplayUnit |
formatExportLosses
images=2, files=0, glyphs=0, layout=0, or null when nothing was lost.
| Name | Type | Default |
|---|---|---|
losses | ExportLosses |
inchesToCm
Parameters:| Name | Type | Default |
|---|---|---|
inches | string | number |
inchesToPoints
Rounded to whole points.
| Name | Type | Default |
|---|---|---|
inches | string | number |
inchesToPx
Exact, quantized (quantizePx).
inchesToTwips
Rounded to whole twips.
| Name | Type | Default |
|---|---|---|
inches | string | number |
isEmbedUrlAllowed
Whether the host may fetch or frame this URL.
allowedHosts undefined → allowed unless isForbiddenEmbedUrl says
otherwise; defined → the URL’s host (including a non-default port) must
also appear in the list. The never-allow class applies either way, so an
allowlist can only narrow, never widen.
| Name | Type | Default |
|---|---|---|
url | string | |
allowedHosts | string[] |
isForbiddenEmbedUrl
Whether a URL is in the never-allow class: no configuration may permit it.
Covers non-http(s) schemes, loopback, RFC1918 private ranges, link-local
(including the cloud-metadata endpoint), CGNAT, IPv6 ULA, .local /
.internal hostnames, and anything new URL cannot parse. Unparseable
input is forbidden rather than passed through — a protocol-relative
//host/x has no origin to judge.
| Name | Type | Default |
|---|---|---|
url | string |
lineHeightsMatch
Parameters:| Name | Type | Default |
|---|---|---|
a | number | |
b | number |
parseExportLosses
The losses a EXPORT_LOSSES_HEADER value names; null when it names none.
| Name | Type | Default |
|---|---|---|
header | string |
parsePtLineHeight
The pt number of an exact "Xpt" line height; null for any other shape.
| Name | Type | Default |
|---|---|---|
value | unknown |
pointToCm
Parameters:| Name | Type | Default |
|---|---|---|
pt | string | number |
pointToInches
Parameters:| Name | Type | Default |
|---|---|---|
pt | string | number |
pointToPx
Exact, quantized (quantizePx).
pointToTwips
Rounded to whole twips.
| Name | Type | Default |
|---|---|---|
pt | string | number |
ptToPxExact
Exact line height in px. Unrounded on purpose: the measurement must match what the browser lays out from the same CSS value, and rounding 13pt to 17px would drift by a third of a px per line.
| Name | Type | Default |
|---|---|---|
pt | number |
pxToCm
Parameters:| Name | Type | Default |
|---|---|---|
px | string | number |
pxToDisplayUnit
Inverse of displayUnitToPx; pt keeps a decimal (Word shows 7.5 pt).
| Name | Type | Default |
|---|---|---|
unit | DisplayUnit | |
value | number |
pxToInches
Parameters:| Name | Type | Default |
|---|---|---|
px | string | number |
pxToPoints
Rounded to whole points.
| Name | Type | Default |
|---|---|---|
px | string | number |
pxToTwips
Rounded to whole twips: the one rounding a stored px length gets, at export.
randomUUID
crypto.randomUUID() with a getRandomValues fallback.
randomUUID is secure-context-only: on a plain-HTTP deploy (intranet
tooling, LAN preview) it is undefined and every id-minting path — new
sections, tables, numbering instances, the uniqueId plugin — would crash the
editor outright. getRandomValues has no such restriction, so the fallback
builds a standard v4 UUID from it with the same entropy.
safeNormalizeUrl
Canonicalizes a URL (forcing https unless told otherwise) and strips
dangerous schemes; null when nothing safe is left. For every path that
renders or follows a user-supplied URL. Refuses everything
safeRawUrl refuses.
Document-relative URLs are kept verbatim: normalizeUrl has no base to resolve
them against and throws, and resolving against our own origin would turn a
pasted #section into a link to our host.
| Name | Type | Default |
|---|---|---|
url | string | |
__1 | SafeNormalizeUrlOptions |
safeRawUrl
Null for dangerous schemes (javascript:, vbscript:, data:, file:) and non-strings; otherwise the URL verbatim — for storage paths that must round-trip exactly (e.g. markdown import; sanitizeUrl would canonicalize via new URL()). Render paths still apply safeNormalizeUrl.
| Name | Type | Default |
|---|---|---|
url | string |
snapToHalfPoint
Round a point value to the nearest 0.5 (e.g. 10.7 → 10.5, 10.8 → 11, 10.2 → 10).
| Name | Type | Default |
|---|---|---|
pt | number |
sniffDocumentType
Identify a document upload by content. The magic bytes decide the CLASS —
PDF, ZIP container, or plain text — and nothing outside those three is
accepted. Within the ZIP class the name’s extension picks the Office type:
telling docx from xlsx by content means reading the archive’s central
directory, and the distinction only ever changes a label and a storage
Content-Type that no browser executes. A ZIP under any other name is
served as application/zip. Text is served as text/plain whatever the
extension says, so a .html upload can never come back as a page.
| Name | Type | Default |
|---|---|---|
file | Blob | |
name | string |
sniffImageBytes
Identify an image by its magic bytes. Needs at least the first 12 bytes; returns null for anything that isn’t a supported raster format.
| Name | Type | Default |
|---|---|---|
bytes | Uint8Array<ArrayBufferLike> |
sniffImageType
Identify a supported image File/Blob by its magic bytes.
| Name | Type | Default |
|---|---|---|
file | Blob |
sniffPdf
True if the blob starts with the %PDF- signature.
| Name | Type | Default |
|---|---|---|
file | Blob |
toLineHeightAtLeast
A lineHeightAtLeast value (types/formatting LineHeightAtLeast), or null
for anything else. JSON, collab and the clipboard reach the attr unchecked.
| Name | Type | Default |
|---|---|---|
value | unknown |
twipsToCm
Parameters:| Name | Type | Default |
|---|---|---|
twips | string | number |
twipsToInches
Parameters:| Name | Type | Default |
|---|---|---|
twips | string | number |
twipsToPt
Parameters:| Name | Type | Default |
|---|---|---|
twips | string | number |
twipsToPx
Exact, quantized (quantizePx).
validateDocumentFile
Validate a candidate document upload: size cap plus content class. On
success returns the content type to store the file under — never
file.type.
| Name | Type | Default |
|---|---|---|
file | Blob | |
options | { maxBytes?: number; name?: string; } |
validateImageFile
Validate a candidate image upload: size cap plus magic-byte sniff.
On success returns the sniffed content type — callers must use it (not
file.type) for storage Content-Type and any branching.
| Name | Type | Default |
|---|---|---|
file | Blob | |
options | { maxBytes?: number; } |
validatePdfFile
Validate a candidate PDF upload: size cap plus %PDF- signature.
| Name | Type | Default |
|---|---|---|
file | Blob | |
options | { maxBytes?: number; } |
Types
FileValidationResult
| Name | Type | Default |
|---|---|---|
ok | boolean |
Other exports
| Export | Type | Description |
|---|---|---|
LINE_HEIGHT_PT_MAX | 600 | |
LINE_HEIGHT_PT_MIN | 0.7 | Exact line-height bounds (pt); the min is Word’s own. |
LINE_HEIGHT_MULTIPLIER_MIN | 0.06 | Multiplier bounds; the min is Word’s floor for “Multiple”. |
LINE_HEIGHT_MULTIPLIER_MAX | 5 | Beyond it a value is corrupt, not a layout. |
MAX_DOCUMENT_BYTES | number | Size cap for a document upload (the file node). |
MAX_IMAGE_BYTES | number | Default upload size cap, aligned with the upload-block dropzone. |
SNIFFED_IMAGE_TYPES | readonly SniffedImageType[] | Every image type the sniffer recognises; the default allowedImageTypes. |
SniffedDocumentType | "application/pdf" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "application/zip" | "text/plain" | |
SniffedImageType | "image/png" | "image/jpeg" | "image/gif" | "image/webp" |
Text folding
foldAccents(text) folds a string’s accents code point by code point (é → e, a combining mark
dropped), keeping offsets one to one; foldCase(codePoint) is the simple case fold the iu regexp
flag compares by. Search, the approximate matcher and redaction’s leak check share them.