@openpresentation/opf 0.26.9 → 0.26.10
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.
- package/dist/{chunk-XMCBFQPY.js → chunk-L52N5FZZ.js} +1 -1
- package/dist/{conversion-Bnx8mWl9.d.ts → conversion-ChmGA8Ul.d.ts} +2 -2
- package/dist/{conversion-63DPSXBJ.js → conversion-IVKCKMCN.js} +1 -1
- package/dist/docs.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/node-engine.d.ts +2 -2
- package/dist/node-engine.js +2 -2
- package/package.json +5 -5
|
@@ -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.
|
|
120
|
+
var PEER_RANGES = { [RENDER_PACKAGE]: "^0.26.10", [PPTX_PACKAGE]: "^0.26.10" };
|
|
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.
|
|
47
|
-
readonly "@openpresentation/opf-pptx": "^0.26.
|
|
46
|
+
readonly "@openpresentation/opf-render": "^0.26.10";
|
|
47
|
+
readonly "@openpresentation/opf-pptx": "^0.26.10";
|
|
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-
|
|
1
|
+
export { convertFiles, deckFileFormat, exportFormatOf, openDeck, planConversion, readInput, saveDeck, writePlanned } from './chunk-L52N5FZZ.js';
|
|
2
2
|
import './chunk-NVT2YDVC.js';
|
|
3
3
|
import './chunk-D25BNPTW.js';
|
|
4
4
|
import './chunk-UJSAR6GF.js';
|
package/dist/docs.js
CHANGED
|
@@ -82,7 +82,7 @@ var docsData = Object.freeze([
|
|
|
82
82
|
"slug": "ecosystem-development",
|
|
83
83
|
"file": "docs/ecosystem-development.md",
|
|
84
84
|
"title": "Local development across the packages",
|
|
85
|
-
"markdown": "# Local development across the packages\n\nUse Node 24 for the current source and published packages. All six packages live in this repository, one pnpm workspace, so there is nothing to link: `workspace:` dependencies resolve to the sibling directories, and a change that touches several packages is built and tested together.\n\n```sh\npnpm install\npnpm build # every package, in dependency order\npnpm check:changed # only what your branch changed (the check to run after each change)\npnpm test # everything; CI and the last run before a pull request\npnpm test:integration # the private tests that need several packages at once\n```\n\nFor a cheap checkout of one task (small blobless worktree, one shared pnpm store), use `node scripts/agent-worktree.mjs add <task>`.\n\nCI is `verify` (`.github/workflows/ci.yml`): one required check that runs the packages your change affects, with their dependents at the contract tier (`test:contract` of each package); `nightly.yml` runs the Windows, macOS, Node 22 and 26 and packed-install legs and the weekly `slow-checks.yml` the published-package consumers. See [the CI design](programs/monorepo/ci-design.md). Add the label `full-ci` to a pull request that touches OS- or Node-version-specific code. There is no `Depends-On`, no lock to roll and no merge queue.\n\nOn Windows, keep the checkout under a short path (esbuild and Biome exceed MAX_PATH deeper) and see the notes in `AGENTS.md`. npm and pnpm orchestration in the scripts invokes the package manager's JavaScript entrypoint with the selected Node runtime instead of a batch shim through a shell, so paths with spaces and shell metacharacters stay literal arguments.\n\nExecute the installed-package browser harnesses after their corresponding build:\n\n```sh\npnpm test:packages\npnpm test:packed-browser\npnpm test:registry-ecosystem\npnpm test:packed-browser registry\n```\n\nThe renderer package supplies the locked Playwright test dependency. Install Chromium with `pnpm --filter @openpresentation/opf-render exec playwright install --with-deps chromium` on Linux. Local Windows runs use Edge; `OPF_BROWSER_CHANNEL` can explicitly select another installed Playwright channel. CI uses the matching official Playwright container pinned by digest (one string in every workflow, `pnpm check:playwright-pin`), without installing OS packages during each run.\n\nEach build writes `artifacts/editor/packed-browser-manifest.json` with the mode, installed versions, consumer build ID, dependency-lock hash and exact font/HTML/JavaScript hashes. The runner rejects a different mode, stale consumer or changed asset. It serves only the verified bytes on loopback and rejects external requests and network writes. Rebuild before switching between candidate and registry modes. Reports include the browser and Node versions and are saved by mode/runtime; failures retain a screenshot.\n\n`node scripts/test-packed-browser-guards.mjs` verifies those four rejection cases against the current disposable harness and restores each changed fixture byte-for-byte. CI runs it after the registry browser checks.\n\nSeven suites exercise canvas, rich text, lists, creation, layout, block moves and styled tables. Real browser input covers divider resizing/cancellation/concurrent changes, block dragging, merged-cell typing/redo/undo, plain-to-rich conversion and bold formatting, and empty-cell typing/undo. Conversion and formatting currently create separate undo transactions. Harness DOM assertions also cover renderer agreement and preservation. These checks do not replace full application export/reimport, public deployment checks or native PowerPoint raster evidence.\n\n`pnpm test:ecosystem` validates the dynamic composition fixture, edits and undoes a composition, renders SVG/PNG/PDF, exports editable PPTX, checks OOXML text-box coordinates against the shared geometry, and imports the result back into schema-valid OPF. Artifacts are written to a temporary directory and its location is printed. For full gallery render coverage, run `pnpm test:gallery -- --render` (or invoke the script with `--render`): it validates all generated documents and can render them with the local SVG engine.\n\n`pnpm test:pagination` verifies long-text and table pagination through SVG and editable PPTX, including exact source reconstruction, table row counts, and absence of extra exporter-created pages. It writes review artifacts under `artifacts/pagination/`.\n\n`pnpm test:fonts` verifies actual-font measurement across editor, SVG, pagination, and PPTX. It also runs the offline font-switch matrix (`scripts/test-font-switch-ecosystem.mjs`, FF-09): a seeded pairwise covering array of 58 decks over the 14 gallery dimensions, plus fixed content-type, block-replacement, per-slide override, CJK-in-Latin, theme and language switches, each switched A to B and back to A. A value class is a group of catalog values that take the same path through the engines, derived from the catalogs in the script: font schemes by language family, then by licensing and preview policy (Office metric, Office visual-only, monospace, open Google); one language per script family in the array and every other catalog script in a language chain; every layout family; the eight content blocks; every distinct chart export path of the non-deprecated chart types; header/footer, background (theme, solid, gradient, pattern, image) and image treatments by kind; and the first and last record of the metadata dimensions. Dimensions that a deck can carry several times (font scheme states, layouts, blocks, charts, backgrounds, images) take several values per deck. Every state is exported and checked with the FF-08 typeface inventory, the catalog's literal theme fonts, a package structure check, a preview re-render and a re-import. It pins the office font pack with visual substitution and asserts every substitution; known engine limitations, including chart types the preview approximates or the exporter writes as bar charts, are named expected failures in the script that fail with a \"limitation resolved\" message when they go away. It runs no browser and no Office. Its report is written to `artifacts/font-switch-matrix/report.json`. The pairwise decks run on four worker threads with a fixed partition (deck i on worker i mod 4), so every host verifies each deck after the same earlier decks; `OPF_MATRIX_WORKERS=1` runs them in one thread, `OPF_MATRIX_WORKERS=n` on n workers. See [font fidelity](font-fidelity.md) for loading and embedding local fonts and for current native PowerPoint limits.\n\n## Regenerating the example goldens (MR-31)\n\nThe raster golden of the examples corpus lives in one place, `packages/render/test/golden/` (a directory per baseline, one file per deck; `packages/render/test/golden/README.md` has the layout and `notes/` the review notes). The corpus is this repository's `examples/` and the renderer, core and the gallery are the checkout's own, so there is no lock selection, no core fixture and no sibling renderer to pin. A pull request that changes `examples/`, the core code that composes them, the gallery's records or the renderer's drawing moves slide hashes (or only the corpus digest, as FA-33 did: `notes/MR-31.md`); `pnpm test` and the `goldens` job of `ci.yml` fail until the baseline follows.\n\n```sh\ngh workflow run golden.yml --ref <branch> # or add the `regenerate-goldens` label to the pull request\npnpm golden:update --local # a review candidate under packages/render/artifacts/golden/, never the baseline\n```\n\n- **The workflow.** `.github/workflows/golden.yml` has two jobs. `regenerate` (read-only token) runs `pnpm golden:update` on the branch in the pinned Playwright image: it builds core, the gallery and the renderer, renders every slide, rewrites only the per-deck files whose hashes differ and uploads the patch, `diff.json` and the review sheets of the changed slides. `push` (write token, no branch code) applies the patch only when it touches `packages/render/test/golden/`, pushes one commit and dispatches `ci.yml` on it (a push made with `GITHUB_TOKEN` starts no workflow). It refuses the default branch and a fork.\n- **`pnpm golden:update`.** `scripts/golden-update.mjs`: without `--local` it refuses to rewrite the baseline anywhere but GitHub Actions on Linux in the Playwright image (the check the renderer's `golden-pinned-image.mjs` made); with `--local` it writes the review candidate only. The comparison is never changed (exact hashes, the baseline's own scale and font policy).\n- **Review.** The commit is evidence, not an approval. Open the artifact, look at the changed slides, write the reason in `notes/<ID>.md` and in the pull request, then approve. A change that moves one slide for a reason other than the PR's intent is a bug in the PR.\n- **Merge rhythm.** A merge conflict in deck files is resolved by merging `main`, taking either side, and regenerating. One pull request changes a layout and the golden of every package together (risk 23 of the monorepo plan).\n\n## Trying the packages in a site checkout\n\nThe three sites (openpresentation.org, pptx.gallery, pptx.dev) live in their own repositories and install the published packages. To browse pptx.gallery with this checkout, keep `pptx-gallery` in the same parent directory as this repository, install its dependencies, build here, run `node scripts/link-ecosystem.mjs` (it replaces the installed `@openpresentation/*` packages in the site's `node_modules` with links to this checkout without saving paths in manifests or lockfiles; reinstalling replaces the links, so rerun it) and start the site with `OPF_LOCAL_WORKSPACE=1 pnpm dev`. Layout detail pages have an interactive composition example; the flag expands Turbopack's local root to include this checkout. Build before starting the site, and stop and restart it around clean rebuilds: removing a linked `dist` directory during compilation can leave Turbopack with stale missing-module errors.\n\nAfter a release, the sites are bumped by `bump-sites.yml` (`release-process.md`); nothing here deploys or changes a site.\n\n## Editor host bundle and font provenance\n\nContributor details moved from the [browser integration guide](live-editor.md); published-package users can start there.\n\nFor library development, build the tarballs of the six packages from this checkout and install them into a clean consumer:\n\n```sh\npnpm build\npnpm pack:ecosystem\npnpm test:packed-ecosystem\n```\n\nThe packed consumer installs actual tarballs without workspace aliases, exercises editing/SVG/PPTX, checks TypeScript declarations, and bundles a browser entry without Node shims. A public release is the one-click workflow in [the release process](release-process.md); it packs and smoke-tests the same way before it publishes.\n\nThe gallery host example also offers local PPTX file import with preview/diagnostics and editable PowerPoint download. It commits active canvas text before export, shares preview text measurements and applies imports as a single undoable change. Save OPF to preserve the original source; native PowerPoint positions, fonts and unsupported features can change during conversion. The browser E2E checks run offline after loading and inspect the downloaded native merged table, then reimport and undo/redo. Native edit/save/reopen is a separate targeted check, not a pixel-equivalence claim.\n\n`pnpm vendor:gallery-editor --version X.Y.Z [--ref vX.Y.Z]` builds the host controls of the pptx.gallery site's vendored editor (`public/opf-editor`) while resolving libraries only from a fresh npm consumer; the manifest records both the example source hashes and the registry package integrities. It reads the editor examples from `packages/editor/examples` at `--ref` (default `HEAD`; the release tag `vX.Y.Z` for a release) and installs the published set `--version` names (`registryPlan({ tag })` in `scripts/registry-plan.mjs`: all six packages share one version; `--pin <package>=<X.Y.Z>` builds against a published set that is not shared) into a clean consumer under `artifacts/npm/gallery-editor-consumer`, with no sibling checkout but the site (`--site <dir>`, default `../pptx-gallery`) and no `release-plan.json`. After a stable release `bump-sites.yml` runs it in the pptx.gallery leg and commits the bundle into the bump pull request (`docs/programs/monorepo/site-workflows/README.md`).\n\nScript fonts: when the pinned editor example loads faces from `./script-fonts/` and the pinned renderer has the script pack (0.10.0 and later), the registry build also writes `script-fonts.json` and lists its hash in `manifest.json`. The manifest is the reviewable half: every `@expo-google-fonts/noto-*` package, exact version, SPDX license, license-file hash and each face's SHA-256, taken from the published renderer. The faces are binaries (63 files, 66.9 MiB), so they are never committed to the gallery repository. The gallery build copies them from its own pinned npm dependencies into the untracked `public/opf-editor/script-fonts/` directory, verifying every hash, and writes the license notices beside them; nothing is fetched from a font CDN. See `scripts/gallery-script-fonts.mjs` and the gallery's `scripts/prepare-editor-script-fonts.mjs`.\n\nBase fonts (FF-41): when the pinned editor example loads `base-fonts.json` (opf-editor 0.10.5: the example passes the faces as the renderer's `extraLazyFonts`, renderer 0.11.7 and later; 0.10.4's example used its own `examples/base-font-gate.js`), the registry build starts the editor with Roboto Regular alone in `fonts.json` (217 KB instead of 12.8 MB) and writes every other eager face (Roboto in six more styles, Roboto Mono, the Office substitutes) as a separate file named after its hash beside it, listed with its SHA-256 in `base-fonts.json` and in `manifest.json`; the editor fetches only the faces a document draws, verified, through its font gate (`scripts/gallery-base-fonts.mjs`). An older pinned example keeps every eager face in `fonts.json`. A default Roboto deck loads about 0.7 MB of fonts instead of 12.8 MB.\n\nLazy fonts: when the pinned editor example calls `ensureLazyFonts` and the pinned renderer vendors faces (Intos for the default Aptos scheme and the open families, renderer 0.11.0 and later), the registry build also writes `lazy-fonts.json` and lists its hash in `manifest.json`. It pins every vendored package (exact version, SPDX license, license-file and notice hashes) and each face SHA-256, taken from the published renderer. The faces are binaries, so they are not committed either: the gallery build copies them from its pinned `@openpresentation/opf-render` package (`fonts/<name>/`) into the untracked `public/opf-editor/fonts/` directory, verifying every hash, and the editor fetches only the families a document uses, same-origin. See `scripts/gallery-lazy-fonts.mjs` and the gallery's `scripts/prepare-editor-lazy-fonts.mjs`.\n\nThe browser bundle links `playground.js.LEGAL.txt`, included in the hashed resources. It contains bundled license notices and package license files, including the vendored PptxGenJS MIT license. For dependencies that publish only an explicit MIT declaration in their README, the build retains that declaration/attribution and the standard terms; omitted upstream notices use a version-specific source URL and verified supplement hash. License collection runs offline from the verified installation and committed supplement. Runtime JavaScript is not rewritten to normalize comment whitespace.\n"
|
|
85
|
+
"markdown": "# Local development across the packages\n\nUse Node 24 for the current source and published packages. All six packages live in this repository, one pnpm workspace, so there is nothing to link: `workspace:` dependencies resolve to the sibling directories, and a change that touches several packages is built and tested together.\n\n```sh\npnpm install\npnpm build # every package, in dependency order\npnpm check:changed # only what your branch changed (the check to run after each change)\npnpm test # everything; CI and the last run before a pull request\npnpm test:integration # the private tests that need several packages at once\n```\n\nFor a cheap checkout of one task (small blobless worktree, one shared pnpm store), use `node scripts/agent-worktree.mjs add <task>`.\n\nCI is `verify` (`.github/workflows/ci.yml`): one required check that runs the packages your change affects, with their dependents at the contract tier (`test:contract` of each package); `nightly.yml` runs the Windows, macOS, Node 22 and 26, packed-install and npm 12 consumer legs (the packed packages installed by npm 12 with its defaults: no install scripts, no git or remote dependencies; `pnpm test:npm12-consumer`) and the weekly `slow-checks.yml` the published-package consumers. See [the CI design](programs/monorepo/ci-design.md). Add the label `full-ci` to a pull request that touches OS- or Node-version-specific code. There is no `Depends-On`, no lock to roll and no merge queue.\n\nOn Windows, keep the checkout under a short path (esbuild and Biome exceed MAX_PATH deeper) and see the notes in `AGENTS.md`. npm and pnpm orchestration in the scripts invokes the package manager's JavaScript entrypoint with the selected Node runtime instead of a batch shim through a shell, so paths with spaces and shell metacharacters stay literal arguments.\n\nExecute the installed-package browser harnesses after their corresponding build:\n\n```sh\npnpm test:packages\npnpm test:packed-browser\npnpm test:registry-ecosystem\npnpm test:packed-browser registry\n```\n\nThe renderer package supplies the locked Playwright test dependency. Install Chromium with `pnpm --filter @openpresentation/opf-render exec playwright install --with-deps chromium` on Linux. Local Windows runs use Edge; `OPF_BROWSER_CHANNEL` can explicitly select another installed Playwright channel. CI uses the matching official Playwright container pinned by digest (one string in every workflow, `pnpm check:playwright-pin`), without installing OS packages during each run.\n\nEach build writes `artifacts/editor/packed-browser-manifest.json` with the mode, installed versions, consumer build ID, dependency-lock hash and exact font/HTML/JavaScript hashes. The runner rejects a different mode, stale consumer or changed asset. It serves only the verified bytes on loopback and rejects external requests and network writes. Rebuild before switching between candidate and registry modes. Reports include the browser and Node versions and are saved by mode/runtime; failures retain a screenshot.\n\n`node scripts/test-packed-browser-guards.mjs` verifies those four rejection cases against the current disposable harness and restores each changed fixture byte-for-byte. CI runs it after the registry browser checks.\n\nSeven suites exercise canvas, rich text, lists, creation, layout, block moves and styled tables. Real browser input covers divider resizing/cancellation/concurrent changes, block dragging, merged-cell typing/redo/undo, plain-to-rich conversion and bold formatting, and empty-cell typing/undo. Conversion and formatting currently create separate undo transactions. Harness DOM assertions also cover renderer agreement and preservation. These checks do not replace full application export/reimport, public deployment checks or native PowerPoint raster evidence.\n\n`pnpm test:ecosystem` validates the dynamic composition fixture, edits and undoes a composition, renders SVG/PNG/PDF, exports editable PPTX, checks OOXML text-box coordinates against the shared geometry, and imports the result back into schema-valid OPF. Artifacts are written to a temporary directory and its location is printed. For full gallery render coverage, run `pnpm test:gallery -- --render` (or invoke the script with `--render`): it validates all generated documents and can render them with the local SVG engine.\n\n`pnpm test:pagination` verifies long-text and table pagination through SVG and editable PPTX, including exact source reconstruction, table row counts, and absence of extra exporter-created pages. It writes review artifacts under `artifacts/pagination/`.\n\n`pnpm test:fonts` verifies actual-font measurement across editor, SVG, pagination, and PPTX. It also runs the offline font-switch matrix (`scripts/test-font-switch-ecosystem.mjs`, FF-09): a seeded pairwise covering array of 58 decks over the 14 gallery dimensions, plus fixed content-type, block-replacement, per-slide override, CJK-in-Latin, theme and language switches, each switched A to B and back to A. A value class is a group of catalog values that take the same path through the engines, derived from the catalogs in the script: font schemes by language family, then by licensing and preview policy (Office metric, Office visual-only, monospace, open Google); one language per script family in the array and every other catalog script in a language chain; every layout family; the eight content blocks; every distinct chart export path of the non-deprecated chart types; header/footer, background (theme, solid, gradient, pattern, image) and image treatments by kind; and the first and last record of the metadata dimensions. Dimensions that a deck can carry several times (font scheme states, layouts, blocks, charts, backgrounds, images) take several values per deck. Every state is exported and checked with the FF-08 typeface inventory, the catalog's literal theme fonts, a package structure check, a preview re-render and a re-import. It pins the office font pack with visual substitution and asserts every substitution; known engine limitations, including chart types the preview approximates or the exporter writes as bar charts, are named expected failures in the script that fail with a \"limitation resolved\" message when they go away. It runs no browser and no Office. Its report is written to `artifacts/font-switch-matrix/report.json`. The pairwise decks run on four worker threads with a fixed partition (deck i on worker i mod 4), so every host verifies each deck after the same earlier decks; `OPF_MATRIX_WORKERS=1` runs them in one thread, `OPF_MATRIX_WORKERS=n` on n workers. See [font fidelity](font-fidelity.md) for loading and embedding local fonts and for current native PowerPoint limits.\n\n## Regenerating the example goldens (MR-31)\n\nThe raster golden of the examples corpus lives in one place, `packages/render/test/golden/` (a directory per baseline, one file per deck; `packages/render/test/golden/README.md` has the layout and `notes/` the review notes). The corpus is this repository's `examples/` and the renderer, core and the gallery are the checkout's own, so there is no lock selection, no core fixture and no sibling renderer to pin. A pull request that changes `examples/`, the core code that composes them, the gallery's records or the renderer's drawing moves slide hashes (or only the corpus digest, as FA-33 did: `notes/MR-31.md`); `pnpm test` and the `goldens` job of `ci.yml` fail until the baseline follows.\n\n```sh\ngh workflow run golden.yml --ref <branch> # or add the `regenerate-goldens` label to the pull request\npnpm golden:update --local # a review candidate under packages/render/artifacts/golden/, never the baseline\n```\n\n- **The workflow.** `.github/workflows/golden.yml` has two jobs. `regenerate` (read-only token) runs `pnpm golden:update` on the branch in the pinned Playwright image: it builds core, the gallery and the renderer, renders every slide, rewrites only the per-deck files whose hashes differ and uploads the patch, `diff.json` and the review sheets of the changed slides. `push` (write token, no branch code) applies the patch only when it touches `packages/render/test/golden/`, pushes one commit and dispatches `ci.yml` on it (a push made with `GITHUB_TOKEN` starts no workflow). It refuses the default branch and a fork.\n- **`pnpm golden:update`.** `scripts/golden-update.mjs`: without `--local` it refuses to rewrite the baseline anywhere but GitHub Actions on Linux in the Playwright image (the check the renderer's `golden-pinned-image.mjs` made); with `--local` it writes the review candidate only. The comparison is never changed (exact hashes, the baseline's own scale and font policy).\n- **Review.** The commit is evidence, not an approval. Open the artifact, look at the changed slides, write the reason in `notes/<ID>.md` and in the pull request, then approve. A change that moves one slide for a reason other than the PR's intent is a bug in the PR.\n- **Merge rhythm.** A merge conflict in deck files is resolved by merging `main`, taking either side, and regenerating. One pull request changes a layout and the golden of every package together (risk 23 of the monorepo plan).\n\n## Trying the packages in a site checkout\n\nThe three sites (openpresentation.org, pptx.gallery, pptx.dev) live in their own repositories and install the published packages. To browse pptx.gallery with this checkout, keep `pptx-gallery` in the same parent directory as this repository, install its dependencies, build here, run `node scripts/link-ecosystem.mjs` (it replaces the installed `@openpresentation/*` packages in the site's `node_modules` with links to this checkout without saving paths in manifests or lockfiles; reinstalling replaces the links, so rerun it) and start the site with `OPF_LOCAL_WORKSPACE=1 pnpm dev`. Layout detail pages have an interactive composition example; the flag expands Turbopack's local root to include this checkout. Build before starting the site, and stop and restart it around clean rebuilds: removing a linked `dist` directory during compilation can leave Turbopack with stale missing-module errors.\n\nAfter a release, the sites are bumped by `bump-sites.yml` (`release-process.md`); nothing here deploys or changes a site.\n\n## Editor host bundle and font provenance\n\nContributor details moved from the [browser integration guide](live-editor.md); published-package users can start there.\n\nFor library development, build the tarballs of the six packages from this checkout and install them into a clean consumer:\n\n```sh\npnpm build\npnpm pack:ecosystem\npnpm test:packed-ecosystem\n```\n\nThe packed consumer installs actual tarballs without workspace aliases, exercises editing/SVG/PPTX, checks TypeScript declarations, and bundles a browser entry without Node shims. A public release is the one-click workflow in [the release process](release-process.md); it packs and smoke-tests the same way before it publishes.\n\nThe gallery host example also offers local PPTX file import with preview/diagnostics and editable PowerPoint download. It commits active canvas text before export, shares preview text measurements and applies imports as a single undoable change. Save OPF to preserve the original source; native PowerPoint positions, fonts and unsupported features can change during conversion. The browser E2E checks run offline after loading and inspect the downloaded native merged table, then reimport and undo/redo. Native edit/save/reopen is a separate targeted check, not a pixel-equivalence claim.\n\n`pnpm vendor:gallery-editor --version X.Y.Z [--ref vX.Y.Z]` builds the host controls of the pptx.gallery site's vendored editor (`public/opf-editor`) while resolving libraries only from a fresh npm consumer; the manifest records both the example source hashes and the registry package integrities. It reads the editor examples from `packages/editor/examples` at `--ref` (default `HEAD`; the release tag `vX.Y.Z` for a release) and installs the published set `--version` names (`registryPlan({ tag })` in `scripts/registry-plan.mjs`: all six packages share one version; `--pin <package>=<X.Y.Z>` builds against a published set that is not shared) into a clean consumer under `artifacts/npm/gallery-editor-consumer`, with no sibling checkout but the site (`--site <dir>`, default `../pptx-gallery`) and no `release-plan.json`. After a stable release `bump-sites.yml` runs it in the pptx.gallery leg and commits the bundle into the bump pull request (`docs/programs/monorepo/site-workflows/README.md`).\n\nScript fonts: when the pinned editor example loads faces from `./script-fonts/` and the pinned renderer has the script pack (0.10.0 and later), the registry build also writes `script-fonts.json` and lists its hash in `manifest.json`. The manifest is the reviewable half: every `@expo-google-fonts/noto-*` package, exact version, SPDX license, license-file hash and each face's SHA-256, taken from the published renderer. The faces are binaries (63 files, 66.9 MiB), so they are never committed to the gallery repository. The gallery build copies them from its own pinned npm dependencies into the untracked `public/opf-editor/script-fonts/` directory, verifying every hash, and writes the license notices beside them; nothing is fetched from a font CDN. See `scripts/gallery-script-fonts.mjs` and the gallery's `scripts/prepare-editor-script-fonts.mjs`.\n\nBase fonts (FF-41): when the pinned editor example loads `base-fonts.json` (opf-editor 0.10.5: the example passes the faces as the renderer's `extraLazyFonts`, renderer 0.11.7 and later; 0.10.4's example used its own `examples/base-font-gate.js`), the registry build starts the editor with Roboto Regular alone in `fonts.json` (217 KB instead of 12.8 MB) and writes every other eager face (Roboto in six more styles, Roboto Mono, the Office substitutes) as a separate file named after its hash beside it, listed with its SHA-256 in `base-fonts.json` and in `manifest.json`; the editor fetches only the faces a document draws, verified, through its font gate (`scripts/gallery-base-fonts.mjs`). An older pinned example keeps every eager face in `fonts.json`. A default Roboto deck loads about 0.7 MB of fonts instead of 12.8 MB.\n\nLazy fonts: when the pinned editor example calls `ensureLazyFonts` and the pinned renderer vendors faces (Intos for the default Aptos scheme and the open families, renderer 0.11.0 and later), the registry build also writes `lazy-fonts.json` and lists its hash in `manifest.json`. It pins every vendored package (exact version, SPDX license, license-file and notice hashes) and each face SHA-256, taken from the published renderer. The faces are binaries, so they are not committed either: the gallery build copies them from its pinned `@openpresentation/opf-render` package (`fonts/<name>/`) into the untracked `public/opf-editor/fonts/` directory, verifying every hash, and the editor fetches only the families a document uses, same-origin. See `scripts/gallery-lazy-fonts.mjs` and the gallery's `scripts/prepare-editor-lazy-fonts.mjs`.\n\nThe browser bundle links `playground.js.LEGAL.txt`, included in the hashed resources. It contains bundled license notices and package license files, including the vendored PptxGenJS MIT license. For dependencies that publish only an explicit MIT declaration in their README, the build retains that declaration/attribution and the standard terms; omitted upstream notices use a version-specific source URL and verified supplement hash. License collection runs offline from the verified installation and committed supplement. Runtime JavaScript is not rewritten to normalize comment whitespace.\n"
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
"slug": "evidence-2026-09-08-windows",
|
|
@@ -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, 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`. What a bump touches is the site's own\nbusiness, so each site owns one script, `pnpm opf:bump <X.Y.Z> [--opf <checkout>]` (`scripts/opf-bump.mjs` of the site), that sets its\n`@openpresentation/*` pins, refreshes its lockfile, regenerates every file it derives from the release (a release history from the\nregistry and these release notes, a pin mirror, preview fonts, chart previews, the vendored editor bundle, a homepage manifest) and runs\nthe checks of its own CI that read them; `scripts/bump-site.mjs` decides the label and calls it (a site without one gets only the\ngeneric pins and lockfile). A bump needs no human edit to be green; one that does (a release that changes behavior or removes a\ndocument the site links) fails the site's CI and stays open. 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"
|
|
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,\n registry and npm 12 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; a version already on npm is\n skipped. Once npm accepted an upload the package is published: if the registry does not serve it yet it is logged as\n **pending**, which is not a failure, and the job goes on), `verify` (below: it first waits for the registry), `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 registry serves an accepted upload some time after `npm publish` exits 0: 1 to 7 minutes for v0.26.0-rc.0 and about 20 for core\nin v0.26.9. That lag is waited out in the `verify` job, never failed in `publish` (a lagging package used to fail the publish job\nand skip verify and the GitHub release; a manual `gh run rerun --failed` was the fix). The `verify` job runs, in order:\n\n1. `node scripts/release.mjs await-registry X.Y.Z --wait 60 --poll 15 --max-poll 120`: for each package still missing, requests\n the version's own registry document (`/@scope%2fname/X.Y.Z`) with `Cache-Control: no-cache` and a unique query string, so\n neither the npm CLI cache nor a CDN's cached 404 answers. The first look is immediate; the interval starts at 15 s, grows by\n half each round up to 120 s, and each round logs which packages are still missing and for how long. A served package is not\n asked again. Only when the 60 minute budget is spent does it fail, with the staged-package guidance (see Troubleshooting). A\n lookup that errors (HTTP 5xx) counts as not served yet.\n2. `node scripts/release.mjs verify X.Y.Z --tag <dist-tag> --commit <release commit> --wait 10 --poll 30`, which waits only for the\n attestation bundle and the scratch install (a few more minutes), and then the smoke test against the registry\n (`release.mjs smoke X.Y.Z --registry`).\n\nFor all six packages `verify` 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. If the budget is spent, re-run the\nfailed jobs (`gh run rerun --failed`): `publish` is not repeated, because it succeeded. 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; after all six uploads each package gets one registry look (`--wait`, default 0, polls longer) and one not served yet is logged **pending**, never a failure; a staged result fails unless `--expect-staged` |\n| `await-registry <version> [--wait minutes] [--poll seconds] [--max-poll seconds]` | waits (default 60 min, backoff 15 s to 120 s, cache-busting) until the registry serves all six; fails only when the budget is spent |\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`. What a bump touches is the site's own\nbusiness, so each site owns one script, `pnpm opf:bump <X.Y.Z> [--opf <checkout>]` (`scripts/opf-bump.mjs` of the site), that sets its\n`@openpresentation/*` pins, refreshes its lockfile, regenerates every file it derives from the release (a release history from the\nregistry and these release notes, a pin mirror, preview fonts, chart previews, the vendored editor bundle, a homepage manifest) and runs\nthe checks of its own CI that read them; `scripts/bump-site.mjs` decides the label and calls it (a site without one gets only the\ngeneric pins and lockfile). A bump needs no human edit to be green; one that does (a release that changes behavior or removes a\ndocument the site links) fails the site's CI and stays open. 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- **`verify` fails at \"Wait for the registry\" with \"npm accepted X (exit 0) but the registry still does not serve it after 60\n min\"**, or `publish` fails with \"npm staged X\": npm returned success (or staged the upload) but the version is not installable.\n Ordinary registry lag never gets here: it is logged as pending in `publish` and waited out in `verify`. Open the package on\n npmjs.com, Staged Packages tab, or run `npm stage list <name>` as a maintainer: a staged version must be approved with 2FA\n (`npm stage approve <stage-id>`; trusted-publishing OIDC tokens cannot approve). Otherwise check npm's status page and, once\n the version is served, re-run the failed jobs of the run (`gh run rerun --failed`); the 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-
|
|
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-
|
|
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-ChmGA8Ul.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-ChmGA8Ul.js';
|
|
4
4
|
export { R as RuleExplanation, e as explain } from './explain-JSuESV-Y.js';
|
|
5
5
|
import { validate as validate$1 } from './validator.js';
|
|
6
6
|
export { DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, findValidationRule, validateCatalogRecord, validationRules } from './validator.js';
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var stats2 = (presentation2, options) => stats(presentation2, withDefaultCatalog
|
|
|
33
33
|
var paginate2 = (input, options) => paginate(input, withDefaultCatalog(options));
|
|
34
34
|
var embed2 = (document, options) => embed(document, withDefaultCatalog(options));
|
|
35
35
|
var edit2 = (deck, patch, options) => edit(deck, patch, withDefaultCatalog(options));
|
|
36
|
-
var engine = () => import('./conversion-
|
|
36
|
+
var engine = () => import('./conversion-IVKCKMCN.js');
|
|
37
37
|
var rethrow = (error, fallback) => {
|
|
38
38
|
if (error instanceof OPFValidationError) throw error;
|
|
39
39
|
throw asApiError(OPFApiError, error, fallback);
|
package/dist/node-engine.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { b as DeckReport, c as DeckReportOptions, r as readDeckReport } from './deck-report-5vwMGcQz.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-
|
|
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-
|
|
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-ChmGA8Ul.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-ChmGA8Ul.js';
|
|
4
4
|
import { a as Catalog } from './catalog-refs-BSfGELpt.js';
|
|
5
5
|
import { e as PresentationPaginationResult } from './pagination-D_kJe4jH.js';
|
|
6
6
|
import './presentation-Bwo8Ko6D.js';
|
package/dist/node-engine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { loadRenderer, listFontDirectories, leaseSharedFonts, prepareFonts } from './chunk-
|
|
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-
|
|
1
|
+
import { loadRenderer, listFontDirectories, leaseSharedFonts, prepareFonts } from './chunk-L52N5FZZ.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-L52N5FZZ.js';
|
|
3
3
|
import { defaultCatalogs } from './chunk-NVT2YDVC.js';
|
|
4
4
|
export { defaultCatalogs, galleryMismatch } from './chunk-NVT2YDVC.js';
|
|
5
5
|
import { OPFApiError } from './chunk-D25BNPTW.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpresentation/opf",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.10",
|
|
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",
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
"ajv-formats": "^3.0.1",
|
|
146
146
|
"jsonc-parser": "3.3.1",
|
|
147
147
|
"yaml": "^2.9.1",
|
|
148
|
-
"@openpresentation/gallery": "^0.26.
|
|
148
|
+
"@openpresentation/gallery": "^0.26.10"
|
|
149
149
|
},
|
|
150
150
|
"devDependencies": {
|
|
151
151
|
"@expo-google-fonts/arimo": "0.4.3",
|
|
@@ -165,8 +165,8 @@
|
|
|
165
165
|
"typescript": "~5.9.3"
|
|
166
166
|
},
|
|
167
167
|
"peerDependencies": {
|
|
168
|
-
"@openpresentation/opf-pptx": "^0.26.
|
|
169
|
-
"@openpresentation/opf-render": "^0.26.
|
|
168
|
+
"@openpresentation/opf-pptx": "^0.26.10",
|
|
169
|
+
"@openpresentation/opf-render": "^0.26.10"
|
|
170
170
|
},
|
|
171
171
|
"peerDependenciesMeta": {
|
|
172
172
|
"@openpresentation/opf-pptx": {
|
|
@@ -186,5 +186,5 @@
|
|
|
186
186
|
"test:packed": "node test/packed-install-smoke.mjs",
|
|
187
187
|
"pack:dry-run": "pnpm run build:deps && pnpm run build && npm pack --dry-run --cache /tmp/opf-npm-cache"
|
|
188
188
|
},
|
|
189
|
-
"gitHead": "
|
|
189
|
+
"gitHead": "da3f869f6f3a152a0d7d5cb42eeb2843d302a46a"
|
|
190
190
|
}
|