@openpresentation/opf 0.26.0-rc.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -117,7 +117,7 @@ function localAssetProblems(deck, root) {
117
117
  }
118
118
  var RENDER_PACKAGE = "@openpresentation/opf-render";
119
119
  var PPTX_PACKAGE = "@openpresentation/opf-pptx";
120
- var PEER_RANGES = { [RENDER_PACKAGE]: "^0.26.0-rc.0", [PPTX_PACKAGE]: "^0.26.0-rc.0" };
120
+ var PEER_RANGES = { [RENDER_PACKAGE]: "^0.26.0", [PPTX_PACKAGE]: "^0.26.0" };
121
121
  var PPTX_TEXT_OPTIONS_FROM = "0.22.0";
122
122
  var numbers = (version) => version.replace(/[-+].*$/, "").split(".").map((part) => Number.parseInt(part, 10) || 0);
123
123
  function atLeast(version, minimum) {
@@ -43,8 +43,8 @@ declare const RENDER_PACKAGE = "@openpresentation/opf-render";
43
43
  declare const PPTX_PACKAGE = "@openpresentation/opf-pptx";
44
44
  /** Peer ranges. The CLI checks the features it calls rather than the version, so a newer release in range keeps working. */
45
45
  declare const PEER_RANGES: {
46
- readonly "@openpresentation/opf-render": "^0.26.0-rc.0";
47
- readonly "@openpresentation/opf-pptx": "^0.26.0-rc.0";
46
+ readonly "@openpresentation/opf-render": "^0.26.0";
47
+ readonly "@openpresentation/opf-pptx": "^0.26.0";
48
48
  };
49
49
  interface Diagnostic {
50
50
  code: string;
@@ -1,4 +1,4 @@
1
- export { convertFiles, deckFileFormat, exportFormatOf, openDeck, planConversion, readInput, saveDeck, writePlanned } from './chunk-NJRFCM4H.js';
1
+ export { convertFiles, deckFileFormat, exportFormatOf, openDeck, planConversion, readInput, saveDeck, writePlanned } from './chunk-F7USLFZE.js';
2
2
  import './chunk-IC6OPLMU.js';
3
3
  import './chunk-WW62PPYV.js';
4
4
  import './chunk-UJSAR6GF.js';
package/dist/docs.js CHANGED
@@ -286,7 +286,7 @@ var docsData = Object.freeze([
286
286
  "slug": "release-process",
287
287
  "file": "docs/release-process.md",
288
288
  "title": "OPF Release Process",
289
- "markdown": "# OPF Release Process\n\nUse Node 24 (`24.x`) for all future source, candidate and registry verification.\nThe packages declare the open-ended `engines.node` `>=22` (RR-20, tested on Node 22, 24 and 26 by the `node-range`\nCI legs, which run in the nightly workflow, before every release and on pull requests labelled `full-ci`); a closed range such as `24.x` makes npm on any other Node silently install an older release, and\n`pnpm check:engines-range` fails on one, including in a lockfile that pins a release made after the fix.\nThe next releases must document the [Node 24 migration](migrations/node24.md)\nand use new versions. Historical dual-runtime release records remain unchanged.\n\nThis document is the release runbook for the six published packages of this repository:\n[`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf) (core, `packages/javascript`),\n`@openpresentation/cli`, `@openpresentation/opf-render`, `@openpresentation/opf-pptx`, `@openpresentation/opf-editor` and\n`@openpresentation/gallery`. They are released together, at one version, with one tag.\n\n## Release: one click\n\n> Status: the workflows and `scripts/release.mjs` are in place and rehearsed with dry runs. The first real release through them\n> is the canary `0.26.0-rc.0` (MR-41), then 0.26.0 (MR-44). Releases up to 0.25.x were made from the four old repositories with\n> a per-package train; that process is retired and not described here.\n\n**One version, one tag, one workflow.** All six packages carry the same version (`pnpm check:versions`); a release is the tag\n`vX.Y.Z` on a commit of `main`, published by `.github/workflows/npm-publish.yml` in dependency order: gallery, core, render,\nPPTX, editor, CLI. In 0.x a breaking change is a minor release and everything else a patch. Prereleases (`0.26.0-rc.0`) go to\nthe dist-tag `next`, releases to `latest`; both are passed explicitly to `npm publish --tag` for every package.\n\n### Run a release\n\nActions, **Release**, **Run workflow** on `main`:\n\n| Input | Meaning |\n| --- | --- |\n| `version` | `X.Y.Z`, `X.Y.Z-rc.N`, or `patch` / `minor` of the shared line (the highest `latest` of core, CLI, render, PPTX and editor) |\n| `dry-run` | Default **true**: rehearse everything up to `npm publish --dry-run`; nothing is committed, tagged or published. Set false for the real release |\n| `skip-gate` | A dry run only: skip the slow gate |\n\nRun it with `dry-run` true first when the change touches the release machinery; a real release of ordinary changes needs no\nrehearsal. The jobs:\n\n1. **plan**: a real release runs on `main`, and the check `verify` is green for its head (one API call, no waiting).\n2. **gate** (parallel with prepare): `nightly.yml` called on that commit: Windows, macOS, Node 22 and 26, the packed-install and\n registry consumers. A red leg stops the release before anything is committed or tagged.\n3. **prepare** (parallel with gate): `node scripts/release.mjs prepare <version>` in the runner: sets the six versions, core's\n peer ranges and `PEER_RANGES` (`packages/javascript/src/node/peers.ts`), assembles every `changes/` fragment into its package\n changelog (a package without a fragment gets \"No package changes; released with the set.\"), applies the gallery stability gate\n and the version rules below, refreshes the lockfile, and uploads the result as a patch. A **prerelease** (`X.Y.Z-rc.N`) only sets the versions: it leaves every changelog and `changes/` alone, and its GitHub release notes are assembled from the fragments (`release.mjs notes`), so the stable release that follows carries every note. A refused version fails here, in two\n minutes.\n4. **commit and tag** (real release, after gate and prepare): with the GitHub App's token (a bypass actor on the `main` ruleset)\n applies the patch as one commit `release vX.Y.Z` on exactly the commit the gate tested, pushes it to `main`, then pushes the\n tag `vX.Y.Z`. If `main` moved during the run, or the tag exists, it stops. A tag pushed with the App's token starts\n `npm-publish.yml` (a tag pushed with `GITHUB_TOKEN` would not).\n5. **npm-publish.yml** on the tag: `plan` (the tag is on `main`), `build` (one install and build, `pnpm pack` of the six,\n `scripts/stamp-githead.mjs` adds `gitHead` to each tarball because `pnpm pack` omits it, a smoke test installs the six\n tarballs together, renders the quickstart deck and runs the CLI), `publish` (the only job with `id-token: write`; the\n pre-publish gate, then `npm publish <tarball> --access public --provenance --tag <tag>` six times; a version already on npm is\n skipped), `verify` (below), `release` (the GitHub release, notes grouped by package) and `report` (a failure opens the issue\n \"Release vX.Y.Z failed\", labeled `release-failed`).\n6. **follow the publish run** (the Release run waits for it, so it is green only when the whole release is).\n\nTime target: about 20 minutes from the click to six verified packages (measured in MR-44).\n\n### Version rules\n\n- `prepare` refuses a version that is not above the shared line's previous release, and one already on npm for any package\n (a published version is never reused; fix forward).\n- **The gallery stability rule** (`scripts/gallery-stability.mjs`, `pnpm check:stability`; see\n [default-catalog.md](default-catalog.md)): a declared drawing change (a `drawing` entry in a gallery fragment), a removed record\n id or a `CATALOG_SCHEMA` change needs a **minor** release; `prepare` refuses a patch. New records and unchanged drawings go in\n any release. The baseline is the gallery's `latest` dist-tag, never its highest version.\n- **The publish gate** (`release.mjs check-publish`, run by `publish`): each package's version is above its own registry\n `latest`, except the gallery, which is compared with the shared line, because its `latest` was 5.0.0 before it joined the\n shared 0.x line (D16). A stable version needs `latest`, a prerelease must not take it. 5.0.0 and the older gallery versions\n stay published and are never deprecated; because they are above the shared line, every gallery publish names its tag explicitly.\n- The shared line cannot use 1.0.0 to 5.0.0 (taken by the gallery): the first stable major for all six is 6.0.0.\n\n### Trusted publishing\n\nAll six packages have the trusted publisher: GitHub Actions, organization `OpenPresentation`, repository `opf`, workflow filename\n`npm-publish.yml`, no environment (owner steps O1 in `docs/programs/monorepo/owner-steps.md`). npm checks the entry against\nthe **top-level** workflow of the run, not against a reusable workflow that contains `npm publish`, so `npm-publish.yml` is the\nworkflow that starts on the tag and `release.yml` only pushes the tag. `publish` refuses to publish from any other top-level\nworkflow. A publish fails with `E404` or `E403 ... not a trusted publisher` when an entry is wrong: fix the entry and re-run\nthe workflow on the tag. There is no `NPM_TOKEN`, and no local publish: the provenance gate applies to every release.\n\n### Retry and failure\n\nA failed `npm-publish.yml` run is fixed and run again on the same tag: Actions, **Publish OPF npm packages**, **Run workflow**,\nref `vX.Y.Z`, `dry-run` false (or re-run the failed run). Versions already on npm are skipped, so it continues where it stopped.\nA partial publish is safe: the `^X.Y.Z` floors point at packages that exist, because the order is dependency order. A real\ndefect in a published version is fixed forward with the next patch and a new tag; tags are never moved. If `prepare` or the gate\nfails, nothing was committed: fix the cause and run the release again.\n\n### Fallback: a release pull request\n\nIf no App token is available, or a person prefers review: on a branch run `pnpm release:prepare X.Y.Z`\n(`node scripts/release.mjs prepare X.Y.Z`; it needs `pnpm build` first for the stability gate), open the pull request, merge it\non green, then on `main` run `git tag vX.Y.Z && git push origin vX.Y.Z`. The publish workflow is the same.\n\n### Verify\n\nThe `verify` job runs `node scripts/release.mjs verify X.Y.Z --tag <dist-tag> --commit <release commit> --wait 10` and then the\nsmoke test against the registry (`release.mjs smoke X.Y.Z --registry`). For all six packages it checks the registry manifest,\n`gitHead` and the provenance source commit equal to the release commit, the provenance workflow `npm-publish.yml` on\n`refs/tags/vX.Y.Z`, the subject digest, the dist-tag, that no manifest carries a `workspace:` range and every sibling range is\n`^X.Y.Z`, `npm audit signatures`, and that each package changelog has the version's section. By hand:\n\n```sh\nnode scripts/release.mjs verify X.Y.Z --tag latest # exit 0 when all six verify\nnpm view @openpresentation/opf@X.Y.Z gitHead dist.attestations.url\nnpm view @openpresentation/gallery dist-tags --json # latest is X.Y.Z; 5.0.0 stays published\n```\n\n### `scripts/release.mjs`\n\n| Command | Does |\n| --- | --- |\n| `prepare <version> [--dry-run] [--no-stability] [--previous V]` | the prepare step above (`--dry-run` writes nothing) |\n| `pack <version> --commit <sha> --out <dir>` | `pnpm pack` of the six, `gitHead` stamped, `release-manifest.json` with digests |\n| `smoke <version> (--dir <dir> \\| --registry)` | install the six together, render, resolve, run the CLI |\n| `check-publish <version> [--tag t]` | the pre-publish gate |\n| `publish <version> --dir <dir> [--tag t] [--dry-run]` | the gate, then `npm publish` in dependency order |\n| `verify <version> [--tag t] [--commit sha] [--wait minutes]` | the registry and provenance check (a prerelease has no changelog section to check) |\n| `notes <version> [--out file]` | the GitHub release body |\n| `order` | the publish order |\n\n### After a release: the sites\n\nAfter a **stable** release, `release.yml` calls `.github/workflows/bump-sites.yml` (a prerelease never does). For each of the\nthree sites (`Data-Advantage/openpresentation-site` and `Data-Advantage/pptx-gallery` on `main`, `Data-Advantage/pptx-dev` on\n`master`) it opens one pull request, \"bump to vX.Y.Z\", from the branch `bot/opf-vX.Y.Z`, that sets every `@openpresentation/*`\npin the site declares to the released version and refreshes the site's lockfile (`scripts/bump-site.mjs`). A patch bump is labeled\n`auto-merge`; a minor or major bump (a breaking change in 0.x) is labeled `needs-review` and stays open. The workflow only opens\nand labels the pull request and never merges it: the site's own CI merges an `auto-merge` pull request after its\n`build-and-smoke` job passes, using the GitHub App's token so that the merge starts the site's deploy workflows (D19; the sites\nare private repositories in a Free-plan organization and have no rulesets or required checks). A bump that cannot be made opens\nan issue in this repository (`bump-sites-failed`) and never fails the release. To run it again, or for a release made by the pull-request\nfallback or from a tag, use Actions, **Bump sites**, **Run workflow** with the version. The App, its secrets and the site-side\nworkflows are owner steps O4 and O8 (`docs/programs/monorepo/owner-steps.md`); the workflow files for the sites are in\n`docs/programs/monorepo/site-workflows/`.\n\n### Who may release\n\nThe owner authorized agents to prepare and publish npm releases on 2026-09-29 (`AGENTS.md`). A release is made only through\n`release.yml` (or the pull-request fallback) and `npm-publish.yml`, only when one is required and only with every gate green;\nagents never publish from a laptop, never skip the gate and never pass a different `--tag` to make a check pass. Dry runs are\nfree at any time.\n\n## Geometry-moving core releases\n\nCore composition changes that move geometry make the preview and the PPTX export drift when the renderer and the exporter resolve\ndifferent core versions. This cannot happen between the packages of this repository: all six are released at one version, and\ntheir runtime dependencies and peers on each other are `workspace:^`, published as `^X.Y.Z`, so the floor of every sibling is the\nrelease itself. Do not hand-edit those ranges, and do not release one package alone.\n\n## Renderer font-count gate (RR-90)\n\nThe installed-consumer check (`scripts/test-packed-ecosystem.mjs`) and `test:gallery-lazy-fonts` compare the number of vendored faces a renderer lists as lazy fonts with `scripts/lazy-font-counts.mjs`. A renderer release whose fonts did not change needs no new entry. The gate hashes the renderer package's font set, which is 143 files: `dist/font-manifest.js` and every file under `fonts/` (faces, licenses, `PROVENANCE.json`, `NOTICE.md`). An unlisted version, stable or a prerelease (`X.Y.Z-rc.N`), passes when that hash is in `REVIEWED_FONT_SETS` and its measured count equals the count recorded there.\n\nIt fails closed, with a message that says what changed, when:\n\n- the font files differ from every reviewed set (the message prints the new hashes): review what changed, then add the set to `REVIEWED_FONT_SETS` and the version's count to `LAZY_FONT_COUNTS`;\n- the font files are a reviewed set but the count differs: find out why the count moved, then add an entry;\n\nA prerelease is held to the same rule as the stable version: its label neither admits nor refuses it, its font files and count do.\n\nTo review a set, pack the release (`npm pack @openpresentation/opf-render@<version>`), run `hashFontSet` from `scripts/lazy-font-counts.mjs` over the unpacked `package/` folder, and record the result with the versions you checked. Every existing `LAZY_FONT_COUNTS` entry stays and keeps its own rule.\n\n## Troubleshooting\n\n- **The tag or version check fails** (`npm-publish.yml`, job `plan`): the tag does not point at a commit of `main`, or the version\n is not well formed. Inspect the exact tag. Never move or re-push a release tag; fix a real error on `main` and release the\n next version. A transient failure is retried on the same unchanged tag.\n- **`E404`, `E403` or `ENEEDAUTH` from `npm publish`**: the trusted publisher of that package is missing or wrong. It must be\n organization `OpenPresentation`, repository `opf`, workflow filename `npm-publish.yml`, no environment, and the `publish` job\n needs `id-token: write`. Fix the entry in npm (owner step O1) and re-run the workflow on the tag.\n- **A version is already on npm**: `publish` skips it; do not try to publish it again. Verify the package and continue.\n- **The gate is red**: the failing leg of `nightly.yml` is in the Release run. Fix it on `main` and release again; nothing was\n committed. A flaky leg is quarantined (`pnpm check:quarantine`), never skipped for a release.\n- **`release.yml` stopped with \"main moved\"**: a commit landed on `main` after the run started, and the gate had tested the\n earlier one. Run the release again.\n- **A nightly failure**: the `nightly-red` issue lists the failing legs and the run; it never blocks a pull request, and the\n release gate refuses to publish on the same failure.\n"
289
+ "markdown": "# OPF Release Process\n\nUse Node 24 (`24.x`) for all future source, candidate and registry verification.\nThe packages declare the open-ended `engines.node` `>=22` (RR-20, tested on Node 22, 24 and 26 by the `node-range`\nCI legs, which run in the nightly workflow, before every release and on pull requests labelled `full-ci`); a closed range such as `24.x` makes npm on any other Node silently install an older release, and\n`pnpm check:engines-range` fails on one, including in a lockfile that pins a release made after the fix.\nThe next releases must document the [Node 24 migration](migrations/node24.md)\nand use new versions. Historical dual-runtime release records remain unchanged.\n\nThis document is the release runbook for the six published packages of this repository:\n[`@openpresentation/opf`](https://www.npmjs.com/package/@openpresentation/opf) (core, `packages/javascript`),\n`@openpresentation/cli`, `@openpresentation/opf-render`, `@openpresentation/opf-pptx`, `@openpresentation/opf-editor` and\n`@openpresentation/gallery`. They are released together, at one version, with one tag.\n\n## Release: one click\n\n> Status: the workflows and `scripts/release.mjs` are in place and rehearsed with dry runs. The first real release through them\n> is the canary `0.26.0-rc.0` (MR-41), then 0.26.0 (MR-44). Releases up to 0.25.x were made from the four old repositories with\n> a per-package train; that process is retired and not described here.\n\n**One version, one tag, one workflow.** All six packages carry the same version (`pnpm check:versions`); a release is the tag\n`vX.Y.Z` on a commit of `main`, published by `.github/workflows/npm-publish.yml` in dependency order: gallery, core, render,\nPPTX, editor, CLI. In 0.x a breaking change is a minor release and everything else a patch. Prereleases (`0.26.0-rc.0`) go to\nthe dist-tag `next`, releases to `latest`; both are passed explicitly to `npm publish --tag` for every package.\n\n### Run a release\n\nActions, **Release**, **Run workflow** on `main`:\n\n| Input | Meaning |\n| --- | --- |\n| `version` | `X.Y.Z`, `X.Y.Z-rc.N`, or `patch` / `minor` of the shared line (the highest `latest` of core, CLI, render, PPTX and editor) |\n| `dry-run` | Default **true**: rehearse everything up to `npm publish --dry-run`; nothing is committed, tagged or published. Set false for the real release |\n| `skip-gate` | A dry run only: skip the slow gate |\n\nRun it with `dry-run` true first when the change touches the release machinery; a real release of ordinary changes needs no\nrehearsal. The jobs:\n\n1. **plan**: a real release runs on `main`, and the check `verify` is green for its head (one API call, no waiting).\n2. **gate** (parallel with prepare): `nightly.yml` called on that commit: Windows, macOS, Node 22 and 26, the packed-install and\n registry consumers. A red leg stops the release before anything is committed or tagged.\n3. **prepare** (parallel with gate): `node scripts/release.mjs prepare <version>` in the runner: sets the six versions, core's\n peer ranges and `PEER_RANGES` (`packages/javascript/src/node/peers.ts`), assembles every `changes/` fragment into its package\n changelog (a package without a fragment gets \"No package changes; released with the set.\"), applies the gallery stability gate\n and the version rules below, refreshes the lockfile, and uploads the result as a patch. A **prerelease** (`X.Y.Z-rc.N`) only sets the versions: it leaves every changelog and `changes/` alone, and its GitHub release notes are assembled from the fragments (`release.mjs notes`), so the stable release that follows carries every note. A refused version fails here, in two\n minutes.\n4. **commit and tag** (real release, after gate and prepare): with the GitHub App's token (a bypass actor on the `main` ruleset)\n applies the patch as one commit `release vX.Y.Z` on exactly the commit the gate tested, pushes it to `main`, then pushes the\n tag `vX.Y.Z`. If `main` moved during the run, or the tag exists, it stops. A tag pushed with the App's token starts\n `npm-publish.yml` (a tag pushed with `GITHUB_TOKEN` would not).\n5. **npm-publish.yml** on the tag: `plan` (the tag is on `main`), `build` (one install and build, `pnpm pack` of the six,\n `scripts/stamp-githead.mjs` adds `gitHead` to each tarball because `pnpm pack` omits it, a smoke test installs the six\n tarballs together, renders the quickstart deck and runs the CLI), `publish` (the only job with `id-token: write`; the\n pre-publish gate, then `npm publish <tarball> --access public --provenance --tag <tag>` six times, printing npm's own output, then polling until the registry serves each version (it lags the upload by up to several minutes); a version already on npm is\n skipped), `verify` (below), `release` (the GitHub release, notes grouped by package) and `report` (a failure opens the issue\n \"Release vX.Y.Z failed\", labeled `release-failed`).\n6. **follow the publish run** (the Release run waits for it, so it is green only when the whole release is).\n\nTime target: about 20 minutes from the click to six verified packages (measured in MR-44).\n\n### Version rules\n\n- `prepare` refuses a version that is not above the shared line's previous release, and one already on npm for any package\n (a published version is never reused; fix forward).\n- **The gallery stability rule** (`scripts/gallery-stability.mjs`, `pnpm check:stability`; see\n [default-catalog.md](default-catalog.md)): a declared drawing change (a `drawing` entry in a gallery fragment), a removed record\n id or a `CATALOG_SCHEMA` change needs a **minor** release; `prepare` refuses a patch. New records and unchanged drawings go in\n any release. The baseline is the gallery's `latest` dist-tag, never its highest version.\n- **The publish gate** (`release.mjs check-publish`, run by `publish`): each package's version is above its own registry\n `latest`, except the gallery, which is compared with the shared line, because its `latest` was 5.0.0 before it joined the\n shared 0.x line (D16). A stable version needs `latest`, a prerelease must not take it. 5.0.0 and the older gallery versions\n stay published and are never deprecated; because they are above the shared line, every gallery publish names its tag explicitly.\n- The shared line cannot use 1.0.0 to 5.0.0 (taken by the gallery): the first stable major for all six is 6.0.0.\n\n### Trusted publishing\n\nAll six packages have the trusted publisher: GitHub Actions, organization `OpenPresentation`, repository `opf`, workflow filename\n`npm-publish.yml`, no environment (owner steps O1 in `docs/programs/monorepo/owner-steps.md`). npm checks the entry against\nthe **top-level** workflow of the run, not against a reusable workflow that contains `npm publish`, so `npm-publish.yml` is the\nworkflow that starts on the tag and `release.yml` only pushes the tag. `publish` refuses to publish from any other top-level\nworkflow. A publish fails with `E404` or `E403 ... not a trusted publisher` when an entry is wrong: fix the entry and re-run\nthe workflow on the tag. There is no `NPM_TOKEN`, and no local publish: the provenance gate applies to every release.\n\n### Retry and failure\n\nA failed `npm-publish.yml` run is fixed and run again on the same tag: Actions, **Publish OPF npm packages**, **Run workflow**,\nref `vX.Y.Z`, `dry-run` false (or re-run the failed run). Versions already on npm are skipped, so it continues where it stopped.\nA partial publish is safe: the `^X.Y.Z` floors point at packages that exist, because the order is dependency order. A real\ndefect in a published version is fixed forward with the next patch and a new tag; tags are never moved. If `prepare` or the gate\nfails, nothing was committed: fix the cause and run the release again.\n\n### Fallback: a release pull request\n\nIf no App token is available, or a person prefers review: on a branch run `pnpm release:prepare X.Y.Z`\n(`node scripts/release.mjs prepare X.Y.Z`; it needs `pnpm build` first for the stability gate), open the pull request, merge it\non green, then on `main` run `git tag vX.Y.Z && git push origin vX.Y.Z`. The publish workflow is the same.\n\n### Verify\n\nThe `verify` job runs `node scripts/release.mjs verify X.Y.Z --tag <dist-tag> --commit <release commit> --wait 10` and then the\nsmoke test against the registry (`release.mjs smoke X.Y.Z --registry`). For all six packages it checks the registry manifest,\n`gitHead` and the provenance source commit equal to the release commit, the provenance workflow `npm-publish.yml` on\n`refs/tags/vX.Y.Z`, the subject digest, the dist-tag, that no manifest carries a `workspace:` range and every sibling range is\n`^X.Y.Z`, `npm audit signatures`, and that each package changelog has the version's section. By hand:\n\n```sh\nnode scripts/release.mjs verify X.Y.Z --tag latest # exit 0 when all six verify\nnpm view @openpresentation/opf@X.Y.Z gitHead dist.attestations.url\nnpm view @openpresentation/gallery dist-tags --json # latest is X.Y.Z; 5.0.0 stays published\n```\n\n### `scripts/release.mjs`\n\n| Command | Does |\n| --- | --- |\n| `prepare <version> [--dry-run] [--no-stability] [--previous V]` | the prepare step above (`--dry-run` writes nothing) |\n| `pack <version> --commit <sha> --out <dir>` | `pnpm pack` of the six, `gitHead` stamped, `release-manifest.json` with digests |\n| `smoke <version> (--dir <dir> \\| --registry)` | install the six together, render, resolve, run the CLI |\n| `check-publish <version> [--tag t]` | the pre-publish gate |\n| `publish <version> --dir <dir> [--tag t] [--dry-run] [--wait minutes] [--poll seconds] [--expect-staged]` | the gate, then `npm publish` in dependency order: npm's output is printed, the version must be served within `--wait` (default 15 min, after all six uploads), a staged result fails unless `--expect-staged` |\n| `verify <version> [--tag t] [--commit sha] [--wait minutes]` | the registry and provenance check (a prerelease has no changelog section to check) |\n| `notes <version> [--out file]` | the GitHub release body |\n| `order` | the publish order |\n\n### After a release: the sites\n\nAfter a **stable** release, `release.yml` calls `.github/workflows/bump-sites.yml` (a prerelease never does). For each of the\nthree sites (`Data-Advantage/openpresentation-site` and `Data-Advantage/pptx-gallery` on `main`, `Data-Advantage/pptx-dev` on\n`master`) it opens one pull request, \"bump to vX.Y.Z\", from the branch `bot/opf-vX.Y.Z`, that sets every `@openpresentation/*`\npin the site declares to the released version and refreshes the site's lockfile (`scripts/bump-site.mjs`). A patch bump is labeled\n`auto-merge`; a minor or major bump (a breaking change in 0.x) is labeled `needs-review` and stays open. The workflow only opens\nand labels the pull request and never merges it: the site's own CI merges an `auto-merge` pull request after its\n`build-and-smoke` job passes, using the GitHub App's token so that the merge starts the site's deploy workflows (D19; the sites\nare private repositories in a Free-plan organization and have no rulesets or required checks). A bump that cannot be made opens\nan issue in this repository (`bump-sites-failed`) and never fails the release. To run it again, or for a release made by the pull-request\nfallback or from a tag, use Actions, **Bump sites**, **Run workflow** with the version. The App, its secrets and the site-side\nworkflows are owner steps O4 and O8 (`docs/programs/monorepo/owner-steps.md`); the workflow files for the sites are in\n`docs/programs/monorepo/site-workflows/`.\n\n### Who may release\n\nThe owner authorized agents to prepare and publish npm releases on 2026-09-29 (`AGENTS.md`). A release is made only through\n`release.yml` (or the pull-request fallback) and `npm-publish.yml`, only when one is required and only with every gate green;\nagents never publish from a laptop, never skip the gate and never pass a different `--tag` to make a check pass. Dry runs are\nfree at any time.\n\n## Geometry-moving core releases\n\nCore composition changes that move geometry make the preview and the PPTX export drift when the renderer and the exporter resolve\ndifferent core versions. This cannot happen between the packages of this repository: all six are released at one version, and\ntheir runtime dependencies and peers on each other are `workspace:^`, published as `^X.Y.Z`, so the floor of every sibling is the\nrelease itself. Do not hand-edit those ranges, and do not release one package alone.\n\n## Renderer font-count gate (RR-90)\n\nThe installed-consumer check (`scripts/test-packed-ecosystem.mjs`) and `test:gallery-lazy-fonts` compare the number of vendored faces a renderer lists as lazy fonts with `scripts/lazy-font-counts.mjs`. A renderer release whose fonts did not change needs no new entry. The gate hashes the renderer package's font set, which is 143 files: `dist/font-manifest.js` and every file under `fonts/` (faces, licenses, `PROVENANCE.json`, `NOTICE.md`). An unlisted version, stable or a prerelease (`X.Y.Z-rc.N`), passes when that hash is in `REVIEWED_FONT_SETS` and its measured count equals the count recorded there.\n\nIt fails closed, with a message that says what changed, when:\n\n- the font files differ from every reviewed set (the message prints the new hashes): review what changed, then add the set to `REVIEWED_FONT_SETS` and the version's count to `LAZY_FONT_COUNTS`;\n- the font files are a reviewed set but the count differs: find out why the count moved, then add an entry;\n\nA prerelease is held to the same rule as the stable version: its label neither admits nor refuses it, its font files and count do.\n\nTo review a set, pack the release (`npm pack @openpresentation/opf-render@<version>`), run `hashFontSet` from `scripts/lazy-font-counts.mjs` over the unpacked `package/` folder, and record the result with the versions you checked. Every existing `LAZY_FONT_COUNTS` entry stays and keeps its own rule.\n\n## Troubleshooting\n\n- **The tag or version check fails** (`npm-publish.yml`, job `plan`): the tag does not point at a commit of `main`, or the version\n is not well formed. Inspect the exact tag. Never move or re-push a release tag; fix a real error on `main` and release the\n next version. A transient failure is retried on the same unchanged tag.\n- **`E404`, `E403` or `ENEEDAUTH` from `npm publish`**: the trusted publisher of that package is missing or wrong. It must be\n organization `OpenPresentation`, repository `opf`, workflow filename `npm-publish.yml`, no environment, and the `publish` job\n needs `id-token: write`. Fix the entry in npm (owner step O1) and re-run the workflow on the tag.\n- **`publish` fails with \"npm accepted X (exit 0) but the registry still does not serve it\"** or \"npm staged X\": npm returned success\n (or staged the upload) but the version is not installable. Open the package on npmjs.com, Staged Packages tab, or run\n `npm stage list <name>` as a maintainer: a staged version must be approved with 2FA (`npm stage approve <stage-id>`;\n trusted-publishing OIDC tokens cannot approve). Otherwise wait for propagation and re-run the workflow on the same tag; the\n packages already served are skipped.\n- **A version is already on npm**: `publish` skips it; do not try to publish it again. Verify the package and continue.\n- **The gate is red**: the failing leg of `nightly.yml` is in the Release run. Fix it on `main` and release again; nothing was\n committed. A flaky leg is quarantined (`pnpm check:quarantine`), never skipped for a release.\n- **`release.yml` stopped with \"main moved\"**: a commit landed on `main` after the run started, and the gate had tested the\n earlier one. Run the release again.\n- **A nightly failure**: the `nightly-red` issue lists the failing legs and the run; it never blocks a pull request, and the\n release gate refuses to publish on the same failure.\n"
290
290
  },
291
291
  {
292
292
  "slug": "rich-text",
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { SchemaEntry, SchemaName, audience, chartType, colorScheme, fontScheme, language, layout, narrative, presentation, purpose, schemaEntries, schemaNames, schemas, socialPlatform, theme, tone } from './schemas.js';
2
- import { C as ConvertInput, a as ConvertOptions, b as ConvertResult, c as ConvertFormat, O as OpenOptions, S as SaveOptions, d as SaveResult } from './conversion-Dh8Q4RiX.js';
3
- export { e as ConvertedFile, E as ExportFormat, F as FontsHandle, f as OPFApiError, g as OPFExportError, h as OPFImportError, i as SlideSelection, p as parseSlideSelection } from './conversion-Dh8Q4RiX.js';
2
+ import { C as ConvertInput, a as ConvertOptions, b as ConvertResult, c as ConvertFormat, O as OpenOptions, S as SaveOptions, d as SaveResult } from './conversion-DyR-2OkM.js';
3
+ export { e as ConvertedFile, E as ExportFormat, F as FontsHandle, f as OPFApiError, g as OPFExportError, h as OPFImportError, i as SlideSelection, p as parseSlideSelection } from './conversion-DyR-2OkM.js';
4
4
  import { v as validate$1 } from './validator-Dym2zYVK.js';
5
5
  export { C as CATALOG_SCHEMA, a as CatalogDisplayKind, b as CatalogRecordKind, D as DEFAULT_VALIDATION_THRESHOLDS, O as OPFValidationError, c as assertValid, d as assertValidCatalogRecord, e as catalogDisplayKinds, f as catalogSchemaNames, g as findValidationRule, h as validateCatalogRecord, i as validationRules } from './validator-Dym2zYVK.js';
6
6
  import { R as ResolveVariablesOptions, h as VariableDiagnostic } from './validation-types-JW8e06ee.js';
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var stats2 = (presentation2, options) => stats(presentation2, withDefaultCatalog
31
31
  var paginate2 = (input, options) => paginate(input, withDefaultCatalog(options));
32
32
  var embed2 = (document, options) => embed(document, withDefaultCatalog(options));
33
33
  var edit2 = (deck, patch, options) => edit(deck, patch, withDefaultCatalog(options));
34
- var engine = () => import('./conversion-AJEJJHLO.js');
34
+ var engine = () => import('./conversion-ZPBLP2K6.js');
35
35
  var rethrow = (error, fallback) => {
36
36
  if (error instanceof OPFValidationError) throw error;
37
37
  throw asApiError(OPFApiError, error, fallback);
@@ -1,6 +1,6 @@
1
1
  export { b as DeckReport, c as DeckReportOptions, r as readDeckReport } from './deck-report-DHaboWbM.js';
2
- import { F as FontsHandle, j as EmbeddedFace, R as Renderer, k as Reporter, P as Peer, l as PptxModule, f as OPFApiError } from './conversion-Dh8Q4RiX.js';
3
- export { m as ConversionPlan, D as Diagnostic, n as EXPORT_FORMATS, o as EXPORT_TEXT, q as ExportContext, r as ExportFile, s as ExportFontSummary, E as ExportFormat, t as ExportOptions, u as ExportResult, v as ExportRun, w as ExportText, I as ImportOptions, x as ImportResult, g as OPFExportError, h as OPFImportError, y as PEER_RANGES, z as PPTX_PACKAGE, A as PreparedExport, B as RENDER_EXTRAS, G as RENDER_PACKAGE, H as ReportFinding, J as ResolvedExportOptions, i as SlideSelection, K as Source, V as VERSIONS, L as asApiError, M as assetRoot, N as checkDate, Q as checkOptions, T as checkRenamedOptions, U as checkScale, W as deckFileFormat, X as exportFormatOf, Y as finishReport, Z as importPresentation, _ as loadPptx, $ as loadRenderer, a0 as locatePackage, a1 as missingPeerFrom, p as parseSlideSelection, a2 as planConversion, a3 as prepareExport, a4 as reaches, a5 as readInput, a6 as reportThrown, a7 as resolveExportOptions, a8 as runExport, a9 as runPreparedExport, aa as writePlanned } from './conversion-Dh8Q4RiX.js';
2
+ import { F as FontsHandle, j as EmbeddedFace, R as Renderer, k as Reporter, P as Peer, l as PptxModule, f as OPFApiError } from './conversion-DyR-2OkM.js';
3
+ export { m as ConversionPlan, D as Diagnostic, n as EXPORT_FORMATS, o as EXPORT_TEXT, q as ExportContext, r as ExportFile, s as ExportFontSummary, E as ExportFormat, t as ExportOptions, u as ExportResult, v as ExportRun, w as ExportText, I as ImportOptions, x as ImportResult, g as OPFExportError, h as OPFImportError, y as PEER_RANGES, z as PPTX_PACKAGE, A as PreparedExport, B as RENDER_EXTRAS, G as RENDER_PACKAGE, H as ReportFinding, J as ResolvedExportOptions, i as SlideSelection, K as Source, V as VERSIONS, L as asApiError, M as assetRoot, N as checkDate, Q as checkOptions, T as checkRenamedOptions, U as checkScale, W as deckFileFormat, X as exportFormatOf, Y as finishReport, Z as importPresentation, _ as loadPptx, $ as loadRenderer, a0 as locatePackage, a1 as missingPeerFrom, p as parseSlideSelection, a2 as planConversion, a3 as prepareExport, a4 as reaches, a5 as readInput, a6 as reportThrown, a7 as resolveExportOptions, a8 as runExport, a9 as runPreparedExport, aa as writePlanned } from './conversion-DyR-2OkM.js';
4
4
  import { C as Catalog } from './catalog-refs-CSHevLn1.js';
5
5
  import { e as PresentationPaginationResult } from './pagination-R8YFwVj7.js';
6
6
  import './presentation-Bwo8Ko6D.js';
@@ -1,5 +1,5 @@
1
- import { loadRenderer, listFontDirectories, leaseSharedFonts, prepareFonts } from './chunk-NJRFCM4H.js';
2
- export { EXPORT_FORMATS, EXPORT_TEXT, PEER_RANGES, PPTX_PACKAGE, RENDER_EXTRAS, RENDER_PACKAGE, Reporter, VERSIONS, assetRoot, checkDate, checkDestinations, checkOptions, checkOutputPattern, checkRenamedOptions, checkScale, createZip, deckFileFormat, deckStem, embeddedFor, exportFormatOf, finishReport, importPresentation, leaseSharedFonts, listFontDirectories, loadPptx, loadRenderer, localAssetProblems, locatePackage, missingPeerFrom, padNumber, planConversion, pointerOf, prepareExport, prepareFonts, publishNew, reaches, readInput, reportThrown, resolveExportOptions, runExport, runImport, runPreparedExport, stemOf, writeFiles, writePlanned } from './chunk-NJRFCM4H.js';
1
+ import { loadRenderer, listFontDirectories, leaseSharedFonts, prepareFonts } from './chunk-F7USLFZE.js';
2
+ export { EXPORT_FORMATS, EXPORT_TEXT, PEER_RANGES, PPTX_PACKAGE, RENDER_EXTRAS, RENDER_PACKAGE, Reporter, VERSIONS, assetRoot, checkDate, checkDestinations, checkOptions, checkOutputPattern, checkRenamedOptions, checkScale, createZip, deckFileFormat, deckStem, embeddedFor, exportFormatOf, finishReport, importPresentation, leaseSharedFonts, listFontDirectories, loadPptx, loadRenderer, localAssetProblems, locatePackage, missingPeerFrom, padNumber, planConversion, pointerOf, prepareExport, prepareFonts, publishNew, reaches, readInput, reportThrown, resolveExportOptions, runExport, runImport, runPreparedExport, stemOf, writeFiles, writePlanned } from './chunk-F7USLFZE.js';
3
3
  import { defaultCatalogs } from './chunk-IC6OPLMU.js';
4
4
  export { defaultCatalogs, galleryMismatch } from './chunk-IC6OPLMU.js';
5
5
  import { OPFApiError } from './chunk-WW62PPYV.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpresentation/opf",
3
- "version": "0.26.0-rc.0",
3
+ "version": "0.26.0",
4
4
  "description": "Canonical Open Presentation Format schemas, TypeScript types, local validation and composition; in Node, the file API and the CLI defaults register the pptx.gallery catalog of @openpresentation/gallery.",
5
5
  "license": "MIT",
6
6
  "author": "Open Presentation",
@@ -137,7 +137,7 @@
137
137
  "ajv-formats": "^3.0.1",
138
138
  "jsonc-parser": "3.3.1",
139
139
  "yaml": "^2.9.1",
140
- "@openpresentation/gallery": "^0.26.0-rc.0"
140
+ "@openpresentation/gallery": "^0.26.0"
141
141
  },
142
142
  "devDependencies": {
143
143
  "@expo-google-fonts/arimo": "0.4.3",
@@ -157,8 +157,8 @@
157
157
  "typescript": "~5.9.3"
158
158
  },
159
159
  "peerDependencies": {
160
- "@openpresentation/opf-pptx": "^0.26.0-rc.0",
161
- "@openpresentation/opf-render": "^0.26.0-rc.0"
160
+ "@openpresentation/opf-pptx": "^0.26.0",
161
+ "@openpresentation/opf-render": "^0.26.0"
162
162
  },
163
163
  "peerDependenciesMeta": {
164
164
  "@openpresentation/opf-pptx": {
@@ -178,5 +178,5 @@
178
178
  "test:packed": "node test/packed-install-smoke.mjs",
179
179
  "pack:dry-run": "pnpm run build:deps && pnpm run build && npm pack --dry-run --cache /tmp/opf-npm-cache"
180
180
  },
181
- "gitHead": "fb80b1e8a7d6dd4a77946509fab9af7713657ce6"
181
+ "gitHead": "ae4ddd76e40b5778c1fdc255e4e3b24334748d7e"
182
182
  }