@blocksuite/store
Advanced tools
Comparing version 0.3.0-alpha.8 to 0.3.0-alpha.9
@@ -12,2 +12,3 @@ import type { Page } from './workspace'; | ||
export declare class BaseBlockModel implements IBaseBlockProps { | ||
static version: [number, number]; | ||
page: Page; | ||
@@ -14,0 +15,0 @@ propsUpdated: Signal<void>; |
import type * as Y from 'yjs'; | ||
import { Awareness } from 'y-protocols/awareness.js'; | ||
import { AwarenessAdapter, SelectionRange } from './awareness'; | ||
import type { BaseBlockModel } from './base'; | ||
import type { RichTextAdapter } from './text-adapter'; | ||
@@ -10,3 +9,3 @@ export interface StackItem { | ||
} | ||
export declare class Space<IBlockSchema extends Record<string, typeof BaseBlockModel> = any> { | ||
export declare class Space { | ||
readonly id: string; | ||
@@ -13,0 +12,0 @@ readonly doc: Y.Doc; |
@@ -20,3 +20,3 @@ import type { Space } from './space'; | ||
readonly providers: DocProvider[]; | ||
readonly spaces: Map<string, Space<any>>; | ||
readonly spaces: Map<string, Space>; | ||
readonly awareness: Awareness; | ||
@@ -23,0 +23,0 @@ readonly idGenerator: IdGenerator; |
@@ -30,2 +30,3 @@ import * as Y from 'yjs'; | ||
private _splitSet; | ||
private _synced; | ||
private _ignoredKeys; | ||
@@ -72,2 +73,3 @@ readonly signals: { | ||
dispose(): void; | ||
private _initYBlocks; | ||
private _getYBlock; | ||
@@ -83,3 +85,4 @@ private _historyAddObserver; | ||
private _handleYEvents; | ||
private _handleVersion; | ||
} | ||
//# sourceMappingURL=page.d.ts.map |
@@ -18,2 +18,3 @@ import * as Y from 'yjs'; | ||
this._splitSet = new Set(); | ||
this._synced = false; | ||
// TODO use schema | ||
@@ -56,19 +57,2 @@ this._ignoredKeys = new Set(Object.keys(new BaseBlockModel(this, {}))); | ||
this._idGenerator = idGenerator; | ||
const { _yBlocks } = this; | ||
// Consider if we need to expose the ability to temporarily unobserve this._yBlocks. | ||
// "unobserve" is potentially necessary to make sure we don't create | ||
// an infinite loop when sync to remote then back to client. | ||
// `action(a) -> YDoc' -> YEvents(a) -> YRemoteDoc' -> YEvents(a) -> YDoc'' -> ...` | ||
// We could unobserve in order to short circuit by ignoring the sync of remote | ||
// events we actually generated locally. | ||
// _yBlocks.unobserveDeep(this._handleYEvents); | ||
_yBlocks.observeDeep(this._handleYEvents); | ||
this._history = new Y.UndoManager([_yBlocks], { | ||
trackedOrigins: new Set([this.doc.clientID]), | ||
doc: this.doc, | ||
}); | ||
this._history.on('stack-cleared', this._historyObserver); | ||
this._history.on('stack-item-added', this._historyAddObserver); | ||
this._history.on('stack-item-popped', this._historyPopObserver); | ||
this._history.on('stack-item-updated', this._historyObserver); | ||
} | ||
@@ -251,2 +235,7 @@ get blobs() { | ||
syncFromExistingDoc() { | ||
if (this._synced) { | ||
throw new Error('Cannot sync from existing doc more than once'); | ||
} | ||
this._handleVersion(); | ||
this._initYBlocks(); | ||
const visited = new Set(); | ||
@@ -259,2 +248,3 @@ this._yBlocks.forEach((_, id) => { | ||
}); | ||
this._synced = true; | ||
} | ||
@@ -271,2 +261,21 @@ dispose() { | ||
} | ||
_initYBlocks() { | ||
const { _yBlocks } = this; | ||
// Consider if we need to expose the ability to temporarily unobserve this._yBlocks. | ||
// "unobserve" is potentially necessary to make sure we don't create | ||
// an infinite loop when sync to remote then back to client. | ||
// `action(a) -> YDoc' -> YEvents(a) -> YRemoteDoc' -> YEvents(a) -> YDoc'' -> ...` | ||
// We could unobserve in order to short circuit by ignoring the sync of remote | ||
// events we actually generated locally. | ||
// _yBlocks.unobserveDeep(this._handleYEvents); | ||
_yBlocks.observeDeep(this._handleYEvents); | ||
this._history = new Y.UndoManager([_yBlocks], { | ||
trackedOrigins: new Set([this.doc.clientID]), | ||
doc: this.doc, | ||
}); | ||
this._history.on('stack-cleared', this._historyObserver); | ||
this._history.on('stack-item-added', this._historyAddObserver); | ||
this._history.on('stack-item-popped', this._historyPopObserver); | ||
this._history.on('stack-item-updated', this._historyObserver); | ||
} | ||
_getYBlock(id) { | ||
@@ -413,3 +422,13 @@ const yBlock = this._yBlocks.get(id); | ||
} | ||
_handleVersion() { | ||
// Initialization from empty yDoc, indicating that the document is new. | ||
if (this._yBlocks.size === 0) { | ||
this.workspace.meta.writeVersion(); | ||
} | ||
// Initialization from existing yDoc, indicating that the document is loaded from storage. | ||
else { | ||
this.workspace.meta.validateVersion(); | ||
} | ||
} | ||
} | ||
//# sourceMappingURL=page.js.map |
@@ -17,9 +17,11 @@ import * as Y from 'yjs'; | ||
declare class WorkspaceMeta extends Space { | ||
_prevPages: Set<string>; | ||
private _workspace; | ||
private _prevPages; | ||
pageAdded: Signal<string>; | ||
pageRemoved: Signal<string>; | ||
pagesUpdated: Signal<void>; | ||
constructor(id: string, doc: Y.Doc, awareness: Awareness); | ||
constructor(id: string, workspace: Workspace, awareness: Awareness); | ||
private get _yMetaRoot(); | ||
private get _yPages(); | ||
private get _yVersions(); | ||
get pageMetas(): PageMeta[]; | ||
@@ -29,2 +31,10 @@ addPage(page: PageMeta, index?: number): void; | ||
removePage(id: string): void; | ||
/** | ||
* @internal Only for page initialization | ||
*/ | ||
writeVersion(): void; | ||
/** | ||
* @internal Only for page initialization | ||
*/ | ||
validateVersion(): void; | ||
private _handlePageEvent; | ||
@@ -31,0 +41,0 @@ } |
@@ -9,4 +9,4 @@ import * as Y from 'yjs'; | ||
class WorkspaceMeta extends Space { | ||
constructor(id, doc, awareness) { | ||
super(id, doc, awareness); | ||
constructor(id, workspace, awareness) { | ||
super(id, workspace.doc, awareness); | ||
this._prevPages = new Set(); | ||
@@ -37,2 +37,3 @@ this.pageAdded = new Signal(); | ||
}; | ||
this._workspace = workspace; | ||
this._yMetaRoot.observeDeep(this._handlePageEvent); | ||
@@ -49,2 +50,8 @@ } | ||
} | ||
get _yVersions() { | ||
if (!this._yMetaRoot.has('versions')) { | ||
this._yMetaRoot.set('versions', new Y.Map()); | ||
} | ||
return this._yMetaRoot.get('versions'); | ||
} | ||
get pageMetas() { | ||
@@ -88,2 +95,20 @@ return this._yPages.toJSON(); | ||
} | ||
/** | ||
* @internal Only for page initialization | ||
*/ | ||
writeVersion() { | ||
const { _yVersions, _workspace } = this; | ||
_workspace.flavourMap.forEach((model, flavour) => { | ||
const yVersion = new Y.Array(); | ||
const [major, minor] = model.version; | ||
yVersion.push([major, minor]); | ||
_yVersions.set(flavour, yVersion); | ||
}); | ||
} | ||
/** | ||
* @internal Only for page initialization | ||
*/ | ||
validateVersion() { | ||
// TODO: validate version | ||
} | ||
} | ||
@@ -96,3 +121,3 @@ export class Workspace { | ||
this._blobStorage = getBlobStorage(options.room); | ||
this.meta = new WorkspaceMeta('space:meta', this.doc, this._store.awareness); | ||
this.meta = new WorkspaceMeta('space:meta', this, this._store.awareness); | ||
this.signals = { | ||
@@ -99,0 +124,0 @@ pagesUpdated: this.meta.pagesUpdated, |
{ | ||
"name": "@blocksuite/store", | ||
"version": "0.3.0-alpha.8", | ||
"version": "0.3.0-alpha.9", | ||
"description": "BlockSuite data store built for general purpose state management.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -87,2 +87,3 @@ // checkout https://vitest.dev/guide/debugging.html for debugging tests | ||
], | ||
versions: {}, | ||
}, | ||
@@ -89,0 +90,0 @@ [spaceId]: {}, |
@@ -16,2 +16,4 @@ import type { Page } from './workspace'; | ||
export class BaseBlockModel implements IBaseBlockProps { | ||
static version: [number, number]; | ||
page: Page; | ||
@@ -18,0 +20,0 @@ propsUpdated = new Signal(); |
import type * as Y from 'yjs'; | ||
import { Awareness } from 'y-protocols/awareness.js'; | ||
import { AwarenessAdapter, SelectionRange } from './awareness'; | ||
import type { BaseBlockModel } from './base'; | ||
import type { RichTextAdapter } from './text-adapter'; | ||
@@ -12,6 +11,3 @@ | ||
export class Space< | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
IBlockSchema extends Record<string, typeof BaseBlockModel> = any | ||
> { | ||
export class Space { | ||
readonly id: string; | ||
@@ -18,0 +14,0 @@ readonly doc: Y.Doc; |
@@ -46,6 +46,7 @@ import * as Y from 'yjs'; | ||
private _idGenerator: IdGenerator; | ||
private _history: Y.UndoManager; | ||
private _history!: Y.UndoManager; | ||
private _root: BaseBlockModel | null = null; | ||
private _blockMap = new Map<string, BaseBlockModel>(); | ||
private _splitSet = new Set<Text | PrelimText>(); | ||
private _synced = false; | ||
@@ -75,22 +76,2 @@ // TODO use schema | ||
this._idGenerator = idGenerator; | ||
const { _yBlocks } = this; | ||
// Consider if we need to expose the ability to temporarily unobserve this._yBlocks. | ||
// "unobserve" is potentially necessary to make sure we don't create | ||
// an infinite loop when sync to remote then back to client. | ||
// `action(a) -> YDoc' -> YEvents(a) -> YRemoteDoc' -> YEvents(a) -> YDoc'' -> ...` | ||
// We could unobserve in order to short circuit by ignoring the sync of remote | ||
// events we actually generated locally. | ||
// _yBlocks.unobserveDeep(this._handleYEvents); | ||
_yBlocks.observeDeep(this._handleYEvents); | ||
this._history = new Y.UndoManager([_yBlocks], { | ||
trackedOrigins: new Set([this.doc.clientID]), | ||
doc: this.doc, | ||
}); | ||
this._history.on('stack-cleared', this._historyObserver); | ||
this._history.on('stack-item-added', this._historyAddObserver); | ||
this._history.on('stack-item-popped', this._historyPopObserver); | ||
this._history.on('stack-item-updated', this._historyObserver); | ||
} | ||
@@ -320,2 +301,9 @@ | ||
syncFromExistingDoc() { | ||
if (this._synced) { | ||
throw new Error('Cannot sync from existing doc more than once'); | ||
} | ||
this._handleVersion(); | ||
this._initYBlocks(); | ||
const visited = new Set<string>(); | ||
@@ -328,2 +316,4 @@ | ||
}); | ||
this._synced = true; | ||
} | ||
@@ -343,2 +333,24 @@ | ||
private _initYBlocks() { | ||
const { _yBlocks } = this; | ||
// Consider if we need to expose the ability to temporarily unobserve this._yBlocks. | ||
// "unobserve" is potentially necessary to make sure we don't create | ||
// an infinite loop when sync to remote then back to client. | ||
// `action(a) -> YDoc' -> YEvents(a) -> YRemoteDoc' -> YEvents(a) -> YDoc'' -> ...` | ||
// We could unobserve in order to short circuit by ignoring the sync of remote | ||
// events we actually generated locally. | ||
// _yBlocks.unobserveDeep(this._handleYEvents); | ||
_yBlocks.observeDeep(this._handleYEvents); | ||
this._history = new Y.UndoManager([_yBlocks], { | ||
trackedOrigins: new Set([this.doc.clientID]), | ||
doc: this.doc, | ||
}); | ||
this._history.on('stack-cleared', this._historyObserver); | ||
this._history.on('stack-item-added', this._historyAddObserver); | ||
this._history.on('stack-item-popped', this._historyPopObserver); | ||
this._history.on('stack-item-updated', this._historyObserver); | ||
} | ||
private _getYBlock(id: string): YBlock { | ||
@@ -535,2 +547,13 @@ const yBlock = this._yBlocks.get(id) as YBlock | undefined; | ||
}; | ||
private _handleVersion() { | ||
// Initialization from empty yDoc, indicating that the document is new. | ||
if (this._yBlocks.size === 0) { | ||
this.workspace.meta.writeVersion(); | ||
} | ||
// Initialization from existing yDoc, indicating that the document is loaded from storage. | ||
else { | ||
this.workspace.meta.validateVersion(); | ||
} | ||
} | ||
} |
@@ -19,3 +19,4 @@ import * as Y from 'yjs'; | ||
class WorkspaceMeta extends Space { | ||
_prevPages = new Set<string>(); | ||
private _workspace: Workspace; | ||
private _prevPages = new Set<string>(); | ||
pageAdded = new Signal<string>(); | ||
@@ -25,4 +26,5 @@ pageRemoved = new Signal<string>(); | ||
constructor(id: string, doc: Y.Doc, awareness: Awareness) { | ||
super(id, doc, awareness); | ||
constructor(id: string, workspace: Workspace, awareness: Awareness) { | ||
super(id, workspace.doc, awareness); | ||
this._workspace = workspace; | ||
this._yMetaRoot.observeDeep(this._handlePageEvent); | ||
@@ -43,2 +45,10 @@ } | ||
private get _yVersions() { | ||
if (!this._yMetaRoot.has('versions')) { | ||
this._yMetaRoot.set('versions', new Y.Map()); | ||
} | ||
return this._yMetaRoot.get('versions') as Y.Map<unknown>; | ||
} | ||
get pageMetas() { | ||
@@ -87,2 +97,22 @@ return this._yPages.toJSON() as PageMeta[]; | ||
/** | ||
* @internal Only for page initialization | ||
*/ | ||
writeVersion() { | ||
const { _yVersions, _workspace } = this; | ||
_workspace.flavourMap.forEach((model, flavour) => { | ||
const yVersion = new Y.Array(); | ||
const [major, minor] = model.version; | ||
yVersion.push([major, minor]); | ||
_yVersions.set(flavour, yVersion); | ||
}); | ||
} | ||
/** | ||
* @internal Only for page initialization | ||
*/ | ||
validateVersion() { | ||
// TODO: validate version | ||
} | ||
private _handlePageEvent = (_: Y.YEvent<Y.Array<unknown>>[]) => { | ||
@@ -136,7 +166,3 @@ const { pageMetas, _prevPages } = this; | ||
this.meta = new WorkspaceMeta( | ||
'space:meta', | ||
this.doc, | ||
this._store.awareness | ||
); | ||
this.meta = new WorkspaceMeta('space:meta', this, this._store.awareness); | ||
@@ -143,0 +169,0 @@ this.signals = { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
324765
6233