@pptx-editor.dev/core/store

Functions (65)

ancestorsOffunctionSource ↗

The ancestor chain of a node, root first, excluding the node itself.

declare function ancestorsOf(part: XmlPart, id: NodeId): Element[];

applyOpfunctionSource ↗

Apply one op. Pure: returns a new package or a typed rejection.

declare function applyOp(pkg: PptxPackage, op: PptxOp): OpResult;

applyOpsfunctionSource ↗

Apply ops in order against one working copy. All or nothing. Pure: never mutates pkg.

declare function applyOps(pkg: PptxPackage, ops: readonly PptxOp[]): OpResult;

asciiFoldfunctionSource ↗

ASCII-only lowercase fold. OPC part-name/extension equivalence is US-ASCII case-insensitive; a locale toLowerCase() mis-folds e.g. Turkish I/İ and could let two "equivalent" part names evade duplicate detection.

declare function asciiFold(s: string): string;

attrfunctionSource ↗

An attribute value, by conventional name (val, r:embed).

declare function attr(el: Element | undefined, name: string | QName): string | undefined;

binaryPartOffunctionSource ↗

The bytes of a binary part, or undefined.

declare function binaryPartOf(pkg: PptxPackage, partName: string | undefined): Uint8Array | undefined;

boolAttrfunctionSource ↗

An xsd:boolean attribute: 1/true and 0/false.

declare function boolAttr(el: Element | undefined, name: string | QName): boolean | undefined;

buildReplicaPartfunctionSource ↗

Rebuild one XML part from replicated records. Refuses an invalid tree as a whole; the caller reports the refusal to its session and never publishes a partial part.

declare function buildReplicaPart(partName: string, root: ReplicaElementSpec, context: ReplicaPartContext): ReplicaPartResult;

canonicalFingerprintfunctionSource ↗

SHA-256 of the canonical form: the structural fingerprint of one part.

declare function canonicalFingerprint(part: XmlPart): string;

canonicalFormfunctionSource ↗

The canonical form of a subtree: expanded names, attributes sorted by expanded name, no prefixes, no namespace declarations, no ids. Two trees with the same canonical form are the same document content however a writer spelled them.

declare function canonicalForm(node: Node): string;

canonicalPackageFingerprintfunctionSource ↗

One hash over all part fingerprints: the package's canonical fingerprint.

declare function canonicalPackageFingerprint(pkg: PptxPackage): string;

changedPartsfunctionSource ↗

Part names whose part object differs between two packages.

declare function changedParts(a: PptxPackage, b: PptxPackage): string[];

childfunctionSource ↗

The first child element with the given name.

declare function child(el: Element | undefined, name: string | QName): Element | undefined;

childrenfunctionSource ↗

All child elements with the given name.

declare function children(el: Element | undefined, name: string | QName): Element[];

contentTypeOffunctionSource ↗

The content type of a part, from [Content_Types].xml.

declare function contentTypeOf(pkg: PptxPackage, partName: string): string | undefined;

conventionalPrefixfunctionSource ↗

The conventional prefix for a namespace URI, used when the engine creates nodes.

declare function conventionalPrefix(ns: string): string | undefined;

decodeEmbeddedFontfunctionSource ↗

