Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/core-data/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
20 changes: 8 additions & 12 deletions packages/core-data/src/sync.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 8 additions & 10 deletions packages/core-data/src/test/sync.jsdom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' ), () => ( {
Expand Down
4 changes: 4 additions & 0 deletions packages/dependency-extraction-webpack-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions packages/dependency-extraction-webpack-plugin/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
8 changes: 4 additions & 4 deletions packages/e2e-tests/mu-plugins/e2e-fast-sync-polling.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions packages/private-apis/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion packages/private-apis/src/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
5 changes: 5 additions & 0 deletions packages/sync/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)).
Expand Down
36 changes: 31 additions & 5 deletions packages/sync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

<https://github.com/yjs/yjs/issues/438>

Expand Down Expand Up @@ -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.
Expand Down
2 changes: 0 additions & 2 deletions packages/sync/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
54 changes: 45 additions & 9 deletions packages/sync/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
Expand Down Expand Up @@ -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.
Comment on lines -65 to -67

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the comment is still relevant, no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is covered in the docs, so not necessary to preserve here. 👍

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! I think the comments above this cover the migration, and most of the information in this paragraph is now out of date (wp-sync exposed, wp.sync.Y global removed in a future release).

*/
export * as Y from 'yjs';

Expand All @@ -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';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not wrong retrySyncConnection and createSyncManager are used by core-data and plugins not need them. In that case maybe it's better to reword the docblocks? e.g. for 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, updated both of those in 92d6e7d.


export type * from './types';
7 changes: 0 additions & 7 deletions packages/sync/src/lock-unlock.ts

This file was deleted.

24 changes: 0 additions & 24 deletions packages/sync/src/private-apis.ts

This file was deleted.

8 changes: 8 additions & 0 deletions packages/sync/src/providers/http-polling/polling-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions packages/sync/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
],
"references": [
{ "path": "../api-fetch/tsconfig.build.json" },
{ "path": "../hooks" },
{ "path": "../private-apis" }
{ "path": "../hooks" }
]
}
3 changes: 1 addition & 2 deletions packages/sync/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"references": [
{ "path": "./tsconfig.build.json" },
{ "path": "../api-fetch/tsconfig.build.json" },
{ "path": "../hooks" },
{ "path": "../private-apis" }
{ "path": "../hooks" }
]
}
Loading
Loading