diff --git a/package-lock.json b/package-lock.json index 48a02fae3c409f..3ee864120fc1bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44879,7 +44879,6 @@ "dependencies": { "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/hooks": "file:../hooks", - "@wordpress/private-apis": "file:../private-apis", "diff": "^8.0.3", "fast-deep-equal": "^3.1.3", "lib0": "^0.2.99", diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index 36bb572a67d80a..8916b2151ecfa5 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -15,6 +15,7 @@ ### Internal - Added an internal entity sync interface: `registerEntitySyncManager` and `getEntitySyncManager` let one manager plug into the entity lifecycle (record loads, edits, saves, deletes, and undo). The real-time collaboration experiment now runs through it instead of being called directly by the store. The interface is not exposed outside the package yet ([#83410](https://github.com/WordPress/gutenberg/pull/83410)). +- Import the sync manager, `Delta`, and the CRDT constants from `@wordpress/sync` directly instead of unlocking them from its removed `privateApis` export ([#81999](https://github.com/WordPress/gutenberg/pull/81999)). ## 8.1.0 (2026-09-23) diff --git a/packages/core-data/src/sync.ts b/packages/core-data/src/sync.ts index 6c8e26f17fcbac..197d1b93ca40c4 100644 --- a/packages/core-data/src/sync.ts +++ b/packages/core-data/src/sync.ts @@ -1,7 +1,14 @@ import fastDeepEqual from 'fast-deep-equal/es6/index.js'; import { dispatch, resolveSelect, select } from '@wordpress/data'; import { - privateApis as syncPrivateApis, + ConnectionErrorCode, + createSyncManager, + Delta, + CRDT_DOC_META_PERSISTENCE_KEY, + CRDT_RECORD_MAP_KEY, + LOCAL_EDITOR_ORIGIN, + LOCAL_UNDO_IGNORED_ORIGIN, + retrySyncConnection, type ConnectionStatus, type ObjectID, type SyncConfig, @@ -29,17 +36,6 @@ import { import { restoreSelection, getSelectionHistory } from './utils/crdt-selection'; import { saveCRDTDoc } from './utils/save-crdt-doc'; -const { - ConnectionErrorCode, - createSyncManager, - Delta, - CRDT_DOC_META_PERSISTENCE_KEY, - CRDT_RECORD_MAP_KEY, - LOCAL_EDITOR_ORIGIN, - LOCAL_UNDO_IGNORED_ORIGIN, - retrySyncConnection, -} = unlock( syncPrivateApis ); - export { ConnectionErrorCode, Delta, diff --git a/packages/core-data/src/test/sync.jsdom.test.js b/packages/core-data/src/test/sync.jsdom.test.js index d0f328e5b7ee45..e5531491f5e528 100644 --- a/packages/core-data/src/test/sync.jsdom.test.js +++ b/packages/core-data/src/test/sync.jsdom.test.js @@ -21,16 +21,14 @@ const { mockSyncManager, mockCreateSyncManager, mockSaveCRDTDoc } = vi.hoisted( ); vi.mock( import( '@wordpress/sync' ), () => ( { - privateApis: { - createSyncManager: mockCreateSyncManager, - ConnectionErrorCode: {}, - Delta: class {}, - CRDT_DOC_META_PERSISTENCE_KEY: 'crdt-doc-meta', - CRDT_RECORD_MAP_KEY: 'crdt-record', - LOCAL_EDITOR_ORIGIN: 'local-editor', - LOCAL_UNDO_IGNORED_ORIGIN: 'local-undo-ignored', - retrySyncConnection: vi.fn(), - }, + createSyncManager: mockCreateSyncManager, + ConnectionErrorCode: {}, + Delta: class {}, + CRDT_DOC_META_PERSISTENCE_KEY: 'crdt-doc-meta', + CRDT_RECORD_MAP_KEY: 'crdt-record', + LOCAL_EDITOR_ORIGIN: 'local-editor', + LOCAL_UNDO_IGNORED_ORIGIN: 'local-undo-ignored', + retrySyncConnection: vi.fn(), } ) ); vi.mock( import( '../lock-unlock' ), () => ( { diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index b82817ebadef96..88ab0e20eec0a3 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancements + +- Add `@wordpress/sync` as a bundled package, so consumer builds bundle it instead of externalizing it to the removed `wp-sync` script ([#81999](https://github.com/WordPress/gutenberg/pull/81999)). + ## 6.56.0 (2026-09-23) ### Enhancements diff --git a/packages/dependency-extraction-webpack-plugin/lib/util.js b/packages/dependency-extraction-webpack-plugin/lib/util.js index 5045e3489971e7..ce5c4b48ffd8bc 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/util.js +++ b/packages/dependency-extraction-webpack-plugin/lib/util.js @@ -12,6 +12,7 @@ const BUNDLED_PACKAGES = [ '@wordpress/kebab-case', '@wordpress/react-inert-value', '@wordpress/style-runtime', + '@wordpress/sync', '@wordpress/ui', '@wordpress/undo-manager', '@wordpress/views', diff --git a/packages/e2e-tests/mu-plugins/e2e-fast-sync-polling.php b/packages/e2e-tests/mu-plugins/e2e-fast-sync-polling.php index 50edf3e8395ec5..960b901b239860 100644 --- a/packages/e2e-tests/mu-plugins/e2e-fast-sync-polling.php +++ b/packages/e2e-tests/mu-plugins/e2e-fast-sync-polling.php @@ -2,15 +2,15 @@ /** * MU-plugin: Speed up collaboration sync polling in e2e tests. * - * The sync module reads its polling intervals via applyFilters at load time, - * so the filters must be registered before the wp-sync script executes. - * Hooked to admin_init so the wp-sync handle is already registered. + * The sync module reads its polling intervals via applyFilters at load time. + * The sync module is bundled into the wp-core-data script, so the filters + * must be registered before that script executes. */ add_action( 'enqueue_block_editor_assets', function () { wp_add_inline_script( - 'wp-sync', + 'wp-core-data', <<<'JS' wp.hooks.addFilter( 'sync.pollingManager.pollingInterval', diff --git a/packages/private-apis/CHANGELOG.md b/packages/private-apis/CHANGELOG.md index c3041273b985e0..a4c5f837daeefe 100644 --- a/packages/private-apis/CHANGELOG.md +++ b/packages/private-apis/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Internal + +- Keep `@wordpress/sync` in the list of core modules allowed to use private APIs for backward compatibility only. The package no longer uses private APIs, but copies published to npm before that change opt in at module load, so a plugin bundling one of those copies would throw at load time without this entry ([#81999](https://github.com/WordPress/gutenberg/pull/81999)). + ## 1.56.0 (2026-09-23) ## 1.55.0 (2026-09-10) diff --git a/packages/private-apis/src/implementation.ts b/packages/private-apis/src/implementation.ts index 2d37466b15ebb7..292a2b28caf5d7 100644 --- a/packages/private-apis/src/implementation.ts +++ b/packages/private-apis/src/implementation.ts @@ -39,12 +39,15 @@ const CORE_MODULES_USING_PRIVATE_APIS = [ '@wordpress/router', '@wordpress/routes', '@wordpress/storybook', - '@wordpress/sync', '@wordpress/theme', // Do not remove: older `@wordpress/dataviews` versions published to npm // call the opt-in at module load, so a plugin bundling one of those // copies throws at load time if this entry is missing. '@wordpress/dataviews', + // Do not remove: older `@wordpress/sync` versions published to npm call + // the opt-in at module load, so a plugin bundling one of those copies + // throws at load time if this entry is missing. + '@wordpress/sync', '@wordpress/fields', '@wordpress/lazy-editor', '@wordpress/media-editor', diff --git a/packages/sync/CHANGELOG.md b/packages/sync/CHANGELOG.md index 7c1e7862871055..02d6d75edb03ab 100644 --- a/packages/sync/CHANGELOG.md +++ b/packages/sync/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Breaking Changes + +- Stop registering the package as a WordPress script. `@wordpress/sync` is now bundled into its consumers, so the `wp-sync` script handle and the `wp.sync` global are no longer exposed ([#81999](https://github.com/WordPress/gutenberg/pull/81999)). +- Remove the `privateApis` export. `ConnectionErrorCode`, `createSyncManager`, `Delta`, `CRDT_DOC_META_PERSISTENCE_KEY`, `CRDT_RECORD_MAP_KEY`, `LOCAL_EDITOR_ORIGIN`, `LOCAL_UNDO_IGNORED_ORIGIN`, and `retrySyncConnection` are now regular exports, and the package no longer depends on `@wordpress/private-apis` ([#81999](https://github.com/WordPress/gutenberg/pull/81999)). + ### Bug Fixes - The sync manager's undo manager no longer stands in for the editor's undo manager. It is the undo history of the loaded entities only: it reports each level Yjs opens through the new `onUndoLevelOpened` record handler (replacing `onUndoStackChange`), and `undo()` and `redo()` take the entity whose level to move and return whether a level was moved. Each entity has its own Yjs undo manager, which replaces the copied `YMultiDocUndoManager`. `stopCapturing()` and `clearRedo()` let the consumer keep its own history in step. The manager exposes `isLoaded()`, and its undo manager exists from creation instead of from the first load. Together with the `core-data` change, undo and redo keep working for entities collaboration does not sync ([#83888](https://github.com/WordPress/gutenberg/pull/83888)). diff --git a/packages/sync/README.md b/packages/sync/README.md index 3926f00e0a7040..86363125429e99 100644 --- a/packages/sync/README.md +++ b/packages/sync/README.md @@ -24,15 +24,43 @@ The Awareness protocol should not be considered a public API. It is a third-part In general, awareness for core entity types is implemented by the `core-data` package and third-party Yjs providers should not provide their own awareness implementation. However, it may be desirable for custom entities to have a custom awareness implementation. -### privateApis +### ConnectionErrorCode -Private @wordpress/sync APIs. +Error codes reported by a sync provider when its connection fails. + +### CRDT_DOC_META_PERSISTENCE_KEY + +Key of the in-memory meta entry marking a CRDT document as loaded from persistence. It is not synced or persisted. + +### CRDT_RECORD_MAP_KEY + +Root-level key for the map that holds the entity record data. + +### createSyncManager + +Creates the sync manager, which orchestrates the lifecycle of syncing entity records: it creates Yjs documents, connects to providers, creates awareness instances, and coordinates with the `core-data` store. Exported for `@wordpress/core-data`; plugins don't need it. + +### Delta + +Quill Delta implementation used to describe rich text changes. + +### LOCAL_EDITOR_ORIGIN + +Origin string for CRDT document changes originating from the local editor. + +### LOCAL_UNDO_IGNORED_ORIGIN + +Origin string for CRDT document changes that should be synced but not recorded in the undo history (e.g. status changes during publish). + +### retrySyncConnection + +Retries the HTTP polling connection now instead of waiting for the next automatic retry. Exported for `@wordpress/core-data`; plugins don't need it. ### Y Yjs should not be considered a public API. It is a third-party library that _will_ experience breaking changes in the future. -Two Yjs instances operating on the same document cause silent data corruption: +`@wordpress/sync` is a bundled package: each consumer bundles its own copy, and no `wp.sync` global or `wp-sync` script handle is exposed by WordPress. Two Yjs instances operating on the same document cause silent data corruption: @@ -78,8 +106,6 @@ const createProvider = async ( { awareness, ydoc, Y } ) => { See `packages/e2e-tests/plugins/rtc-websocket-provider` for a complete working example. -Deprecated: `@wordpress/sync` is currently also exposed as the `wp-sync` WordPress script, which provides the same Yjs module as the `wp.sync.Y` global. This global will be removed in a future release, and it is already unavailable in WordPress core. Providers that still resolve `yjs` to `wp.sync.Y` through webpack externals should migrate to the `Y` option described above. - ### YJS_VERSION The major version of Yjs that is bundled and exported by this package. This can be used by third-party code to ensure that they are targeting a compatible version of Yjs. diff --git a/packages/sync/package.json b/packages/sync/package.json index baccd8c936eb01..04e49ac2deafe9 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -38,13 +38,11 @@ }, "./package.json": "./package.json" }, - "wpScript": true, "types": "build-types", "sideEffects": false, "dependencies": { "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/hooks": "file:../hooks", - "@wordpress/private-apis": "file:../private-apis", "diff": "^8.0.3", "fast-deep-equal": "^3.1.3", "lib0": "^0.2.99", diff --git a/packages/sync/src/index.ts b/packages/sync/src/index.ts index 76552fc08f2407..d7849f6a01458d 100644 --- a/packages/sync/src/index.ts +++ b/packages/sync/src/index.ts @@ -2,6 +2,8 @@ * Yjs should not be considered a public API. It is a third-party library that * _will_ experience breaking changes in the future. * + * `@wordpress/sync` is a bundled package: each consumer bundles its own copy, + * and no `wp.sync` global or `wp-sync` script handle is exposed by WordPress. * Two Yjs instances operating on the same document cause silent data * corruption: * @@ -58,13 +60,6 @@ * * See `packages/e2e-tests/plugins/rtc-websocket-provider` for a complete * working example. - * - * Deprecated: `@wordpress/sync` is currently also exposed as the `wp-sync` - * WordPress script, which provides the same Yjs module as the `wp.sync.Y` - * global. This global will be removed in a future release, and it is already - * unavailable in WordPress core. Providers that still resolve `yjs` to - * `wp.sync.Y` through webpack externals should migrate to the `Y` option - * described above. */ export * as Y from 'yjs'; @@ -87,8 +82,49 @@ export const YJS_VERSION = '13'; export { Awareness } from 'y-protocols/awareness'; /** - * Private @wordpress/sync APIs. + * Key of the in-memory meta entry marking a CRDT document as loaded from + * persistence. It is not synced or persisted. + */ +export { CRDT_DOC_META_PERSISTENCE_KEY } from './config'; + +/** + * Root-level key for the map that holds the entity record data. + */ +export { CRDT_RECORD_MAP_KEY } from './config'; + +/** + * Origin string for CRDT document changes originating from the local editor. + */ +export { LOCAL_EDITOR_ORIGIN } from './config'; + +/** + * Origin string for CRDT document changes that should be synced but not + * recorded in the undo history (e.g. status changes during publish). + */ +export { LOCAL_UNDO_IGNORED_ORIGIN } from './config'; + +/** + * Error codes reported by a sync provider when its connection fails. + */ +export { ConnectionErrorCode } from './errors'; + +/** + * Creates the sync manager, which orchestrates the lifecycle of syncing entity + * records: it creates Yjs documents, connects to providers, creates awareness + * instances, and coordinates with the `core-data` store. Exported for + * `@wordpress/core-data`; plugins don't need it. + */ +export { createSyncManager } from './manager'; + +/** + * Quill Delta implementation used to describe rich text changes. + */ +export { default as Delta } from './quill-delta/Delta'; + +/** + * Retries the HTTP polling connection now instead of waiting for the next + * automatic retry. Exported for `@wordpress/core-data`; plugins don't need it. */ -export { privateApis } from './private-apis'; +export { retrySyncConnection } from './providers/http-polling/polling-manager'; export type * from './types'; diff --git a/packages/sync/src/lock-unlock.ts b/packages/sync/src/lock-unlock.ts deleted file mode 100644 index cb3ac609eaae85..00000000000000 --- a/packages/sync/src/lock-unlock.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis'; - -export const { lock, unlock } = - __dangerousOptInToUnstableAPIsOnlyForCoreModules( - 'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.', - '@wordpress/sync' - ); diff --git a/packages/sync/src/private-apis.ts b/packages/sync/src/private-apis.ts deleted file mode 100644 index 9e6a457446ea91..00000000000000 --- a/packages/sync/src/private-apis.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - CRDT_DOC_META_PERSISTENCE_KEY, - CRDT_RECORD_MAP_KEY, - LOCAL_EDITOR_ORIGIN, - LOCAL_UNDO_IGNORED_ORIGIN, -} from './config'; -import { ConnectionErrorCode } from './errors'; -import { lock } from './lock-unlock'; -import { createSyncManager } from './manager'; -import { pollingManager } from './providers/http-polling/polling-manager'; -import { default as Delta } from './quill-delta/Delta'; - -export const privateApis = {}; - -lock( privateApis, { - ConnectionErrorCode, - createSyncManager, - Delta, - CRDT_DOC_META_PERSISTENCE_KEY, - CRDT_RECORD_MAP_KEY, - LOCAL_EDITOR_ORIGIN, - LOCAL_UNDO_IGNORED_ORIGIN, - retrySyncConnection: () => pollingManager.retryNow(), -} ); diff --git a/packages/sync/src/providers/http-polling/polling-manager.ts b/packages/sync/src/providers/http-polling/polling-manager.ts index 497721ec489c6a..072a16e5f99030 100644 --- a/packages/sync/src/providers/http-polling/polling-manager.ts +++ b/packages/sync/src/providers/http-polling/polling-manager.ts @@ -1114,6 +1114,14 @@ function retryNow(): void { } } +/** + * Immediately retries the sync connection instead of waiting for the next + * automatic retry. See `retryNow`. + */ +export function retrySyncConnection(): void { + pollingManager.retryNow(); +} + export const pollingManager: PollingManager = { registerRoom, retryNow, diff --git a/packages/sync/tsconfig.build.json b/packages/sync/tsconfig.build.json index 4181f49a509a7d..d1666601fc84e8 100644 --- a/packages/sync/tsconfig.build.json +++ b/packages/sync/tsconfig.build.json @@ -14,7 +14,6 @@ ], "references": [ { "path": "../api-fetch/tsconfig.build.json" }, - { "path": "../hooks" }, - { "path": "../private-apis" } + { "path": "../hooks" } ] } diff --git a/packages/sync/tsconfig.json b/packages/sync/tsconfig.json index 85d1956df7c720..c3c3dfa9157eba 100644 --- a/packages/sync/tsconfig.json +++ b/packages/sync/tsconfig.json @@ -7,7 +7,6 @@ "references": [ { "path": "./tsconfig.build.json" }, { "path": "../api-fetch/tsconfig.build.json" }, - { "path": "../hooks" }, - { "path": "../private-apis" } + { "path": "../hooks" } ] } diff --git a/tools/validation/check-private-apis.config.json b/tools/validation/check-private-apis.config.json index 6add480025282b..e6ee0d12437c2f 100644 --- a/tools/validation/check-private-apis.config.json +++ b/tools/validation/check-private-apis.config.json @@ -12,7 +12,7 @@ "via": "packages/ui/src/utils/theme-provider.ts" }, { - "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, sync, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", + "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", "package": "fields", "via": "packages/core-data/src/index.js" }, @@ -72,7 +72,7 @@ "via": "packages/global-styles-engine/src/index.ts" }, { - "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, sync, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", + "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", "package": "global-styles-ui", "via": "packages/core-data/src/index.js" }, @@ -97,7 +97,7 @@ "via": "packages/ui/src/utils/theme-provider.ts" }, { - "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, sync, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", + "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", "package": "media-editor", "via": "packages/core-data/src/index.js" }, @@ -117,17 +117,22 @@ "via": "packages/ui/src/utils/theme-provider.ts" }, { - "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, sync, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", + "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", "package": "media-fields", "via": "packages/core-data/src/index.js" }, + { + "comment": "Depends on @wordpress/api-fetch, whose internals unlock private APIs. Remove when api-fetch no longer does.", + "package": "sync", + "via": "packages/api-fetch/src/index.ts" + }, { "comment": "ThemeProvider falls back to the WP 7.2 theme/ui private APIs when the public API is unavailable. Remove when the fallback is deleted (scheduled for WP 7.3; see the TODO in the file).", "package": "ui", "via": "packages/ui/src/utils/theme-provider.ts" }, { - "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, sync, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", + "comment": "Depends on @wordpress/core-data, whose internals unlock private APIs across api-fetch, blocks, block-editor, and global-styles-engine. Remove when the core-data dependency is dropped or those internals stop using private APIs.", "package": "views", "via": "packages/core-data/src/index.js" },