feat(document-api): metadata.* for anchored payloads (SD-3104)#3351
Conversation
…SD-3104)
Adds the editor.doc.metadata.* operation surface: attach, list, get,
update, remove, resolve. Backed by hidden inline content controls
(anchors) + namespaced Custom XML Data Storage Parts (payload), but
consumers see one operation set.
v1 is opinionated:
- anchor is a hidden inline content control; w:tag carries the stable id
- payload is JSON (any JSON-serializable value), stored opaquely inside
a SuperDoc-owned <ref id="..." encoding="json">...</ref> envelope
- one Custom XML Data Storage Part per namespace
- target is a same-paragraph text range (rejects nodeEdge, cross-block)
- list({ within }) filters by anchor-range overlap (mirrors hyperlinks.list)
- remove is atomic: anchor + payload both stripped, no dangling state
- no schemaRefs written; consumers can patch via customXml.parts if needed
Naming: top-level metadata.* (not customXml.refs.*) because the API
hides the two-layer SDT+customXml pattern; consumers shouldn't need to
know the storage shape. Type names use AnchoredMetadata* to disambiguate
from doc-level properties.
Contract layer only. Adapter (super-editor) lands next. Generator also
catches up parts.* reference docs that #3245 omitted.
…ory (SD-3104) The metadata.remove contract claimed 'both succeed or neither does,' but the adapter writes to two separate state systems (ProseMirror doc + OOXML package) with no shared commit primitive. The adapter resolves the target up-front so the common failure mode lands before any state change, but a crash strictly between the two writes can leave a dangling payload. Wording now reflects that. Also adds tests/doc-api-stories/tests/metadata/all-commands.ts: a real Editor + DOCX round-trip story covering attach / list / get / resolve / update / remove. Goes through the SDK + CLI like the rest of the doc-api stories. Catches the gap that smoke unit tests cannot: that the adapter actually wraps a text range, writes the payload, and resolves the anchor back to a SelectionTarget on a live editor.
|
📖 Docs preview: https://superdoc-caio-pizzol-sd-3104-metadata-api.mintlify.app |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d8e02527ba
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…ry-run (SD-3104) metadata.attach and metadata.remove returned early on dryRun: true before consulting expectedRevision. The live paths route through executeDomainCommand or executeOutOfBandMutation, which check revision; metadata.update already used executeOutOfBandMutation for both modes. Result: dry-run previews against a stale revision incorrectly reported success, masking the optimistic-concurrency conflict the real call would have caught. Adds a revisionMismatchFailure helper that mirrors checkRevision but returns a FailureResult, and calls it in both dry-run early-return paths. Extends the adapter unit suite with two stale-revision cases (one per op).
Follow-up on the previous dry-run fix. Returning a structured
{ success: false, failure: { code: 'REVISION_MISMATCH' } } on the
attach/remove dry-run paths was shape-inconsistent: the live paths and
the metadata.update dry-run all reach checkRevision via
executeDomainCommand or executeOutOfBandMutation, both of which throw
PlanError. A consumer wrapping these calls in one try/catch couldn't
treat the two failure modes uniformly.
Calls checkRevision directly in the attach + remove dry-run early-return
paths so they throw on stale revisions, matching every other path. Drops
the local REVISION_MISMATCH from the FailureCode union and the
revisionMismatchFailure helper. Updates the two new unit tests to assert
the throw.
Also declares REVISION_MISMATCH on metadata.attach/update/remove throws
arrays in operation-definitions, which the contract was missing.
|
🎉 This PR is included in superdoc-sdk v1.10.0 |
|
🎉 This PR is included in @superdoc-dev/mcp v0.6.0 The release is available on GitHub release |
|
🎉 This PR is included in superdoc v1.34.0 The release is available on GitHub release |
|
🎉 This PR is included in @superdoc-dev/react v1.5.0 The release is available on GitHub release |
|
🎉 This PR is included in vscode-ext v2.6.0 |
Adds editor.doc.metadata.* as a public surface for attaching structured data to a span of text and reading it back across DOCX round-trips. Consumers building citation, suggestion, or review workflows on top of SuperDoc currently rediscover the same two-layer pattern (hidden content control as anchor + namespaced custom XML part as payload store) and write their own glue. This collapses it into one operation set: attach, list, get, update, remove, resolve.
v1 is opinionated: text-range anchors only, JSON payload stored opaquely inside a SuperDoc-owned XML envelope, one custom XML part per namespace. Block-level anchors and structural JSON-to-XML mapping are out of scope.
w:sdt,w15:appearance="hidden", and the JSON payload incustomXml/itemN.xml.removeis sequenced across two state systems (PM doc + OOXML package), not transactional.AnchoredMetadataMutationSuccessJSDoc spells out the failure window.Word save and edit round-trip is a tracked follow-up, not in this PR.