Repository navigation
[lexical-y][lexical-playground][lexical-website] Feature: The binding's setup API from the layout 4 spec (section 11, with 11.8), beside the existing one - #9891
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
06d473e to
d9a76d3
Compare
d9a76d3 to
aad17e7
Compare
aad17e7 to
9c943ed
Compare
9c943ed to
74c111b
Compare
74c111b to
8f200d7
Compare
8f200d7 to
c778052
Compare
There was a problem hiding this comment.
Thanks for the layout 4 setup API: the new entry points, the canonical example and the tests read well. One problem breaks the package build. The other items below are suggestions.
Blocking
pnpm run buildfails onpackages/lexical-y/src/internal/Collab.ts. Sixinvariantmessages (lines 45, 50, 56, 61, 66 and 75) contain backticks, such asneeds `doc`. The build's error-code transform (scripts/error-codes/transform-error-messages.mjs) rewrites each message as a template literal, and Babel rejects a backtick in the raw text. Rollup then stops withCollab.ts: Invalid raw, which is what fails the integrity, e2e canary and publish jobs on this PR. Unit tests and tsc read the sources without that transform, so they pass. To fix it, remove the backticks from these messages (or use quotes) and update the matching regexes inPublicApi.test.ts.
Suggestions
- The changesets disagree.
.changeset/lexical-y-public-api.mdsays "The existingYBindingandcreateYCollabConfigkeep working", but the second commit removescreateYCollabConfigand five related exports, andlexical-y-public-api-breaking.mdrecords the removal.@lexical/yhas not been in a stable release yet (its "Add@lexical/y" changeset is still pending). As a result, the pendinglexical-y-layout-upgrade.mdandlexical-y-collab-input.mdwould announcecreateYCollabConfigandYCollabInputas features in the same release that removes them. If the removal ships with this PR, consider editing those pending entries instead of adding a[breaking]entry for APIs no release included (format in.changeset/README.md). Otherwise, split the second commit out as the description plans. - Docs in
packages/lexical-website/docs/collaboration/y.md: line 422 tellsnew YBinding(...)users to callcollab.markSynced(), but that path has nocollab; the call is stillbinding.initialize(). The full@y/websocketexample callsshowStatus(status)at line 214 without defining it, so it does not compile when copied. - Coverage: the tests call
describeUnsyncedEditsonly with{state}. The documented call passesfreeze()'s result, which takes thejsonbranch, so one case withedits: binding.freeze()would cover it. - Nits:
CollabBinding.ts:43still sayscreateYCollabConfigkeeps working.yCollabandcreateYBindingalways declare a server layout upgrade (Collab.ts:106,:177), so for an app without a migrating server (such as the playground relay), the below-floor warning says its server should have migrated the room.
c778052 to
0af3f2d
Compare
2312e74 to
f6f92f2
Compare
There was a problem hiding this comment.
Thanks for the layout 4 setup API: requiring synced and onStatus, the typed read-only cause and the shared canonical example all make the binding harder to misuse. One problem breaks the package build; the rest are suggestions.
Blocking
pnpm run buildfails onpackages/lexical-y/src/internal/Collab.ts. Sixinvariantmessages (lines 45, 50, 56, 61, 66 and 75) contain backticks, such asneeds `doc`. The build's error-code transform (scripts/error-codes/transform-error-messages.mjs, lines 127-129) turns everyinvariantmessage into a template literal, and Babel rejects an unescaped backtick in the raw text, so the build stops withCollab.ts: Invalid raw. The same command passes on the base branch. Unit tests,tscand Flow read the sources without that transform, which is why the test plan is green. To fix it, remove the backticks from these messages (or use quotes) and update the five matching regexes inPublicApi.test.ts(lines 351-362).
Suggestions
status.causereports the second cause when two arrive together (packages/lexical-y/src/internal/Binding.ts:3793-3794).warn()keeps the last read-only warning, butevaluate()warns the newer schema first on purpose (its comment at line 3023 says a newer version's content explains the warnings that follow). When aschemaVersion: 2peer has also changed how a node is stored, aschemaVersion: 1client getsunsupported-schemaand thenstorage-mismatch, andcollab.statusandonStatusreportstorage-mismatchinstead ofschema-newer. An app would then tell its user the node class changed rather than "reload to update". Keeping the first cause since the binding was last active, plus a test that combines two causes, would fix it.- The changesets disagree.
.changeset/lexical-y-public-api.mdsays "The existingYBindingandcreateYCollabConfigkeep working", but the second commit removescreateYCollabConfigand five related exports, andlexical-y-public-api-breaking.mdrecords that removal.@lexical/yhas not been in a stable release yet (its "Add@lexical/y" changeset is still pending), so the pendinglexical-y-layout-upgrade.mdandlexical-y-collab-input.mdwould announcecreateYCollabConfigandYCollabInputas features in the same release that removes them. If the removal ships with this PR, consider editing those pending entries instead of adding a[breaking]entry (format in.changeset/README.md); otherwise split the second commit out, as its message plans. - Docs in
packages/lexical-website/docs/collaboration/y.md: line 421 tellsnew YBinding(...)users to callcollab.markSynced(), but that path has nocollab; the call there is stillbinding.initialize(). The@y/websocketexample callsshowStatus(status)at line 214 without defining it (only the Start here example defines it), so it does not compile when copied on its own. - Coverage: the tests call
describeUnsyncedEditsonly with{state}. The documented call passesfreeze()'s result, which takes thejsonbranch; one case withedits: binding.freeze()would cover it. AGENTS.md also asks the Test plan for### Beforeand### Afteroutput. - Nits:
CollabBinding.ts:43still sayscreateYCollabConfigkeeps working.yCollabandcreateYBindingalways declare a server layout upgrade (Collab.ts:106,:177), so an app without a migrating server (such as the playground relay) has no equivalent oflayoutUpgrade: null, and its below-floor warning says the server should have migrated the room.packages/lexical-y/AGENTS.mdasks for a DECISIONS.md row when a decision like this changes.
0af3f2d to
9add8b4
Compare
There was a problem hiding this comment.
Thanks for the layout 4 setup API. Requiring synced and onStatus, the typed read-only cause and the shared canonical example all make the binding harder to misuse. This revision rebases the same two commits, so the points below carry over; the createYBinding undo point and the stale warning text are new. One problem breaks the package build; the rest are suggestions.
Blocking
pnpm run buildfails onpackages/lexical-y/src/internal/Collab.ts. Sixinvariantmessages (lines 45, 50, 56, 61, 66 and 75) contain backticks, such asneeds `doc`. The build's error-code transform (scripts/error-codes/transform-error-messages.mjs, lines 127-129) turns everyinvariantmessage into a template literal, and Babel rejects an unescaped backtick in the raw text, so the build stops withCollab.ts: Invalid raw. That is the failure in the integrity, e2e canary and publish jobs. The same command passes on the base branch, and it passes on this head once those backticks are removed. Unit tests,tscand Flow read the sources without that transform, which is why the test plan is green.packages/lexical-y/AGENTS.mdasks for required checks to be green before merging. To fix it, drop the backticks from these messages (or use quotes) and update the five matching regexes inPublicApi.test.ts(lines 351-362).
Suggestions (non-blocking)
status.causereports the second cause when two arrive together (packages/lexical-y/src/internal/Binding.ts:3682-3684).warn()keeps the last read-only warning, butevaluate()checks the schema first on purpose (line 2912: a newer version's content explains the warnings that follow). Suppose aschemaVersion: 2peer has also changed how a node type is stored. AschemaVersion: 1client then queuesunsupported-schemaand thenstorage-mismatch, andcollab.statusandonStatusreportstorage-mismatchinstead ofschema-newer, so the app tells its user the node class changed rather than "reload to update". A below-floor document opened throughconfigExtension(YCollabExtension, ...)withoutlayoutUpgradelikewise reportsunsupported-docinstead oflayout-older. Keeping the first cause since the binding was last active, plus a test that combines two causes, would fix both. The playground's own notice already keeps the first, newer-app warning (needsNewerApp).createYBindingadvertises undo it does not handle (Collab.ts:130-151). It dispatchesCAN_UNDO_COMMAND/CAN_REDO_COMMANDthe wayYCollabExtensiondoes, but unlike the extension (YExtension.ts:293-310) it registers noUNDO_COMMAND/REDO_COMMANDhandler. After an edit,CAN_UNDOistrue, but Ctrl/Cmd+Z and an undo button do nothing until the app wires them tobinding.history. Registering both commands (removed indispose), or saying in the JSDoc and in Start here that the app routes them, would close the gap.- The changesets disagree.
.changeset/lexical-y-public-api.mdsays "The existingYBindingandcreateYCollabConfigkeep working", but the second commit removescreateYCollabConfigand five related exports, andlexical-y-public-api-breaking.mdrecords that removal.@lexical/yhas not been in a stable release yet (its "Add@lexical/y" changeset is still pending), so the pendinglexical-y-layout-upgrade.mdandlexical-y-collab-input.mdwould announcecreateYCollabConfigandYCollabInputas features in the same release that removes them. If the removal ships with this PR, consider editing those pending entries instead of adding a[breaking]entry (format in.changeset/README.md); otherwise split the second commit out, as its message plans. - Guidance still names the removed export. The
'missing-layout-upgrade'warning atBinding.ts:2988tells developers to "create the config with createYCollabConfig and pass layoutUpgrade". It still fires forconfigExtension(YCollabExtension, ...)andnew YBinding(...)withoutlayoutUpgrade, yet neither name is importable from@lexical/yany more.y.mdline 504 points that reader to Layout upgrades, which no longer describeslayoutUpgrade. The JSDoc atBinding.ts:821-828,YExtension.ts:126-128andCollabBinding.ts:41-43namescreateYCollabConfigthe same way. - Docs in
packages/lexical-website/docs/collaboration/y.md: line 421 tellsnew YBinding(...)users to callcollab.markSynced(), but that path has nocollab; the call there is stillbinding.initialize(). The@y/websocketexample callsshowStatus(status)at line 214 without defining it (only the Start here example defines it), so it does not compile when copied on its own. - Coverage: the tests call
describeUnsyncedEditsonly with{state}. The documented call passesfreeze()'s result, which takes thejsonbranch; one case withedits: binding.freeze()would cover it. AGENTS.md also asks the Test plan for### Beforeand### Afteroutput. - Nits:
yCollabandcreateYBindingalways declare a server layout upgrade (Collab.ts:106,:177). An app without a migrating server, such as the playground relay, therefore has no equivalent oflayoutUpgrade: null, and its below-floor warning says the server should have migrated the room.packages/lexical-y/AGENTS.mdasks for a DECISIONS.md row when a recorded decision like this changes.flow/LexicalYServer.js.flow:263-264still says those types are "imported and re-exported, never restated", right above the declarations this PR restates.
9add8b4 to
f4cf065
Compare
lexical
@lexical/a11y
@lexical/clipboard
@lexical/code
@lexical/code-core
@lexical/code-prism
@lexical/code-shiki
@lexical/compiler
@lexical/devtools-core
@lexical/dragon
@lexical/eslint-plugin
@lexical/extension
@lexical/fast-check
@lexical/file
@lexical/hashtag
@lexical/headless
@lexical/history
@lexical/html
@lexical/internal
@lexical/link
@lexical/list
@lexical/mark
@lexical/markdown
@lexical/mdast
@lexical/offset
@lexical/overflow
@lexical/plain-text
@lexical/react
@lexical/rich-text
@lexical/selection
@lexical/table
@lexical/tailwind
@lexical/text
@lexical/utils
@lexical/y
@lexical/yjs
commit: |
f4cf065 to
84e3e09
Compare
There was a problem hiding this comment.
Thanks, this looks good. The new yCollab / createYBinding entry points are additive, the existing YBinding and createYCollabConfig keep working, and the earlier points (backticks in the invariant messages, the first read-only cause, undo commands in createYBinding, the changeset wording) are addressed at this head. Everything below is optional and non-blocking.
- No way to say "no migrating server".
packages/lexical-y/src/internal/Collab.ts:110and:202always passcreateYServerLayoutUpgrade().createYCollabConfigmade this an explicit choice (nullor a handle), whichpackages/lexical-y/DECISIONS.mdD03 asks for (choices are explicit). An app without a migrating server, like the playground (see the comment atpackages/lexical-playground/src/collaborationY.tsx:184), now gets an'older'warning saying its server upgrades such documents. An optionallayoutUpgradeonYCollabOptions, defaulting to the server handle, would keep the choice expressible. hasLocalEditsand a cached state applied after binding.packages/lexical-y/src/internal/CollabBinding.ts:416counts state present when the binding is made, plus this client's writes. A stored update applied later (for example by an asynchronous persistence provider) is a remote transaction, sohandshake()reportsmaterial: false, and with a stale generationcheckYSessionanswersbootstrapinstead ofstale.createYDoc(stored)avoids this. A sentence in thehandshake/YCollabOptions.docJSDoc saying that the cached state must be in the document before binding would make it explicit.pendinguntilmarkSynced(). The JSDoc onYStatus,YCollabOptions.synced(CollabBinding.ts:188) andcreateYBindingsays so, but a document that holds content, or that receives the provider's state first, isactivebeforemarkSynced(), as the description notes. Worth saying in the JSDoc.collab.dispose()on an editor built from extensions (nit). It disposes the binding, butYCollabExtension'sUNDO_COMMAND/REDO_COMMANDhandlers stay registered and keep returningtrue, andcollab.statusstill readsactive. Either document that such editors are disposed witheditor.dispose(), or havedispose()defer to it.- DECISIONS.md (nit).
packages/lexical-y/AGENTS.mdasks a pull request that makes new consequential decisions to add their rows. TheYCollabBindingname, thehasLocalEditsdefinition and the built-in server layout upgrade look like candidates.
The collab Y end-to-end specs did not run for this head (the extended matrix was skipped). Since the playground's collab Y setup changed, running pnpm run test-e2e-collab-y-chromium against pnpm run start:y would be a useful extra check.
| return configExtension(YCollabExtension, { | ||
| captureTimeout: history === false ? null : history.captureTimeout, | ||
| doc: options.doc, | ||
| layoutUpgrade: createYServerLayoutUpgrade(), |
There was a problem hiding this comment.
Optional: yCollab (and createYBinding at line 110) always declares a migrating server, and YCollabOptions has no way to say there is none, which createYCollabConfig made an explicit null (packages/lexical-y/DECISIONS.md D03: choices are explicit). A room below the floor is read only either way, but the 'older' warning then says the app's server upgrades such documents. The playground, whose relay does not migrate, now does this (see the comment at packages/lexical-playground/src/collaborationY.tsx:184). An optional layoutUpgrade on YCollabOptions, defaulting to this handle, would keep the choice expressible.
| ); | ||
| return createYHandshake({ | ||
| generation, | ||
| hasLocalEdits: this.hadState || this.wroteLocally, |
There was a problem hiding this comment.
Optional: this counts state present at bind plus this client's writes. A stored update applied after the binding is made (for example by an asynchronous persistence provider) is a remote transaction, so it counts as neither, and handshake(null) reports material: false for a document that holds a cached edit. With a stale generation, checkYSession then answers bootstrap (Server.ts:479-488) and the edit is never handed off. createYDoc(stored) avoids this; a sentence in the handshake / YCollabOptions.doc JSDoc saying the cached state must be in the document before binding would make it explicit.
| * Whether the document already holds what the server has (headless, tests, | ||
| * a document loaded from storage): `markSynced()` runs at bind. With | ||
| * `false`, call `binding.markSynced()` after the provider's first sync; | ||
| * until then the editor is not editable. |
There was a problem hiding this comment.
Optional: true for an empty document only. A document that holds content when bound, or receives the provider's state before markSynced(), is read at once and is active without markSynced() (as the PR description notes). The same sentence is in YStatus (line 147) and createYBinding's JSDoc (Collab.ts:94-95).
| if (this.stop !== null) { | ||
| this.stop(); | ||
| } else { | ||
| this.binding.dispose(); |
There was a problem hiding this comment.
Nit: for the extension's collab this disposes the binding, but YCollabExtension.register keeps its UNDO_COMMAND/REDO_COMMAND handlers, which keep returning true (and collab.status still reads active). Consider documenting that an editor built from extensions is disposed with editor.dispose(), or deferring to it here.
…'s setup API from the layout 4 spec (section 11, with 11.8), beside the existing one
## Description
The layout 4 spec's public API, added as new exports while the existing `YBinding`, `createYCollabConfig` and their options keep working unchanged:
- `createYBinding(editor, options)` (an editor built without extensions) and `yCollab(options)` (YCollabExtension configured for `dependencies`, whose output gains `collab`) take `YCollabOptions`: `doc`, `schemaVersion`, and the two choices an app must make, **`synced`** and **`onStatus`**, required; `root` and `history` optional with their defaults in the exported **`Y_DEFAULTS`**.
- Both return a `YCollabBinding`: `status` (`pending`, `active`, or `readonly` with a `YReadOnlyCause`: `layout-older`, `layout-newer`, `schema-newer`, `requires-feature`, `malformed-record`, `unknown-node-types`, `storage-mismatch`, `unsupported-doc`, `unreadable-content`, `unstorable-change`, `frozen`), `markSynced()`, **`handshake(generation)`** (fills `hasLocalEdits` and this release's layouts and features), `history` (`YHistory`), `getCursor()`, `freeze()` (stops reading and writing and returns `YLocalEdits`: `state`, `json`, `text`, `differs`), `dispose()`.
- A generation token type, `YGeneration`, read with `parseYGeneration`, and `createYHandshake` for servers and tests.
- `describeUnsyncedEdits` on `@lexical/y/headless` (11.6): block-by-block report of a frozen client's edits against their base and the current document.
- Errors that name the fix: a missing `synced`, `onStatus`, `doc` or `schemaVersion`, a `root` or `history` of the wrong shape, or a string that is not a generation, throws with the call to make.
- A "start here" JSDoc on both entry points, and one canonical setup example used identically in the package README, y.md (both now open with the same five steps) and the playground; a test keeps the three identical.
Review fixes in this revision: the error messages carry no backticks (the build's error-code transform rejects them); when two read-only causes arrive together, `status.cause` keeps the first (a newer schema before what its content does); `createYBinding` handles `UNDO_COMMAND` and `REDO_COMMAND` as the extension does; the binding notes this client's writes after each transaction rather than through an `update` listener, so a document with no provider is not made to encode updates, and the extension starts listening only when it starts the binding. The removal of the earlier entry points (`createYCollabConfig`, `YCollabInput`, `createYServerLayoutUpgrade` from the main entry, `YClientSession`, ...) is a separate draft pull request, held until apps have moved to this API.
## Test plan
### Before
```
$ pnpm exec vitest run --project unit packages/lexical-y/src/__tests__/unit/PublicApi.test.ts # this PR's test on main
FAIL |unit| packages/lexical-y/src/__tests__/unit/PublicApi.test.ts > createYBinding > starts pending, not editable, until markSynced; a second markSynced does nothing
TypeError: createYBinding is not a function
Test Files 1 failed (1)
Tests 28 failed (28)
```
### After
```
$ pnpm exec vitest run --project unit packages/lexical-y/src/__tests__/unit/PublicApi.test.ts
Tests 29 passed (29)
$ pnpm exec vitest run --project unit packages/lexical-y packages/lexical-playground
Tests 25031 passed | 375 expected fail | 154 skipped (25560)
$ pnpm exec vitest run --project scripts-unit
Tests 3676 passed (3676)
$ pnpm run flow
Found 0 errors
$ pnpm exec tsc -p tsconfig.json --noEmit # 0 errors
$ pnpm run build # passes (and build-release)
```
84e3e09 to
8920efb
Compare
There was a problem hiding this comment.
This looks good: yCollab and createYBinding are added beside YBinding and createYCollabConfig without changing them, the types match in TypeScript and Flow, and the new tests cover the setup errors, status causes, handshake, history and freeze(). Thanks for addressing the earlier points (backticks in the invariant messages, the first read-only cause winning, undo/redo in createYBinding, and keeping createYCollabConfig while the removal waits in its own pull request). Everything below is optional and non-blocking.
Optional suggestions
hasLocalEditsand a cache loaded after binding (packages/lexical-y/src/internal/CollabBinding.ts:415).handshake()counts state present at bind plus this client's own writes. A persistence provider that loads its cache asynchronously applies it as a remote transaction after the binding exists, sohandshake(...)reportsmaterial: falsefor a document that holds a cached edit. With a stale generation,checkYSessionthen answersbootstraprather thanstale(Server.ts:479-488), and the cached edit is not handed off, althoughcreateYHandshakedocuments the flag as covering "a cached state the server lacks". A sentence in thehandshakeandYCollabOptions.docJSDoc, and in Start here step 1, saying a cache must be loaded withcreateYDoc(stored)before binding would keep apps on the safe path.layoutUpgrade: nullcannot be expressed (packages/lexical-y/src/internal/Collab.ts:110,:202). Both entry points always declare a server layout upgrade, whilepackages/lexical-y/DECISIONS.mdD03 asks for explicit choices that may benull. A below-floor room is read only either way, but an app on a plain relay gets a warning saying its server upgrades such documents. An optionallayoutUpgradeonYCollabOptions, defaulting to the handle, would keep the choice. The playground can keep its own today by addinglayoutUpgrade: nullto its existingconfigExtension(YCollabExtension, {selfCheck})call (inline suggestion), which also makes the comment above it accurate. Start here step 3 could say the handshake is for servers that check sessions withcheckYSession, since a plain relay has nowhere to receive it.- Catalog coverage.
packages/lexical-y/src/__tests__/catalog/README.mdasks that a new public export or option land with its catalog entry and a matrix test inunit/matrix/, andpackages/lexical-y/AGENTS.mdsays to test the input space. AddingcreateYBindingandyCollabas bind modes incatalog/bind.ts(and toboundincatalog/configs.ts) would run the existing matrix contracts through them. - A DECISIONS.md row.
packages/lexical-y/AGENTS.mdasks a pull request that makes a new consequential decision to add its row in the same pull request. The description's "Where the spec is underspecified" list (theYCollabBindingname, what counts ashasLocalEdits, documents with content going active beforemarkSynced(), the always-declared layout upgrade) would fit there.
Nits
YStatus's andcreateYBinding's JSDoc say the binding ispendinguntilmarkSynced(); that holds for an empty document only, as the description notes.collab.dispose()on the extension's output disposes the binding but leavesYCollabExtension's undo/redo handlers returningtrueandcollab.statusreadingactive; the JSDoc could say that an editor built from extensions is disposed witheditor.dispose().- Without a
base,describeUnsyncedEditsreports a block a collaborator added since asdeleted(inline comment onpackages/lexical-y/src/headless.ts). - The cause-precedence test in
PublicApi.test.tsreachesYBinding's privatewarn; a document that brings both causes would also pin the order in which they are emitted.
| }); | ||
| } | ||
| } | ||
| for (const [id, was] of againstById) { |
There was a problem hiding this comment.
Nit: without a base, against is the current document (line 498), so a block a collaborator added after the user went stale is reported here as change: 'deleted' with yours: null (with edits [b1] and current [b1, b9], the report holds ['b9', 'deleted']). A recovery view would list it as the user's deletion. Skipping deletions when base is null, or reporting them with a separate change value, would avoid that.
| // job tests, has NODE_ENV production and DEV false. The | ||
| // playground's relay does not migrate stored documents: one below | ||
| // the floor stays read only. | ||
| configExtension(YCollabExtension, { |
There was a problem hiding this comment.
Optional: yCollab above declares a server layout upgrade, so a below-floor room here now logs that the app's server upgrades such documents, while the comment above says the playground's relay does not migrate. Passing layoutUpgrade: null here keeps the playground's explicit choice (a later configExtension overrides the value yCollab set) without changing the canonical yCollab({...}) call that CanonicalExample.test.ts compares:
| configExtension(YCollabExtension, { | |
| configExtension(YCollabExtension, { | |
| layoutUpgrade: null, |
Description
The layout 4 spec's public API, added as new exports while the existing
YBinding,createYCollabConfigand their options keep working unchanged:createYBinding(editor, options)(an editor built without extensions) andyCollab(options)(YCollabExtension configured fordependencies, whose output gainscollab) takeYCollabOptions:doc,schemaVersion, and the two choices an app must make,syncedandonStatus, required;rootandhistoryoptional with their defaults in the exportedY_DEFAULTS.YCollabBinding:status(pending,active, orreadonlywith aYReadOnlyCause:layout-older,layout-newer,schema-newer,requires-feature,malformed-record,unknown-node-types,storage-mismatch,unsupported-doc,unreadable-content,unstorable-change,frozen),markSynced(),handshake(generation)(fillshasLocalEditsand this release's layouts and features),history(YHistory),getCursor(),freeze()(stops reading and writing and returnsYLocalEdits:state,json,text,differs),dispose().YGeneration, read withparseYGeneration, andcreateYHandshakefor servers and tests.describeUnsyncedEditson@lexical/y/headless(11.6): block-by-block report of a frozen client's edits against their base and the current document.synced,onStatus,docorschemaVersion, arootorhistoryof the wrong shape, or a string that is not a generation, throws with the call to make.Review fixes in this revision: the error messages carry no backticks (the build's error-code transform rejects them); when two read-only causes arrive together,
status.causekeeps the first (a newer schema before what its content does);createYBindinghandlesUNDO_COMMANDandREDO_COMMANDas the extension does; the binding notes this client's writes after each transaction rather than through anupdatelistener, so a document with no provider is not made to encode updates, and the extension starts listening only when it starts the binding. The removal of the earlier entry points (createYCollabConfig,YCollabInput,createYServerLayoutUpgradefrom the main entry,YClientSession, ...) is a separate draft pull request, held until apps have moved to this API.Test plan
Before
After
Where the spec is underspecified, and what this does
YBinding, which is today's class; here it isYCollabBinding(bindingexposes the class underneath).hasLocalEdits: true when the document held state when the binding was made (pending structs included), or this client wrote to it since (a local transaction, or the binding's own writes such as the seed of an empty document).unsupported-doc.markSynced()holds for an empty document. A document that already holds content when the binding starts (a cached state) is read at once and isactivebeforemarkSynced(), asYBindingdoes today; spec 11.1 has it pending, which needs a change in the binding itself.freeze()returns fresh copies on every call, reportsfrozenonce Yjs is idle, and takes itstextfrom the draft JSON (falling back to the editor's text where a node class cannot read it back).parseYGenerationchecks only a generation's form ('unknown', org1:and a non-empty rest): generations stay opaque.layout-older; an app without a migrating server has nonullequivalent yet.describeUnsyncedEditsleaves migration of a below-floor input to the caller (spec 11.6 migrates on a scratch copy); a reordering that changes no block is not reported.@lexical/y/debug(enableYSelfCheck,validateYState), the closedYWarningunion andcheckYSchema, and a development-build status report of the effective settings.