Decodes an EOT (as found in PPTX ppt/fonts/*.fntdata) into TrueType/OpenType sfnt bytes. Never throws: malformed or hostile input yields { ok: false, reason, detail }.

declare function decodeEmbeddedFont(bytes: Uint8Array, limits?: Partial<DecodeLimits>): DecodeEmbeddedFontResult;

elementsfunctionSource ↗

All child elements.

declare function elements(el: Element | undefined): Element[];

equivalentPartNamefunctionSource ↗

The existing part whose name is OPC-equivalent to partName (ASCII case-insensitive), or undefined. A new part name must not match one: the writer folds case, so Image1.png and image1.png are one ZIP entry and a package holding both cannot be saved.

declare function equivalentPartName(pkg: PptxPackage, partName: string): string | undefined;

escapeCssStringfunction

CSS string-escape a file-derived value (e.g. an @font-face family name or an inline style value). Emits \<hex> escapes for quotes, backslash, and controls so the value cannot break out of its CSS string.

declare function escapeCssString(value: string): string;

escapeXmlfunction

XML-escape a value for validated serialization back into owned OOXML.

declare function escapeXml(value: string): string;

escapeXmlAttributefunction

XML-escape a value bound for an ATTRIBUTE, preserving CR/LF/TAB across a re-parse.

declare function escapeXmlAttribute(value: string): string;

findfunctionSource ↗

Depth-first search for the first descendant element with the given name.

declare function find(el: Element | undefined, name: string | QName, maxDepth?: number): Element | undefined;

fontEmbeddingPermissionfunctionSource ↗

Maps OS/2 fsType to an embedding level. Per EOT section 4.1, when legacy fonts set several of bits 0-3 the least restrictive one wins (editable preview & print restricted). Bit 9 (bitmap embedding only) means no outlines may be used, which this engine treats as 'restricted' because it renders outlines only. Reserved bits are ignored.

declare function fontEmbeddingPermission(fsType: number): EmbeddingPermission;

imagePartForfunctionSource ↗

Whether a relationship target is an image the engine may decode (never external).

declare function imagePartFor(pkg: PptxPackage, source: string, rId: string): string | undefined;

imageSizefunctionSource ↗

The size of a PNG, JPEG, GIF, BMP, or WebP image, or null for anything else.

declare function imageSize(bytes: Uint8Array): ImageSize | null;

imageSizeEmufunctionSource ↗

The size in EMU at the image's resolution (96 DPI when it states none).

declare function imageSizeEmu(size: ImageSize): {
    cx: number;
    cy: number;
};

intAttrfunctionSource ↗

An integer attribute. Non-numeric or out-of-range values read as undefined, so a corrupt file falls back to the inherited value instead of propagating NaN into layout.

declare function intAttr(el: Element | undefined, name: string | QName): number | undefined;

isfunctionSource ↗

Whether an element has the given expanded name.

declare function is(el: Element | undefined, name: string | QName): el is Element;

isTemplateIndexfunctionSource ↗

Whether index names a template, not a slide.

isTemplateIndex: (index: number) => boolean

isTemplateReffunctionSource ↗

Whether ref is a slide master or a slide layout.

isTemplateRef: (ref: SlideRef | undefined) => ref is TemplateRef

kindOffunctionSource ↗

Assign a typed kind from the expanded name and the parent's kind.

declare function kindOf(ns: string, local: string, parent: NodeKind | null): NodeKind;

mainPresentationPartfunctionSource ↗

/ppt/presentation.xml (or wherever the package root points).

declare function mainPresentationPart(pkg: PptxPackage): string | undefined;

materializefunctionSource ↗

Materialize a spec into nodes with fresh ids, assigning typed kinds under parentKind (the kind context of the parent: for a markup-compatibility wrapper, the context it passes through). Kinds follow the reader's rule, so a created node has the kind it has after save and reopen. Only the documented spec fields are copied.

declare function materialize(spec: NodeSpec, parentKind: Element['kind'] | null, counter: {
    next: number;
}, depth?: number): Element;

metafileFormatfunctionSource ↗

The metafile format of bytes from its signature, or undefined for anything else.

declare function metafileFormat(bytes: Uint8Array): MetafileFormat | undefined;

metafileSizeEmufunctionSource ↗

The picture frame of a metafile in EMU: the EMF header frame (in 0.01 mm), or the bounding box of a placeable WMF over its units per inch. Undefined for a WMF with no placeable header, which states no physical size, and for an empty or implausible frame.

declare function metafileSizeEmu(bytes: Uint8Array): {
    cx: number;
    cy: number;
} | undefined;

nodeByIdfunctionSource ↗

Find a node by id.

declare function nodeById(part: XmlPart, id: NodeId): Node | undefined;

nodeIndexfunctionSource ↗

The id → location index of a part, built once per root identity.

declare function nodeIndex(part: XmlPart): ReadonlyMap<NodeId, Location>;

normalizePartNamefunctionSource ↗

Normalize a ZIP entry name or OPC part name into a canonical part name (/word/document.xml). Accepts leading-slash and no-leading-slash inputs; rejects traversal, dot segments, and the attack surface. Duplicate detection folds ASCII case (OPC part-name equivalence).

declare function normalizePartName(raw: string): NameResult;

packageFingerprintsfunctionSource ↗

Per-part canonical fingerprints. XML parts hash their canonical form; binaries hash bytes. Two packages with the same map are the same document content, however each was written.

declare function packageFingerprints(pkg: PptxPackage): ReadonlyMap<string, string>;

parseXmlPartfunctionSource ↗

Parse one XML part into the canonical tree. Never throws: the bytes are untrusted.

declare function parseXmlPart(partName: string, xml: string, limits?: XmlLimits): TreeResult;

pathfunctionSource ↗

Follow a chain of child names.

declare function path(el: Element | undefined, ...names: string[]): Element | undefined;

presentationInfofunctionSource ↗

Read the presentation structure. Memoized per package identity.

declare function presentationInfo(pkg: PptxPackage): PresentationInfo;

qfunctionSource ↗

The expanded name for a conventional prefix:local spelling. Unprefixed means no namespace (the usual case for attributes). Throws on an unknown prefix: this is for engine code, never for file data.

declare function q(name: string): QName;

readPptxfunctionSource ↗

Read a .pptx at the trust boundary. Never throws.

declare function readPptx(bytes: Uint8Array, limits?: ZipLimits): PackageResult;

readZipfunctionSource ↗

Inflate a ZIP archive with bounds + OPC name normalization. Entry name, count, archive ratio, and total-uncompressed-size limits are enforced BEFORE each entry is decompressed (via fflate's pre-inflation filter), so a zip bomb or a traversal name is rejected without ever being inflated. Keys are canonical part names.

The budgets count each entry's DECLARED sizes, and the declared size is also a hard cap on what the entry may produce: fflate inflates into a buffer of exactly originalSize bytes and never grows it, so an entry whose stream holds more than it declares is truncated (or refused as inflate-error), never expanded past its budget. A stored entry is copied at its compressed size, so the larger of its two declared sizes is what counts. Before any of this, the layout check refuses entries that lie outside the input or share bytes, so the compressed sizes the budgets count are real and add up to at most the input length.

declare function readZip(bytes: Uint8Array, limits?: ZipLimits): ZipReadResult;

relationshipsOffunctionSource ↗

All relationships of a source part. Unsafe or unresolvable targets are dropped.

declare function relationshipsOf(pkg: PptxPackage, source: string): readonly Relationship[];

relsPartNamefunctionSource ↗

The .rels part name for a source part (/ for the package).

declare function relsPartName(source: string): string;

relTargetfunctionSource ↗

The internal target of a relationship id, or undefined.

declare function relTarget(pkg: PptxPackage, source: string, id: string): string | undefined;

relTargetByTypefunctionSource ↗

The first internal target of a relationship type.

declare function relTargetByType(pkg: PptxPackage, source: string, type: string): string | undefined;

resolveInternalTargetfunctionSource ↗

Resolve an internal relationship target against its owner part, without escaping the package root. ownerPartName is a canonical part name; the base is its containing folder. A leading "/" target is package-absolute. ./.. segments resolve on a stack; popping above root is traversal-escape.

declare function resolveInternalTarget(ownerPartName: string, rawTarget: string): NameResult;

sanitizeHreffunction

Project a file-derived URL for a DOM/navigation sink. Strips embedded tab/LF/CR (used to smuggle java\nscript:), allows relative URLs and the scheme allowlist, and renders everything else inert. Never fetches.

declare function sanitizeHref(raw: string): HrefProjection;

semanticDigestfunctionSource ↗

Save, reopen, and fingerprint: the semantic digest. Equal digests before and after a round trip prove save loses nothing the model carries.

declare function semanticDigest(pkg: PptxPackage): string;

serializeXmlPartfunctionSource ↗

Serialize one part to an XML string with the standard OOXML declaration.

declare function serializeXmlPart(part: XmlPart): string;

shapeTreeOffunctionSource ↗

The p:cSld/p:spTree of a slide, layout, or master part.

declare function shapeTreeOf(pkg: PptxPackage, partName: string | undefined): Element | undefined;

slideRefAtfunctionSource ↗

The slide at index, or the template at index for a template index. Code that reads a part by index (the cascade, the session, the commands) reads it here, so the slide master view edits a layout or a master as a slide.

declare function slideRefAt(pkg: PptxPackage, index: number): SlideRef | undefined;

specOffunctionSource ↗

Describe an existing subtree as a spec (used by duplicate/copy commands).

declare function specOf(el: Element): NodeSpec;

templateIndexOffunctionSource ↗

The index of a template part in the slide master view, or -1.

declare function templateIndexOf(pkg: PptxPackage, partName: string): number;

templateRefsfunctionSource ↗

The deck's masters, each followed by its layouts. Memoized per package identity.

declare function templateRefs(pkg: PptxPackage): readonly TemplateRef[];

textOffunctionSource ↗

Concatenated descendant text.

declare function textOf(el: Element | undefined): string;

updateNodefunctionSource ↗

Replace the node with id by replace(node), rebuilding the path to the root.

declare function updateNode(part: XmlPart, id: NodeId, replace: (node: Node) => Node, nextId?: number): XmlPart | null;

withAttrfunctionSource ↗

Set or remove an attribute, preserving attribute order.

declare function withAttr(el: Element, name: {
    ns: string;
    local: string;
    prefix?: string;
}, value: string | null): Element;

writePptxfunctionSource ↗

Write the package back to .pptx bytes. XML parts re-emit normalized; binaries verbatim. The bytes own an ArrayBuffer, so they are a valid BlobPart as they are. A deferred part that has not been read parses here; one that did not parse refuses the write (it throws), so a stand-in never leaves the session.

declare function writePptx(pkg: PptxPackage): Uint8Array<ArrayBuffer>;

writeZipfunctionSource ↗

Deflate a set of canonical-part-name - bytes into a ZIP archive. Every part name is re-validated through the OPC normalization profile before writing, so a traversal/encoded/normalized-alias name from an untrusted serialized model can never be smuggled into a ZIP entry (write-side path-traversal guard). The name is encoded, never decoded again: the entry reads back as exactly the same part name. Already-compressed media is stored, not deflated a second time. The archive stays within the ratio cap of [DEFAULT_ZIP_LIMITS](DEFAULT_ZIP_LIMITS), so a document that opened opens again.

declare function writeZip(entries: ReadonlyMap<string, Uint8Array>): Uint8Array;

xmlPartOffunctionSource ↗

The XML tree of a part, or undefined.

declare function xmlPartOf(pkg: PptxPackage, partName: string | undefined): XmlPart | undefined;

Classes (1)

PptxDocumentStoreclassSource ↗

The transactional document store.

declare class PptxDocumentStore
MemberTypeSummary
(constructor)Constructs a new instance of the `PptxDocumentStore` class
canRedoboolean
canUndoboolean
closeHistoryGroupClose the open history group, if any: the next transaction records its own entry. The store does this itself for another token, none, undo, and redo. A caller does it for a boundary the store cannot see, such as a selection change.
dryRunValidate ops, and the parts they leave, without committing.
packagePptxPackageThe current package. Reference-stable until the next commit.
redoRedo one entry. Returns the selection captured after it, if any.
replacePartsPublish parts rebuilt by a collaboration replica as one `remote` revision: `parts` replace or add parts, `removed` names parts to drop. Like a remote transaction, it makes no history entry and clears local undo and redo. The replica validates XML parts with `buildReplicaPart`; this method checks part names and that the content types part stays.
revisionnumberMonotonic revision, bumped by every commit (including undo and redo).
selectionForRedoThe selection that `redo()` restores next: the newest redo entry's `selectionAfter`.
selectionForUndoThe selection that `undo()` restores next: the newest entry's `selectionBefore`.
setSelectionAfterSet the selection after the newest history entry, for a caller that knows it only after the commit published (a command that adds a slide). Returns false, and changes nothing, when the newest entry did not produce the current package.
subscribeSubscribe to commits. Returns the unsubscribe function.
transactApply ops atomically and publish one change.
undoUndo one entry. Returns the selection captured before it, if any.

Interfaces (24)

Attr_2interface

One attribute, carried verbatim.

interface Attr
MemberTypeSummary
localstring
nsstring
prefix?string
valuestring

AttrNameinterfaceSource ↗

An expanded attribute name with an optional preferred prefix.

interface AttrName
MemberTypeSummary
localstring
nsstring
prefix?string

DocumentStoreOptionsinterfaceSource ↗

Options for a store.

interface DocumentStoreOptions
MemberTypeSummary
onListenerError?(error: unknown, change: StoreChange) => voidCalled when a listener throws. Delivery continues to the other listeners either way. The default reports the error to the host (`reportError` where it exists, otherwise an uncaught error on a microtask), so it is never swallowed.
paragraphText?ParagraphTextThe text of a slide paragraph as the editor counts it: a threaded comment on a range of text counts characters this way (`comment-anchors.ts`). The default counts the stored text.

Element_2interface

An element node. Immutable; edits produce new nodes along the changed path.

interface Element
MemberTypeSummary
attrsreadonly Attr[]
childrenreadonly Node[]
idNodeId
kindNodeKind
localstring
nsstring
nsDeclsreadonly NamespaceDecl[]
prefix?string
type'element'

EmbeddedFontInfointerfaceSource ↗

interface EmbeddedFontInfo
MemberTypeSummary
charsetnumber
compressedboolean
eotVersionnumber
familyNamestring
flagsnumberEOT processing flags.
fsTypenumberOS/2 fsType copy from the EOT header.
fullNamestring
italicboolean
numGlyphsnumber
permissionEmbeddingPermission
styleNamestring
subsetboolean
versionNamestring
warningsstring[]Non-fatal observations (e.g. dropped optional tables, unconsumed stream bytes).
weightnumber
xorEncryptedboolean

EmbeddedFontRefinterfaceSource ↗

One embedded font family with the part of each face.

interface EmbeddedFontRef
MemberTypeSummary
facesreadonly { readonly style: 'regular' | 'bold' | 'italic' | 'boldItalic'; readonly partName: string; }[]
typefacestring

ImageSizeinterface

Pixel size and resolution. dpiX/dpiY are undefined when the file states none.

interface ImageSize
MemberTypeSummary
dpiX?number
dpiY?number
heightnumber
widthnumber

NamespaceDeclinterfaceSource ↗

One xmlns declaration authored on an element. prefix is '' for the default namespace.

interface NamespaceDecl
MemberTypeSummary
prefixstring
uristring

NodeSpecinterfaceSource ↗

A serializable element description, used by ops to create nodes. Ids are minted on apply.

interface NodeSpec
MemberTypeSummary
attrs?readonly { ns: string; local: string; prefix?: string; value: string; }[]
children?readonly (NodeSpec | { readonly text: string; })[]
localstring
nsstring
nsDecls?readonly { prefix: string; uri: string; }[]
prefix?string

PptxPackageinterfaceSource ↗

The whole package. Immutable: an edit produces a new package that shares untouched parts.

interface PptxPackage
MemberTypeSummary
partsReadonlyMap<string, PackagePart>

PresentationInfointerfaceSource ↗

The structural summary of a presentation.

interface PresentationInfo
MemberTypeSummary
embeddedFontsreadonly EmbeddedFontRef[]
heightnumber
notesMasterPart?string
presentationPartstring
slidesreadonly SlideRef[]
widthnumberSlide size in EMU.

QNameinterfaceSource ↗

An expanded element or attribute name.

interface QName
MemberTypeSummary
localstring
nsstring

RelationshipinterfaceSource ↗

One relationship, resolved against its source part.

interface Relationship
MemberTypeSummary
externalboolean
externalTarget?stringExternal: the raw URI, retained verbatim and NEVER fetched.
idstring
relativeTarget?stringExternal: a relative path to another file (`report.pdf`, `../docs/a%20b.pdf`), retained verbatim. It is NEVER fetched or opened: a page cannot reach the file.
target?stringInternal: the resolved part name. External: undefined.
typestring

ReplicaElementSpecinterfaceSource ↗

A replicated element: a node spec whose nodes carry shared keys.

interface ReplicaElementSpec extends Omit<NodeSpec, 'children'>
MemberTypeSummary
children?readonly (ReplicaElementSpec | ReplicaTextSpec)[]Child elements and text nodes, in document order.
keystringThe logical key every peer uses for this node.

ReplicaPartContextinterfaceSource ↗

What the replica already knows about the part.

interface ReplicaPartContext
MemberTypeSummary
idOfThe store id this replica uses for a key, if any.
nextIdnumberThe part's id counter: fresh ids start here.

ReplicaTextSpecinterfaceSource ↗

A replicated text node: its shared key and its text.

interface ReplicaTextSpec
MemberTypeSummary
keystringThe logical key every peer uses for this node.
textstringThe text content.

SlideRefinterfaceSource ↗

One slide in presentation order.

interface SlideRef
MemberTypeSummary
hiddenboolean`p:sld/@show="0"`.
layoutPart?string
masterPart?string
nodeIdstringThe `p:sldId` node id in presentation.xml, for reorder ops.
notesPart?string
partNamestring
rIdstringThe `r:id` in presentation.xml.rels.
sldIdstring`p:sldId/@id`, stable across reorders.
themePart?string

StoreChangeinterfaceSource ↗

What a committed transaction changed.

interface StoreChange
MemberTypeSummary
fromRevisionnumber
label?string
merged?booleanTrue when a recorded transaction continued the open history group (`historyGroup`). It is also true for the first frame after a `remote` commit cleared the stacks in the middle of a gesture: the local entry is new, but the gesture goes on, so a collaboration session that owns undo keeps it one step.
ops?readonly PptxOp[]The ops of a `user` or `remote` transaction, as committed. Undo and redo restore snapshots and carry none. A collaboration replica sends the `user` ops to its peers.
origin'undo' | 'redo' | 'load' | TransactOrigin
partsreadonly string[]Part names whose content changed. Layout re-does only what these touch.
toRevisionnumber

TemplateRefinterfaceSource ↗

A slide master or a slide layout, as a slide of the slide master view. Its partName is the master or layout part. A layout's masterPart is its master; a master has none, and neither has a layoutPart.

interface TemplateRef extends SlideRef
MemberTypeSummary
namestring`p:cSld/@name`, or the theme name of a master without one.
template'master' | 'layout'

TextEditAtinterfaceSource ↗

Where a text edit started, before the edit: the slide part, the shape node (p:sp), and the paragraph and offset of the start of the selection.

interface TextEditAt
MemberTypeSummary
nodestring
offsetnumber
paragraphnumber
partstring

TextNodeinterfaceSource ↗

A text node. Only retained where it is significant (see [isSignificantText](isSignificantText)).

interface TextNode
MemberTypeSummary
idNodeId
type'text'
valuestring

TransactOptionsinterfaceSource ↗

Options for one transaction.

interface TransactOptions
MemberTypeSummary
historyGroup?HistoryGroupThe gesture this transaction belongs to: consecutive transactions that carry the SAME token extend one history entry (the package from before the first, the selection after the latest). Another token, none, undo, or redo closes the group. See [HistoryGroup](HistoryGroup).
label?stringA short label for the history entry (`move`, `typing`).
origin?TransactOriginWhat made the transaction; `user` when omitted. See [TransactOrigin](TransactOrigin).
recordsHistory?booleanWhether this transaction enters the undo stack. Omitted: `true` for `user`, `false` for `projection` and `awareness`. A `remote` transaction never records history and always clears it.
selectionAfter?unknownOpaque selection state restored when this entry is redone. A later frame of an open group replaces it; a frame that omits it keeps the previous one.
selectionBefore?unknownOpaque selection state restored when this entry is undone.
textAt?TextEditAtWhere a text edit started, before it. Animation ranges that name paragraphs use it to tell identical paragraphs apart. Omitted: the text alone decides.

XmlPartinterfaceSource ↗

One parsed XML part. nextId is the id counter for nodes created by later edits.

interface XmlPart
MemberTypeSummary
nextIdnumber
partNamestring
rootElement

ZipLimitsinterface

Archive caps: entry count, total decompressed bytes, and the decompression ratio.

interface ZipLimits
MemberTypeSummary
maxEntriesnumber
maxRatio?numberMax ratio of the archive's total declared uncompressed bytes to the archive's own byte length (zip-bomb guard). Applies once that total exceeds 8 MiB, so a small archive is never refused for compressing well.
maxTotalBytesnumberMax total UNCOMPRESSED bytes across the archive.

Type aliases (23)

DecodeEmbeddedFontResulttypeSource ↗

type DecodeEmbeddedFontResult = {
    ok: true;
    sfnt: Uint8Array;
    info: EmbeddedFontInfo;
} | FontRejection;

EmbeddingPermissiontypeSource ↗

type EmbeddingPermission = 'installable' | 'editable' | 'preview-print' | 'restricted';

HistoryGrouptypeSource ↗

The identity of one continuous user gesture, for history grouping.

A drag, a typing burst, or a live picker applies every intermediate value, so the slide updates under the pointer, and each of those writes is a transaction in its own right: it must render, and a collaborator must receive it. They are not separate undo steps. The caller mints one token when the gesture starts (Symbol('drag')) and passes it with every write until the gesture ends.

A symbol, not a string, so two unrelated callers cannot collide by naming their gesture the same way, and a token cannot outlive its gesture through serialization.

type HistoryGroup = symbol;

KnownPrefixtypeSource ↗

A conventional prefix the engine knows.

type KnownPrefix = keyof typeof NS;

MetafileFormattype

The two metafile formats.

type MetafileFormat = 'emf' | 'wmf';

Node_2type

type Node = Element | TextNode;

NodeIdtypeSource ↗

A node's stable identity within its part.

type NodeId = string;

NodeKindtypeSource ↗

The typed kinds. Everything not listed is generic, carried verbatim.

A kind is a LABEL on the same node shape, assigned from the expanded name and the parent's kind. Layout reads typed kinds; everything else only has to survive save.

type NodeKind = 'generic' | 'spTree' | 'grpSp' | 'sp' | 'pic' | 'cxnSp' | 'graphicFrame' | 'txBody' | 'paragraph' | 'run' | 'break' | 'field' | 'text';

OpRejectiontypeSource ↗

Why an op was refused.

Name and namespace refusals: invalid-name (not an XML NCName), reserved-name (the xmlns prefix or namespace, the xml prefix bound to another namespace, an unprefixed xmlns attribute), invalid-namespace (a URI that is not valid XML text, or a binding XML 1.0 cannot express), and duplicate-name (one attribute or prefix twice on an element). too-deep refuses a node the reader would refuse on reopen. malformed-op refuses an op whose JSON shape is wrong.

Commit refusals: tree-invariant (a touched part's own tree breaks a [TreeInvariantRule](TreeInvariantRule)) and package-invariant (a [PackageInvariantRule](PackageInvariantRule), which reads more than one part). The store checks the package a transaction leaves, in the parts it touched, and refuses only a violation the transaction introduced. part names the part the violation is in, rule the broken rule, and detail what is wrong, in English, for logs. Such a refusal means the ops would save a broken package; undo and redo are never refused.

type OpRejection = {
    readonly code: 'unknown-part';
    readonly part: string;
} | {
    readonly code: 'not-xml-part';
    readonly part: string;
} | {
    readonly code: 'unknown-node';
    readonly part: string;
    readonly node: string;
} | {
    readonly code: 'not-an-element';
    readonly node: string;
} | {
    readonly code: 'invalid-text';
    readonly detail: string;
} | {
    readonly code: 'invalid-index';
    readonly index: number;
} | {
    readonly code: 'root-node';
    readonly node: string;
} | {
    readonly code: 'cycle';
    readonly node: string;
} | {
    readonly code: 'part-exists';
    readonly part: string;
} | {
    readonly code: 'unsafe-part-name';
    readonly part: string;
} | {
    readonly code: 'invalid-part-content';
    readonly part: string;
    readonly detail: string;
} | {
    readonly code: 'invalid-name';
    readonly name: string;
    readonly detail: string;
} | {
    readonly code: 'reserved-name';
    readonly name: string;
    readonly detail: string;
} | {
    readonly code: 'invalid-namespace';
    readonly uri: string;
    readonly detail: string;
} | {
    readonly code: 'duplicate-name';
    readonly name: string;
} | {
    readonly code: 'too-deep';
    readonly detail: string;
} | {
    readonly code: 'malformed-op';
    readonly detail: string;
} | {
    readonly code: 'unsupported';
    readonly detail: string;
} | {
    readonly code: 'tree-invariant';
    readonly part: string;
    readonly rule: TreeInvariantRule;
    readonly detail: string;
} | {
    readonly code: 'package-invariant';
    readonly part: string;
    readonly rule: PackageInvariantRule;
    readonly detail: string;
};

OpResulttypeSource ↗

type OpResult = {
    readonly ok: true;
    readonly pkg: PptxPackage;
} | {
    readonly ok: false;
    readonly rejection: OpRejection;
};

PackageInvariantRuletypeSource ↗

A rule that reads more than one part, checked when a transaction commits (package-invariant):

- slide-list: each p:sldId, p:sldMasterId, and p:notesMasterId names, by r:id, a relationship of the presentation part to a part of the package (ECMA-376 Part 1, 19.2.1.33). - relationship-target: each internal relationship targets a part of the package (ECMA-376 Part 2, relationships). - content-type: each part has a content type, by Override or by Default (ECMA-376 Part 2, content types).

type PackageInvariantRule = 'slide-list' | 'relationship-target' | 'content-type';

PackageParttypeSource ↗

One part of the package.

type PackagePart = {
    readonly kind: 'xml';
    readonly partName: string;
    readonly xml: XmlPart;
} | {
    readonly kind: 'binary';
    readonly partName: string;
    readonly bytes: Uint8Array;
};

PackageRejectiontypeSource ↗

Why a file was refused.

type PackageRejection = {
    readonly reason: 'zip';
    readonly detail: string;
} | {
    readonly reason: 'no-content-types';
} | {
    readonly reason: 'not-a-presentation';
} | {
    readonly reason: 'xml';
    readonly partName: string;
    readonly detail: TreeRejection;
} | {
    readonly reason: 'too-many-parts';
};

PackageResulttypeSource ↗

type PackageResult = {
    readonly ok: true;
    readonly pkg: PptxPackage;
} | {
    readonly ok: false;
    readonly error: PackageRejection;
};

ParagraphTexttypeSource ↗

The text of paragraph p of slide part, as the editor counts it, so that a range moves in the units it was written in. The store has no math or field model; the editor names its own (DocumentStoreOptions.paragraphText).

type ParagraphText = (p: Element, pkg: PptxPackage, part: string) => string;

PptxOptypeSource ↗

One serializable document operation.

type PptxOp = {
    readonly op: 'setAttribute';
    readonly part: string;
    readonly node: string;
    readonly name: AttrName;
    readonly value: string | null;
} | {
    readonly op: 'setText';
    readonly part: string;
    readonly node: string;
    readonly text: string;
} | {
    readonly op: 'insertNode';
    readonly part: string;
    readonly parent: string;
    readonly index: number;
    readonly spec: NodeSpec;
} | {
    readonly op: 'removeNode';
    readonly part: string;
    readonly node: string;
} | {
    readonly op: 'moveNode';
    readonly part: string;
    readonly node: string;
    readonly parent: string;
    readonly index: number;
} | {
    readonly op: 'addPart';
    readonly part: string;
    readonly xml?: string;
    readonly base64?: string;
} | {
    readonly op: 'removePart';
    readonly part: string;
};

PptxOpKindtypeSource ↗

type PptxOpKind = PptxOp['op'];

ReplicaPartResulttypeSource ↗

The outcome of a rebuild. ids maps every key in the part to its store id.

type ReplicaPartResult = {
    readonly ok: true;
    readonly part: XmlPart;
    readonly ids: ReadonlyMap<string, NodeId>;
} | {
    readonly ok: false;
    readonly reason: string;
};

TransactOrigintypeSource ↗

What made a transaction.

- user: a local edit. It records a history entry. - remote: ops from a collaboration peer. No history entry; the local undo and redo stacks are cleared, because undo restores whole snapshots and would revert the peer's edit. - projection: the view reconciled with state the store already holds. It publishes a revision so consumers re-derive, but it is not a user intent: no history entry, and the undo and redo stacks stay. A collaboration replica does not send it to peers, so commit with it only what each replica derives on its own; a peer never sees any other change. - awareness: presence-style state, handled as projection.

type TransactOrigin = 'user' | 'remote' | 'projection' | 'awareness';

TransactResulttypeSource ↗

The outcome of a transaction.

type TransactResult = {
    readonly ok: true;
    readonly revision: number;
} | {
    readonly ok: false;
    readonly rejection: OpRejection;
};

TreeInvariantRuletypeSource ↗

A rule of one part's own tree that the store checks when a transaction commits (tree-invariant):

- table-grid: every a:tr holds one a:tc per a:gridCol (ECMA-376 Part 1, 21.1.3.16). - table-merge: gridSpan and rowSpan stay inside the grid, merged blocks do not overlap, and the hMerge and vMerge markers agree with the blocks (21.1.3.16). - shape-id: p:cNvPr/@id is unique among the shapes of one slide, layout, or master.

type TreeInvariantRule = 'table-grid' | 'table-merge' | 'shape-id';

TreeRejectiontypeSource ↗

Why a part was refused at the trust boundary.

type TreeRejection = XmlRejection | 'no-root' | 'unbound-prefix' | 'bad-name';

TreeResulttypeSource ↗

type TreeResult = {
    readonly ok: true;
    readonly part: XmlPart;
} | {
    readonly ok: false;
    readonly reason: TreeRejection;
    readonly detail?: string;
};

Variables (11)

CONTENT_TYPEconstSource ↗

Content types the engine writes.

CONTENT_TYPE: Readonly<{
    presentation: "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml";
    slide: "application/vnd.openxmlformats-officedocument.presentationml.slide+xml";
    notesSlide: "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml";
    relationships: "application/vnd.openxmlformats-package.relationships+xml";
}>

CONTENT_TYPES_PARTconstSource ↗

CONTENT_TYPES_PART = "/[Content_Types].xml"

DEFAULT_ZIP_LIMITSconstSource ↗

The archive caps in force when a host configures none. Conservative and finite.

DEFAULT_ZIP_LIMITS: ZipLimits

EMU_PER_INCHconstSource ↗

EMUs per inch, point, and CSS pixel (96 dpi).

EMU_PER_INCH = 914400

EMU_PER_POINTconstSource ↗

EMU_PER_POINT = 12700

EMU_PER_PXconstSource ↗

EMU_PER_PX = 9525

NSconstSource ↗

NS: Readonly<{
    p: "http://schemas.openxmlformats.org/presentationml/2006/main";
    a: "http://schemas.openxmlformats.org/drawingml/2006/main";
    r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
    mc: "http://schemas.openxmlformats.org/markup-compatibility/2006";
    p14: "http://schemas.microsoft.com/office/powerpoint/2010/main";
    p15: "http://schemas.microsoft.com/office/powerpoint/2012/main";
    a14: "http://schemas.microsoft.com/office/drawing/2010/main";
    a16: "http://schemas.microsoft.com/office/drawing/2014/main";
    asvg: "http://schemas.microsoft.com/office/drawing/2016/SVG/main";
    m: "http://schemas.openxmlformats.org/officeDocument/2006/math";
    pic: "http://schemas.openxmlformats.org/drawingml/2006/picture";
    c: "http://schemas.openxmlformats.org/drawingml/2006/chart";
    dgm: "http://schemas.openxmlformats.org/drawingml/2006/diagram";
    dsp: "http://schemas.microsoft.com/office/drawing/2008/diagram";
    rel: "http://schemas.openxmlformats.org/package/2006/relationships";
    ct: "http://schemas.openxmlformats.org/package/2006/content-types";
    xml: "http://www.w3.org/XML/1998/namespace";
    xmlns: "http://www.w3.org/2000/xmlns/";
}>

PPTX_OP_KINDSconstSource ↗

Every op kind, for coverage gates.

PPTX_OP_KINDS: readonly PptxOpKind[]

PPTX_ZIP_LIMITSconstSource ↗

The archive caps of a presentation when a host configures none. A slide is at least two entries (its part and its relationships), and four with speaker notes, so the format-neutral cap of 10,000 entries refused a deck of 5,000 slides. This cap admits about 10,000 slides with notes. It stays below ZIP_HARD_LIMITS, and the byte and ratio caps do not change.

PPTX_ZIP_LIMITS: ZipLimits

RELconstSource ↗

Relationship types the engine follows.

REL: Readonly<{
    officeDocument: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
    slide: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
    slideLayout: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
    slideMaster: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
    theme: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
    notesSlide: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide";
    notesMaster: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster";
    image: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
    font: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/font";
    hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
    chart: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
    diagramDrawing: "http://schemas.microsoft.com/office/2007/relationships/diagramDrawing";
    tableStyles: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles";
    presProps: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps";
    viewProps: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps";
}>

TEMPLATE_BASEconstSource ↗

The first template index. Slide indices stay below it: a deck never holds this many slides (the open limits allow far fewer).

TEMPLATE_BASE: number