diff --git a/AGENTS.md b/AGENTS.md index eb59352d55..8db586c013 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -72,8 +72,8 @@ Do not hand-edit `COMMAND_CATALOG`, `OPERATION_MEMBER_PATH_MAP`, `OPERATION_REFE - `pnpm test` - unit tests - `pnpm dev` - dev server from `examples/` - `pnpm check:types` - raw TS compile across all referenced projects (`tsc -b tsconfig.references.json`). Does NOT run the public-interface chain. Legacy alias: `pnpm run type-check`. -- `pnpm check:public` - **canonical pre-merge command for typed public surfaces.** Validates both `superdoc` (tier discipline + jsdoc ratchet + vite build + postbuild chain + consumer typecheck matrix + deep-type audit + package-shape + snapshots + classification closure) and Document API (contract parity + output staleness + examples + overview). ~5 min. Non-mutating. Combines `check:public:superdoc` + `check:public:docapi`. -- `pnpm check:public:superdoc` - SuperDoc public package surface only. Wraps nine stages in cheap-to-expensive order: `contract-tiers-test`, `contract-tiers`, `jsdoc-ratchet`, `build`, `consumer-typecheck-matrix`, `deep-type-audit-supported-root`, `package-shape`, `export-snapshots`, `root-classification-closure`. Legacy alias: `pnpm run check:public-contract`. +- `pnpm check:public` - **canonical pre-merge command for typed public surfaces.** Validates both `superdoc` (tier discipline + jsdoc ratchet + public-method fixture coverage + vite build + postbuild chain + consumer typecheck matrix + deep-type audit + package-shape + snapshots + classification closure) and Document API (contract parity + output staleness + examples + overview). ~5 min. Non-mutating. Combines `check:public:superdoc` + `check:public:docapi`. +- `pnpm check:public:superdoc` - SuperDoc public package surface only. Wraps ten stages in cheap-to-expensive order: `contract-tiers-test`, `contract-tiers`, `jsdoc-ratchet`, `public-method-coverage`, `build`, `consumer-typecheck-matrix`, `deep-type-audit-supported-root`, `package-shape`, `export-snapshots`, `root-classification-closure`. Legacy alias: `pnpm run check:public-contract`. - `pnpm check:public:docapi` - Document API public surface only. Wraps four stages: `contract-parity`, `contract-outputs`, `examples`, `overview-alignment`. Clean-checkout safe: gitignored generated artifacts are built in memory; tracked outputs (reference docs, overview block) are compared byte-for-byte. No mutation. Legacy alias: `pnpm run docapi:check`. - `pnpm generate:docapi` - regenerate Document API outputs after editing the contract (alias of `docapi:sync`). Writes gitignored Document API generated artifacts. Run only when you need the artifacts materialized locally (SDK builds, publishing); `check:public:docapi` does not require it. - `pnpm generate:all` - regenerate schemas, SDK clients, tool catalogs, reference docs. diff --git a/packages/superdoc/scripts/README.md b/packages/superdoc/scripts/README.md index a4a51636d3..8ca6c952a2 100644 --- a/packages/superdoc/scripts/README.md +++ b/packages/superdoc/scripts/README.md @@ -144,12 +144,14 @@ what an actual consumer would see — not the workspace source. | `check-all-public-types-fixture.mjs` | Asserts every type-only root export has an `AssertNotAny` line in `src/all-public-types.ts`. | Derives the expected set from `superdoc-root-classification.json`. | | `package-shape-gate.mjs` | External package-shape linters (publint + attw) against the packed tarball. | Catches condition ordering, masquerading exports, missing field declarations. | | `check-root-classification-closure.mjs` | Asserts no `supported-root` or `legacy-root` export references an `internal-candidate` symbol in its public declared type. | Closure rule from SD-3212. | +| `check-public-method-coverage.mjs` | Obligation-based ratchet over public `SuperDoc` methods + getters. For each member the AST computes which obligations are meaningful (`parameters`, `returns`, or `call`); the gate fails when any required obligation is unsatisfied by a fixture under `src/` AND not on the debt snapshot. Catches the `search(text: string)` regression class — call sites do NOT satisfy `parameters`/`returns` on their own. | Snapshot at `public-method-coverage-debt-snapshot.json`; allowlist at `public-method-coverage-allowlist.cjs` (each entry validated: key must match a real member, value must be a non-empty reason). Refresh with `--write`. | -Of these, five run as wrapper stages of `check:public:superdoc` +Of these, six run as wrapper stages of `check:public:superdoc` after the cheap policy gates (`contract-tiers-test`, -`contract-tiers`, `jsdoc-ratchet`) and `build`: -`consumer-typecheck-matrix`, `deep-type-audit-supported-root`, -`package-shape`, `export-snapshots`, `root-classification-closure`. +`contract-tiers`, `jsdoc-ratchet`, `public-method-coverage`) and +`build`: `consumer-typecheck-matrix`, +`deep-type-audit-supported-root`, `package-shape`, +`export-snapshots`, `root-classification-closure`. `consumer-typecheck-matrix` packs `superdoc.tgz` and installs it into the consumer fixture. The rest reuse what matrix produced: `deep-type-audit-supported-root`, `export-snapshots`, and diff --git a/scripts/check-public-contract.mjs b/scripts/check-public-contract.mjs index 0ce7a465f4..5737e226ed 100755 --- a/scripts/check-public-contract.mjs +++ b/scripts/check-public-contract.mjs @@ -29,7 +29,20 @@ * land without `// @ts-check` or * when the allowlist carries * empty/stale entries. - * 4. build - vite build + the postbuild + * 4. public-method-coverage - obligation-based ratchet over + * public SuperDoc methods + + * getters. For each member the + * AST computes which obligations + * are meaningful (parameters / + * returns / call); each unmet + * obligation must be on the debt + * snapshot or the gate fails. + * Call sites do NOT satisfy + * parameters/returns obligations + * on their own — that's why + * `search(text: string)` shipped + * under v1 of this gate. + * 5. build - vite build + the postbuild * validator chain * (check-tsconfig-type-surface, * ensure-types, audit-bundle, @@ -40,29 +53,29 @@ * Skipped when `--skip-build` is * passed (CI calls `pnpm run build` * separately in its own step). - * 5. consumer-typecheck-matrix - packs superdoc + installs the + * 6. consumer-typecheck-matrix - packs superdoc + installs the * tarball into * tests/consumer-typecheck/ * node_modules/, then runs every * consumer scenario. - * 6. deep-type-audit-supported-root - strict gate on the supported- + * 7. deep-type-audit-supported-root - strict gate on the supported- * root public surface; fails on any * `any` leak. Reuses the install - * from stage 5. - * 7. package-shape - publint + attw against the packed + * from stage 6. + * 8. package-shape - publint + attw against the packed * manifest. Reuses the tarball - * from stage 5. - * 8. export-snapshots - super-editor / legacy / root + * from stage 6. + * 9. export-snapshots - super-editor / legacy / root * no-growth export snapshots. * Reuses the install. - * 9. root-classification-closure - no supported-root or legacy-root + * 10. root-classification-closure - no supported-root or legacy-root * export references an internal- * candidate type in its public * declared shape (SD-3212 A1b). * - * Why stage 5 runs before 6-9: stage 5 packs `superdoc.tgz` and - * installs the tarball into the consumer fixture once. Stages 6, 8, - * and 9 reuse the installed fixture; stage 7 reuses the packed tarball + * Why stage 6 runs before 7-10: stage 6 packs `superdoc.tgz` and + * installs the tarball into the consumer fixture once. Stages 7, 9, + * and 10 reuse the installed fixture; stage 8 reuses the packed tarball * directly. Without this ordering each downstream stage would `--pack` * separately and multiply the work. * @@ -122,6 +135,18 @@ const stages = [ 'fails when new public-reachable JSDoc files land without // @ts-check. ' + 'Cheap; runs before the slow build so JSDoc drift fails fast.', }, + { + name: 'public-method-coverage', + cwd: REPO_ROOT, + cmd: 'node', + args: ['tests/consumer-typecheck/check-public-method-coverage.mjs'], + blurb: + 'Obligation-based ratchet over public SuperDoc methods + getters. ' + + 'Each member has computed obligations (parameters / returns / call) ' + + 'that must be satisfied by a typed assertion in a consumer fixture, ' + + 'or be on the debt snapshot. Call sites do NOT satisfy parameters/' + + 'returns on their own (this is why search(text: string) shipped).', + }, { name: 'build', cwd: REPO_ROOT, diff --git a/tests/consumer-typecheck/check-public-method-coverage.mjs b/tests/consumer-typecheck/check-public-method-coverage.mjs new file mode 100644 index 0000000000..dd155f47ef --- /dev/null +++ b/tests/consumer-typecheck/check-public-method-coverage.mjs @@ -0,0 +1,317 @@ +#!/usr/bin/env node +/** + * Public-method fixture coverage gate. + * + * Obligation-based ratchet over public SuperDoc methods + getters. + * For each public member, the gate computes what fixture coverage is + * meaningful (`parameters`, `returns`, or `call`) and fails when any + * required obligation is unmet AND the member is not on the debt + * snapshot. + * + * Obligations (per member, computed from the AST): + * + * - **method with >=1 parameter** → requires `parameters` coverage + * - **method with non-void return** → requires `returns` coverage + * - **getter** → requires `returns` coverage + * - **zero-param method that returns void / Promise** → requires + * `call` coverage (otherwise renaming the method would silently slip + * past) + * + * Satisfaction patterns (scanned across every `.ts` / `.cts` / `.mts` + * file under `tests/consumer-typecheck/src/`): + * + * - `parameters` → `Parameters` + * - `returns` (method) → `ReturnType` + * - `returns` (getter) → `SuperDoc['name']` (bare indexed access) or + * `typeof (superdoc|sd).name` + * - `call` → `(superdoc|sd).name(` + * + * Call sites do NOT satisfy parameter or return obligations on their + * own (TypeScript would accept a wrong-typed argument if the consumer + * matched the signature). This is the central distinction from a + * "mentioned somewhere" ratchet: the gate must catch the + * `search(text: string)` regression class, where a call site + * `sd.search('hello')` shipped while `Parameters` + * was never asserted. + * + * Two failure modes: + * + * 1. RATCHET — A NEW unmet obligation lands (member added, fixture + * removed, or migration narrows a signature) and the obligation + * is not on the debt snapshot. + * 2. SNAPSHOT DRIFT — A snapshot entry is stale (the obligation it + * records is now satisfied). The contributor must run `--write` + * to lock the win. + * + * Refresh the snapshot after intentional changes: + * node tests/consumer-typecheck/check-public-method-coverage.mjs --write + * + * Allowlist: `tests/consumer-typecheck/public-method-coverage-allowlist.cjs`. + * Use only for members that are intentionally not consumer-callable + * (e.g. internal lifecycle relays that escaped `private` for runtime + * reasons). Each entry requires (a) a key that matches an actual public + * member of `SuperDoc`, and (b) a non-empty string reason. The gate + * validates both. + * + * Wrapper stage: `public-method-coverage` in `scripts/check-public-contract.mjs`. + */ + +import { readFileSync, readdirSync, existsSync, writeFileSync } from 'node:fs'; +import { dirname, resolve, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { createRequire } from 'node:module'; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const REPO_ROOT = resolve(HERE, '..', '..'); +const SUPERDOC_TS = resolve(REPO_ROOT, 'packages/superdoc/src/core/SuperDoc.ts'); +const FIXTURE_DIR = resolve(REPO_ROOT, 'tests/consumer-typecheck/src'); +const ALLOWLIST_PATH = resolve(HERE, 'public-method-coverage-allowlist.cjs'); +const SNAPSHOT_PATH = resolve(HERE, 'public-method-coverage-debt-snapshot.json'); + +const require = createRequire(import.meta.url); +const ts = require('typescript'); + +const flags = new Set(process.argv.slice(2)); +const writeMode = flags.has('--write'); + +const EVENT_EMITTER_MEMBERS = new Set([ + 'on', 'off', 'once', 'emit', + 'addListener', 'removeListener', 'removeAllListeners', + 'listeners', 'listenerCount', 'eventNames', + 'prependListener', 'prependOnceListener', 'rawListeners', +]); + +function loadAllowlist() { + if (!existsSync(ALLOWLIST_PATH)) return {}; + const mod = require(ALLOWLIST_PATH); + if (typeof mod !== 'object' || mod === null) return {}; + return mod; +} + +function loadSnapshot() { + if (!existsSync(SNAPSHOT_PATH)) return []; + const raw = JSON.parse(readFileSync(SNAPSHOT_PATH, 'utf8')); + if (!Array.isArray(raw.knownUnmet)) { + console.error(`[public-method-coverage] invalid snapshot at ${SNAPSHOT_PATH} (missing "knownUnmet" array)`); + process.exit(1); + } + return raw.knownUnmet.slice().sort(); +} + +function writeSnapshot(entries) { + const payload = { + $comment: + 'Auto-managed by tests/consumer-typecheck/check-public-method-coverage.mjs. ' + + 'Each entry is "memberName:obligation" where obligation is one of ' + + 'parameters | returns | call. Refresh with --write after adding fixtures.', + knownUnmet: entries.slice().sort(), + }; + writeFileSync(SNAPSHOT_PATH, JSON.stringify(payload, null, 2) + '\n'); +} + +/** Enumerate public members and compute their obligations. */ +function enumerateObligations() { + const src = readFileSync(SUPERDOC_TS, 'utf8'); + const sf = ts.createSourceFile(SUPERDOC_TS, src, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + + let cls = null; + for (const stmt of sf.statements) { + if (ts.isClassDeclaration(stmt) && stmt.name?.text === 'SuperDoc') { + cls = stmt; + break; + } + } + if (!cls) { + console.error(`[public-method-coverage] could not find SuperDoc class in ${SUPERDOC_TS}`); + process.exit(1); + } + + const members = []; + for (const m of cls.members) { + if (!ts.isMethodDeclaration(m) && !ts.isGetAccessorDeclaration(m)) continue; + if (!m.name || !ts.isIdentifier(m.name)) continue; + + const name = m.name.text; + const mods = m.modifiers ?? []; + if (mods.some((mod) => mod.kind === ts.SyntaxKind.PrivateKeyword)) continue; + if (mods.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) continue; + if (ts.getJSDocTags(m).some((tag) => tag.tagName?.text === 'internal')) continue; + if (EVENT_EMITTER_MEMBERS.has(name)) continue; + + const isGetter = ts.isGetAccessorDeclaration(m); + const hasParams = !isGetter && (m.parameters?.length ?? 0) > 0; + + // Return-type meaningfulness: meaningful unless explicitly declared void + // / Promise. Undeclared returns are treated as meaningful (i.e. + // the gate prefers requiring an assertion over silently letting it pass). + let returnsMeaningful = true; + if (!isGetter && m.type) { + const rtText = m.type.getText(sf).trim(); + if (rtText === 'void' || rtText === 'Promise') returnsMeaningful = false; + } + + const obligations = []; + if (isGetter) { + obligations.push('returns'); + } else { + if (hasParams) obligations.push('parameters'); + if (returnsMeaningful) obligations.push('returns'); + if (!hasParams && !returnsMeaningful) obligations.push('call'); + } + + members.push({ name, kind: isGetter ? 'getter' : 'method', obligations }); + } + return members; +} + +/** + * Recursively walk FIXTURE_DIR and return every `.ts` / `.cts` / `.mts` + * file path, relative to FIXTURE_DIR. Manual recursion (not + * `readdirSync(..., { recursive: true })`) so the gate works on any + * Node version this repo supports without depending on the recursive + * option being available. + */ +function listFixtureFiles(dir, rel = '') { + const out = []; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const child = join(dir, entry.name); + const relPath = rel ? `${rel}/${entry.name}` : entry.name; + if (entry.isDirectory()) { + out.push(...listFixtureFiles(child, relPath)); + } else if (entry.isFile() && /\.(c|m)?ts$/.test(entry.name)) { + out.push(relPath); + } + } + return out; +} + +function loadFixtures() { + const files = listFixtureFiles(FIXTURE_DIR).sort(); + return files + .map((rel) => `// === ${rel} ===\n${readFileSync(join(FIXTURE_DIR, rel), 'utf8')}`) + .join('\n'); +} + +/** Test whether a specific obligation is satisfied by any fixture. */ +function isSatisfied(fixtures, name, kind, obligation) { + const n = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + if (obligation === 'parameters') { + return new RegExp(`Parameters<\\s*SuperDoc\\[['"]${n}['"]\\]\\s*>`).test(fixtures); + } + if (obligation === 'returns') { + if (kind === 'method') { + return new RegExp(`ReturnType<\\s*SuperDoc\\[['"]${n}['"]\\]\\s*>`).test(fixtures); + } + // Getter: accept bare indexed access OR typeof on a SuperDoc instance. + if (new RegExp(`SuperDoc\\[['"]${n}['"]\\](?!\\.)`).test(fixtures)) return true; + if (new RegExp(`typeof\\s+(?:superdoc|sd)\\.${n}\\b`).test(fixtures)) return true; + return false; + } + if (obligation === 'call') { + return new RegExp(`(?:superdoc|sd)\\.${n}\\s*\\(`).test(fixtures); + } + return false; +} + +// ─── Main ──────────────────────────────────────────────────────────── + +const members = enumerateObligations(); +const fixtures = loadFixtures(); +const allowlist = loadAllowlist(); +const allowlistKeys = new Set(Object.keys(allowlist)); +const memberNames = new Set(members.map((m) => m.name)); + +// Validate allowlist BEFORE applying it. +const allowlistFailures = []; +for (const [k, v] of Object.entries(allowlist)) { + if (!memberNames.has(k)) { + allowlistFailures.push(` - ${k}: not a public member of SuperDoc (typo or stale entry)`); + continue; + } + if (typeof v !== 'string' || v.trim().length === 0) { + allowlistFailures.push(` - ${k}: missing or empty reason`); + } +} + +// Compute current unmet obligations (skip allowlisted members entirely). +const unmetNow = []; +for (const m of members) { + if (allowlistKeys.has(m.name)) continue; + for (const ob of m.obligations) { + if (!isSatisfied(fixtures, m.name, m.kind, ob)) { + unmetNow.push(`${m.name}:${ob}`); + } + } +} +unmetNow.sort(); + +if (writeMode) { + writeSnapshot(unmetNow); + console.log( + `[public-method-coverage] wrote ${SNAPSHOT_PATH.replace(REPO_ROOT + '/', '')} (${unmetNow.length} entries).`, + ); + process.exit(0); +} + +const snapshot = loadSnapshot(); +const snapshotSet = new Set(snapshot); +const unmetSet = new Set(unmetNow); + +const newUnmet = unmetNow.filter((e) => !snapshotSet.has(e)); +const stale = snapshot.filter((e) => !unmetSet.has(e)); + +const totalObligations = members.reduce((n, m) => n + m.obligations.length, 0); + +const HR = '='.repeat(72); +console.log('[public-method-coverage] SuperDoc public-surface fixture coverage'); +console.log(HR); +console.log(`Members inspected: ${members.length}`); +console.log(` Methods (non-EventEmitter): ${members.filter((m) => m.kind === 'method').length}`); +console.log(` Getters: ${members.filter((m) => m.kind === 'getter').length}`); +console.log(`Total obligations: ${totalObligations}`); +console.log(`Allowlisted members: ${allowlistKeys.size}`); +console.log(`Tracked as known debt: ${unmetNow.length - newUnmet.length}`); +console.log(`Snapshot at: ${SNAPSHOT_PATH.replace(REPO_ROOT + '/', '')}`); +console.log(''); + +const failures = []; +if (allowlistFailures.length > 0) { + failures.push('public-method-coverage-allowlist contract violations:'); + for (const f of allowlistFailures) failures.push(f); +} +if (newUnmet.length > 0) { + if (failures.length > 0) failures.push(''); + failures.push(`${newUnmet.length} NEW unmet obligation(s):`); + for (const e of newUnmet) failures.push(` + ${e}`); + failures.push(''); + failures.push(`Add a consumer fixture under tests/consumer-typecheck/src/ that asserts the`); + failures.push(`required shape for each entry above. Obligation key is "memberName:obligation":`); + failures.push(` parameters → Parameters`); + failures.push(` returns (method) → ReturnType`); + failures.push(` returns (getter) → SuperDoc['name'] or typeof sd.name`); + failures.push(` call → sd.name( … ) or superdoc.name( … )`); + failures.push(``); + failures.push(`If the member is intentionally not consumer-callable, add an entry with a`); + failures.push(`one-line reason to public-method-coverage-allowlist.cjs.`); +} +if (stale.length > 0) { + if (failures.length > 0) failures.push(''); + failures.push(`${stale.length} stale entry/entries in the debt snapshot (obligation now satisfied):`); + for (const e of stale) failures.push(` - ${e}`); + failures.push(''); + failures.push( + `Run \`node tests/consumer-typecheck/check-public-method-coverage.mjs --write\``, + ); + failures.push(`to refresh the snapshot and lock in the win.`); +} + +if (failures.length > 0) { + console.log('FAIL fixture coverage drift:'); + for (const line of failures) console.log(line); + process.exit(1); +} + +console.log( + `OK ${totalObligations} obligation(s) across ${members.length - allowlistKeys.size} members; ${unmetNow.length} tracked as known debt; ratchet snapshot in sync.`, +); +process.exit(0); diff --git a/tests/consumer-typecheck/public-method-coverage-allowlist.cjs b/tests/consumer-typecheck/public-method-coverage-allowlist.cjs new file mode 100644 index 0000000000..03515d9a2f --- /dev/null +++ b/tests/consumer-typecheck/public-method-coverage-allowlist.cjs @@ -0,0 +1,30 @@ +/** + * Allowlist for `check-public-method-coverage.mjs`. + * + * The script enumerates public instance methods + getters on the + * `SuperDoc` class and requires each to have at least one consumer-side + * fixture reference (Parameters<>, ReturnType<>, or a real call site). + * Use this file ONLY for members that are intentionally not part of + * the consumer-callable surface but escaped the `private` modifier + * for legitimate reasons (e.g. called by extensions or composables + * that live in the workspace but aren't exposed through the public + * facade). + * + * Each entry MUST carry a one-line reason. The key is the SuperDoc + * member name (no path needed since it scopes to one class). The + * value is the reason. + */ +module.exports = { + // Lifecycle/relay methods called by SuperDoc.vue and extension code. + // Not part of `superdoc.X()` consumer surface; consumers register + // callbacks on Config (`onEditorBeforeCreate`, etc.) and SuperDoc + // relays via these broadcast helpers. + broadcastReady: 'Internal lifecycle relay; called by editor/Vue glue, not by consumers.', + broadcastEditorBeforeCreate: 'Internal lifecycle relay; called by editor/Vue glue, not by consumers.', + broadcastEditorCreate: 'Internal lifecycle relay; called by editor/Vue glue, not by consumers.', + broadcastEditorDestroy: 'Internal lifecycle relay; called by editor/Vue glue, not by consumers.', + broadcastPdfDocumentReady: 'Internal lifecycle relay; called by editor/Vue glue, not by consumers.', + broadcastSidebarToggle: 'Internal lifecycle relay; called by editor/Vue glue, not by consumers.', + setActiveEditor: 'Internal setter; called by editor lifecycle, not by consumers.', + onContentError: 'Internal handler bound to editor `content-error`; consumers use Config.onContentError.', +}; diff --git a/tests/consumer-typecheck/public-method-coverage-debt-snapshot.json b/tests/consumer-typecheck/public-method-coverage-debt-snapshot.json new file mode 100644 index 0000000000..251cc1187f --- /dev/null +++ b/tests/consumer-typecheck/public-method-coverage-debt-snapshot.json @@ -0,0 +1,64 @@ +{ + "$comment": "Auto-managed by tests/consumer-typecheck/check-public-method-coverage.mjs. Each entry is \"memberName:obligation\" where obligation is one of parameters | returns | call. Refresh with --write after adding fixtures.", + "knownUnmet": [ + "addCommentsList:parameters", + "addCommentsList:returns", + "addSharedUser:parameters", + "addSharedUser:returns", + "canPerformPermission:parameters", + "canPerformPermission:returns", + "closeSurface:parameters", + "closeSurface:returns", + "destroy:returns", + "element:returns", + "export:parameters", + "export:returns", + "exportEditorsToDOCX:parameters", + "exportEditorsToDOCX:returns", + "focus:returns", + "getComment:parameters", + "getComment:returns", + "getHTML:parameters", + "getHTML:returns", + "getPresentationEditorForDocument:parameters", + "getPresentationEditorForDocument:returns", + "getZoom:returns", + "goToSearchResult:returns", + "lockSuperdoc:parameters", + "lockSuperdoc:returns", + "navigateTo:parameters", + "navigateTo:returns", + "openSurface:parameters", + "openSurface:returns", + "removeCommentsList:returns", + "removeSharedUser:parameters", + "removeSharedUser:returns", + "requiredNumberOfEditors:returns", + "save:returns", + "scrollToComment:parameters", + "scrollToComment:returns", + "scrollToElement:parameters", + "scrollToElement:returns", + "setDisableContextMenu:parameters", + "setDisableContextMenu:returns", + "setDocumentMode:parameters", + "setDocumentMode:returns", + "setHighContrastMode:parameters", + "setHighContrastMode:returns", + "setLocked:parameters", + "setLocked:returns", + "setShowBookmarks:parameters", + "setShowBookmarks:returns", + "setShowFormattingMarks:parameters", + "setShowFormattingMarks:returns", + "setTrackedChangesPreferences:parameters", + "setTrackedChangesPreferences:returns", + "setZoom:parameters", + "setZoom:returns", + "state:returns", + "toggleFormattingMarks:returns", + "toggleRuler:returns", + "upgradeToCollaboration:parameters", + "upgradeToCollaboration:returns" + ] +}