feat(editor): schema extension (#10447)

1. **Major Architectural Change: Schema Management**
   - Moved from `workspace.schema` to `store.schema` throughout the codebase
   - Removed schema property from Workspace and Doc interfaces
   - Added `BlockSchemaExtension` pattern across multiple block types

2. **Block Schema Extensions Added**
   - Added new `BlockSchemaExtension` to numerous block types including:
     - DataView, Surface, Attachment, Bookmark, Code
     - Database, Divider, EdgelessText, Embed blocks (Figma, Github, HTML, etc.)
     - Frame, Image, Latex, List, Note, Paragraph
     - Root, Surface Reference, Table blocks

3. **Import/Export System Updates**
   - Updated import functions to accept `schema` parameter:
     - `importHTMLToDoc`
     - `importHTMLZip`
     - `importMarkdownToDoc`
     - `importMarkdownZip`
     - `importNotionZip`
   - Modified export functions to use new schema pattern

4. **Test Infrastructure Updates**
   - Updated test files to use new schema extensions
   - Modified test document creation to include schema extensions
   - Removed direct schema registration in favor of extensions

5. **Service Layer Changes**
   - Updated various services to use `getAFFiNEWorkspaceSchema()`
   - Modified transformer initialization to use document schema
   - Updated collection initialization patterns

6. **Version Management**
   - Removed version-related properties and methods from:
     - `WorkspaceMetaImpl`
     - `TestMeta`
     - `DocImpl`
   - Removed `blockVersions` and `workspaceVersion/pageVersion`

7. **Store and Extension Updates**
   - Added new store extensions and adapters
   - Updated store initialization patterns
   - Added new schema-related functionality in store extension

This PR represents a significant architectural shift in how schemas are managed, moving from a workspace-centric to a store-centric approach, while introducing a more extensible block schema system through `BlockSchemaExtension`. The changes touch multiple layers of the application including core functionality, services, testing infrastructure, and import/export capabilities.
This commit is contained in:
Saul-Mirone
2025-02-26 11:31:28 +00:00
parent 2732b96d00
commit ce87dcf58e
95 changed files with 655 additions and 490 deletions

View File

@@ -1,4 +1,3 @@
import { Schema } from '@blocksuite/store';
import {
createAutoIncrementIdGenerator,
TestWorkspace,
@@ -9,19 +8,23 @@ import { effects } from '../effects.js';
import { TestEditorContainer } from './test-editor.js';
import {
type HeadingBlockModel,
HeadingBlockSchema,
NoteBlockSchema,
RootBlockSchema,
HeadingBlockSchemaExtension,
NoteBlockSchemaExtension,
RootBlockSchemaExtension,
} from './test-schema.js';
import { testSpecs } from './test-spec.js';
effects();
const extensions = [
RootBlockSchemaExtension,
NoteBlockSchemaExtension,
HeadingBlockSchemaExtension,
];
function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator();
const schema = new Schema();
schema.register([RootBlockSchema, NoteBlockSchema, HeadingBlockSchema]);
return { id: 'test-collection', idGenerator, schema };
return { id: 'test-collection', idGenerator };
}
function wait(time: number) {
@@ -33,7 +36,7 @@ describe('editor host', () => {
const collection = new TestWorkspace(createTestOptions());
collection.meta.initialize();
const doc = collection.createDoc({ id: 'home' });
const doc = collection.createDoc({ id: 'home', extensions });
doc.load();
const rootId = doc.addBlock('test:page');
const noteId = doc.addBlock('test:note', {}, rootId);

View File

@@ -1,4 +1,8 @@
import { BlockModel, defineBlockSchema } from '@blocksuite/store';
import {
BlockModel,
BlockSchemaExtension,
defineBlockSchema,
} from '@blocksuite/store';
export const RootBlockSchema = defineBlockSchema({
flavour: 'test:page',
@@ -15,6 +19,8 @@ export const RootBlockSchema = defineBlockSchema({
},
});
export const RootBlockSchemaExtension = BlockSchemaExtension(RootBlockSchema);
export class RootBlockModel extends BlockModel<
ReturnType<(typeof RootBlockSchema)['model']['props']>
> {}
@@ -30,6 +36,8 @@ export const NoteBlockSchema = defineBlockSchema({
},
});
export const NoteBlockSchemaExtension = BlockSchemaExtension(NoteBlockSchema);
export class NoteBlockModel extends BlockModel<
ReturnType<(typeof NoteBlockSchema)['model']['props']>
> {}
@@ -47,6 +55,9 @@ export const HeadingBlockSchema = defineBlockSchema({
},
});
export const HeadingBlockSchemaExtension =
BlockSchemaExtension(HeadingBlockSchema);
export class HeadingBlockModel extends BlockModel<
ReturnType<(typeof HeadingBlockSchema)['model']['props']>
> {}

View File

@@ -142,7 +142,7 @@ export class BlockStdScope {
getTransformer(middlewares: TransformerMiddleware[] = []) {
return new Transformer({
schema: this.workspace.schema,
schema: this.store.schema,
blobCRUD: this.workspace.blobSync,
docCRUD: {
create: (id: string) => this.workspace.createDoc({ id }),

View File

@@ -2,6 +2,7 @@ import { computed, effect } from '@preact/signals-core';
import { describe, expect, test, vi } from 'vitest';
import * as Y from 'yjs';
import { BlockSchemaExtension } from '../extension/schema.js';
import {
Block,
BlockModel,
@@ -9,7 +10,6 @@ import {
internalPrimitives,
} from '../model/block/index.js';
import type { YBlock } from '../model/block/types.js';
import { Schema } from '../schema/index.js';
import { createAutoIncrementIdGenerator } from '../test/index.js';
import { TestWorkspace } from '../test/test-workspace.js';
@@ -27,6 +27,7 @@ const pageSchema = defineBlockSchema({
version: 1,
},
});
const pageSchemaExtension = BlockSchemaExtension(pageSchema);
const tableSchema = defineBlockSchema({
flavour: 'table',
@@ -39,6 +40,7 @@ const tableSchema = defineBlockSchema({
version: 1,
},
});
const tableSchemaExtension = BlockSchemaExtension(tableSchema);
const flatTableSchema = defineBlockSchema({
flavour: 'flat-table',
@@ -54,6 +56,8 @@ const flatTableSchema = defineBlockSchema({
isFlatData: true,
},
});
const flatTableSchemaExtension = BlockSchemaExtension(flatTableSchema);
class RootModel extends BlockModel<
ReturnType<(typeof pageSchema)['model']['props']>
> {}
@@ -66,9 +70,7 @@ class FlatTableModel extends BlockModel<
function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator();
const schema = new Schema();
schema.register([pageSchema, tableSchema, flatTableSchema]);
return { id: 'test-collection', idGenerator, schema };
return { id: 'test-collection', idGenerator };
}
const defaultDocId = 'doc:home';
@@ -76,7 +78,14 @@ function createTestDoc(docId = defaultDocId) {
const options = createTestOptions();
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: docId });
const doc = collection.createDoc({
id: docId,
extensions: [
pageSchemaExtension,
tableSchemaExtension,
flatTableSchemaExtension,
],
});
doc.load();
return doc;
}

View File

@@ -4,29 +4,20 @@ import type { Slot } from '@blocksuite/global/utils';
import { assert, beforeEach, describe, expect, it, vi } from 'vitest';
import { applyUpdate, type Doc, encodeStateAsUpdate } from 'yjs';
import type { BlockModel, BlockSchemaType, DocMeta, Store } from '../index.js';
import { Schema } from '../index.js';
import type { BlockModel, DocMeta, Store } from '../index.js';
import { Text } from '../reactive/text.js';
import { createAutoIncrementIdGenerator } from '../test/index.js';
import { TestWorkspace } from '../test/test-workspace.js';
import {
NoteBlockSchema,
ParagraphBlockSchema,
RootBlockSchema,
NoteBlockSchemaExtension,
ParagraphBlockSchemaExtension,
RootBlockSchemaExtension,
} from './test-schema.js';
import { assertExists } from './test-utils-dom.js';
export const BlockSchemas = [
ParagraphBlockSchema,
RootBlockSchema,
NoteBlockSchema,
] as BlockSchemaType[];
function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator();
const schema = new Schema();
schema.register(BlockSchemas);
return { id: 'test-collection', idGenerator, schema };
return { id: 'test-collection', idGenerator };
}
const defaultDocId = 'doc:home';
@@ -58,11 +49,20 @@ function createRoot(doc: Store) {
return doc.root;
}
const extensions = [
NoteBlockSchemaExtension,
ParagraphBlockSchemaExtension,
RootBlockSchemaExtension,
];
function createTestDoc(docId = defaultDocId) {
const options = createTestOptions();
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: docId });
const doc = collection.createDoc({
id: docId,
extensions,
});
doc.load();
return doc;
}
@@ -113,13 +113,6 @@ describe('basic', () => {
tags: [],
},
],
workspaceVersion: 2,
pageVersion: 2,
blockVersions: {
'affine:note': 1,
'affine:page': 2,
'affine:paragraph': 1,
},
},
spaces: {
[spaceId]: {
@@ -155,6 +148,7 @@ describe('basic', () => {
collection.meta.initialize();
const doc = collection.createDoc({
id: 'space:0',
extensions,
});
const readyCallback = vi.fn();
@@ -181,6 +175,7 @@ describe('basic', () => {
const collection2 = new TestWorkspace(options);
const doc = collection.createDoc({
id: 'space:0',
extensions,
});
doc.load(() => {
doc.addBlock('affine:page', {
@@ -209,7 +204,9 @@ describe('basic', () => {
// apply doc update
const update = encodeStateAsUpdate(doc.spaceDoc);
expect(collection2.docs.size).toBe(1);
const doc2 = collection2.getDoc('space:0');
const doc2 = collection2.getDoc('space:0', {
extensions,
});
assertExists(doc2);
applyUpdate(doc2.spaceDoc, update);
expect(serializCollection(collection2.doc)['spaces']).toEqual({

View File

@@ -2,31 +2,28 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
import * as Y from 'yjs';
import type { BlockModel, Store } from '../model/index.js';
import { Schema } from '../schema/index.js';
import { createAutoIncrementIdGenerator } from '../test/index.js';
import { TestWorkspace } from '../test/test-workspace.js';
import {
DividerBlockSchema,
ListBlockSchema,
NoteBlockSchema,
ParagraphBlockSchema,
DividerBlockSchemaExtension,
ListBlockSchemaExtension,
NoteBlockSchemaExtension,
ParagraphBlockSchemaExtension,
type RootBlockModel,
RootBlockSchema,
RootBlockSchemaExtension,
} from './test-schema.js';
const BlockSchemas = [
RootBlockSchema,
ParagraphBlockSchema,
ListBlockSchema,
NoteBlockSchema,
DividerBlockSchema,
const extensions = [
RootBlockSchemaExtension,
ParagraphBlockSchemaExtension,
ListBlockSchemaExtension,
NoteBlockSchemaExtension,
DividerBlockSchemaExtension,
];
function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator();
const schema = new Schema();
schema.register(BlockSchemas);
return { id: 'test-collection', idGenerator, schema };
return { id: 'test-collection', idGenerator };
}
test('trigger props updated', () => {
@@ -34,7 +31,7 @@ test('trigger props updated', () => {
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: 'home' });
const doc = collection.createDoc({ id: 'home', extensions });
doc.load();
doc.addBlock('affine:page');
@@ -94,7 +91,7 @@ test('stash and pop', () => {
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: 'home' });
const doc = collection.createDoc({ id: 'home', extensions });
doc.load();
doc.addBlock('affine:page');
@@ -164,7 +161,7 @@ test('always get latest value in onChange', () => {
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: 'home' });
const doc = collection.createDoc({ id: 'home', extensions });
doc.load();
doc.addBlock('affine:page');
@@ -210,11 +207,12 @@ test('query', () => {
const options = createTestOptions();
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc1 = collection.createDoc({ id: 'home' });
const doc1 = collection.createDoc({ id: 'home', extensions });
doc1.load();
const doc2 = collection.getDoc('home');
const doc2 = collection.getDoc('home', { extensions });
const doc3 = collection.getDoc('home', {
extensions,
query: {
mode: 'loose',
match: [
@@ -247,10 +245,10 @@ test('local readonly', () => {
const options = createTestOptions();
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc1 = collection.createDoc({ id: 'home' });
const doc1 = collection.createDoc({ id: 'home', extensions });
doc1.load();
const doc2 = collection.getDoc('home', { readonly: true });
const doc3 = collection.getDoc('home', { readonly: false });
const doc2 = collection.getDoc('home', { readonly: true, extensions });
const doc3 = collection.getDoc('home', { readonly: false, extensions });
expect(doc1.readonly).toBeFalsy();
expect(doc2?.readonly).toBeTruthy();
@@ -276,7 +274,7 @@ describe('move blocks', () => {
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: 'home' });
const doc = collection.createDoc({ id: 'home', extensions });
doc.load();
const pageId = doc.addBlock('affine:page');
const page = doc.getBlock(pageId)!.model;

View File

@@ -1,25 +1,23 @@
import { literal } from 'lit/static-html.js';
import { describe, expect, it, vi } from 'vitest';
import { BlockSchemaExtension } from '../extension/schema.js';
import { defineBlockSchema } from '../model/block/zod.js';
// import some blocks
import { SchemaValidateError } from '../schema/error.js';
import { Schema } from '../schema/index.js';
import { createAutoIncrementIdGenerator } from '../test/index.js';
import { TestWorkspace } from '../test/test-workspace.js';
import {
DividerBlockSchema,
ListBlockSchema,
NoteBlockSchema,
ParagraphBlockSchema,
RootBlockSchema,
DividerBlockSchemaExtension,
ListBlockSchemaExtension,
NoteBlockSchemaExtension,
ParagraphBlockSchemaExtension,
RootBlockSchemaExtension,
} from './test-schema.js';
function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator();
const schema = new Schema();
schema.register(BlockSchemas);
return { id: 'test-collection', idGenerator, schema };
return { id: 'test-collection', idGenerator };
}
const TestCustomNoteBlockSchema = defineBlockSchema({
@@ -35,6 +33,10 @@ const TestCustomNoteBlockSchema = defineBlockSchema({
},
});
const TestCustomNoteBlockSchemaExtension = BlockSchemaExtension(
TestCustomNoteBlockSchema
);
const TestInvalidNoteBlockSchema = defineBlockSchema({
flavour: 'affine:note-invalid-block-video',
props: internal => ({
@@ -48,14 +50,18 @@ const TestInvalidNoteBlockSchema = defineBlockSchema({
},
});
const BlockSchemas = [
RootBlockSchema,
ParagraphBlockSchema,
ListBlockSchema,
NoteBlockSchema,
DividerBlockSchema,
TestCustomNoteBlockSchema,
TestInvalidNoteBlockSchema,
const TestInvalidNoteBlockSchemaExtension = BlockSchemaExtension(
TestInvalidNoteBlockSchema
);
const extensions = [
RootBlockSchemaExtension,
ParagraphBlockSchemaExtension,
ListBlockSchemaExtension,
NoteBlockSchemaExtension,
DividerBlockSchemaExtension,
TestCustomNoteBlockSchemaExtension,
TestInvalidNoteBlockSchemaExtension,
];
const defaultDocId = 'doc0';
@@ -63,7 +69,7 @@ function createTestDoc(docId = defaultDocId) {
const options = createTestOptions();
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: docId });
const doc = collection.createDoc({ id: docId, extensions });
doc.load();
return doc;
}

View File

@@ -1,3 +1,4 @@
import { BlockSchemaExtension } from '../extension/schema.js';
import { BlockModel, defineBlockSchema } from '../model/index.js';
export const RootBlockSchema = defineBlockSchema({
@@ -14,6 +15,8 @@ export const RootBlockSchema = defineBlockSchema({
},
});
export const RootBlockSchemaExtension = BlockSchemaExtension(RootBlockSchema);
export class RootBlockModel extends BlockModel<
ReturnType<(typeof RootBlockSchema)['model']['props']>
> {}
@@ -42,6 +45,8 @@ export const NoteBlockSchema = defineBlockSchema({
},
});
export const NoteBlockSchemaExtension = BlockSchemaExtension(NoteBlockSchema);
export const ParagraphBlockSchema = defineBlockSchema({
flavour: 'affine:paragraph',
props: internal => ({
@@ -60,6 +65,9 @@ export const ParagraphBlockSchema = defineBlockSchema({
},
});
export const ParagraphBlockSchemaExtension =
BlockSchemaExtension(ParagraphBlockSchema);
export const ListBlockSchema = defineBlockSchema({
flavour: 'affine:list',
props: internal => ({
@@ -80,6 +88,8 @@ export const ListBlockSchema = defineBlockSchema({
},
});
export const ListBlockSchemaExtension = BlockSchemaExtension(ListBlockSchema);
export const DividerBlockSchema = defineBlockSchema({
flavour: 'affine:divider',
metadata: {
@@ -88,3 +98,6 @@ export const DividerBlockSchema = defineBlockSchema({
children: [],
},
});
export const DividerBlockSchemaExtension =
BlockSchemaExtension(DividerBlockSchema);

View File

@@ -2,10 +2,10 @@ import { expect, test } from 'vitest';
import * as Y from 'yjs';
import { MemoryBlobCRUD } from '../adapter/index.js';
import { BlockSchemaExtension } from '../extension/schema.js';
import { BlockModel } from '../model/block/block-model.js';
import { defineBlockSchema } from '../model/block/zod.js';
import { Text } from '../reactive/index.js';
import { Schema } from '../schema/index.js';
import { createAutoIncrementIdGenerator } from '../test/index.js';
import { TestWorkspace } from '../test/test-workspace.js';
import { AssetsManager, BaseBlockTransformer } from '../transformer/index.js';
@@ -39,15 +39,16 @@ const docSchema = defineBlockSchema({
},
});
const docSchemaExtension = BlockSchemaExtension(docSchema);
class RootBlockModel extends BlockModel<
ReturnType<(typeof docSchema)['model']['props']>
> {}
const extensions = [docSchemaExtension];
function createTestOptions() {
const idGenerator = createAutoIncrementIdGenerator();
const schema = new Schema();
schema.register([docSchema]);
return { id: 'test-collection', idGenerator, schema };
return { id: 'test-collection', idGenerator };
}
const transformer = new BaseBlockTransformer(new Map());
@@ -58,7 +59,7 @@ test('model to snapshot', () => {
const options = createTestOptions();
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: 'home' });
const doc = collection.createDoc({ id: 'home', extensions });
doc.load();
doc.addBlock('page');
const rootModel = doc.root as RootBlockModel;
@@ -75,7 +76,7 @@ test('snapshot to model', async () => {
const options = createTestOptions();
const collection = new TestWorkspace(options);
collection.meta.initialize();
const doc = collection.createDoc({ id: 'home' });
const doc = collection.createDoc({ id: 'home', extensions });
doc.load();
doc.addBlock('page');
const rootModel = doc.root as RootBlockModel;

View File

@@ -1,3 +1,4 @@
export * from './extension';
export * from './schema';
export * from './selection';
export * from './store-extension';

View File

@@ -0,0 +1,20 @@
import { createIdentifier } from '@blocksuite/global/di';
import type { BlockSchemaType } from '../model/block/zod';
import type { ExtensionType } from './extension';
export const BlockSchemaIdentifier =
createIdentifier<BlockSchemaType>('BlockSchema');
export function BlockSchemaExtension(
blockSchema: BlockSchemaType
): ExtensionType {
return {
setup: di => {
di.addImpl(
BlockSchemaIdentifier(blockSchema.model.flavour),
() => blockSchema
);
},
};
}

View File

@@ -1,7 +1,6 @@
import type { Slot } from '@blocksuite/global/utils';
import type * as Y from 'yjs';
import type { Schema } from '../schema/schema.js';
import type { AwarenessStore } from '../yjs/awareness.js';
import type { YBlock } from './block/types.js';
import type { Query } from './store/query.js';
@@ -18,7 +17,6 @@ export type YBlocks = Y.Map<YBlock>;
export interface Doc {
readonly id: string;
get meta(): DocMeta | undefined;
get schema(): Schema;
remove(): void;
load(initFn?: () => void): void;

View File

@@ -4,8 +4,11 @@ import { type Disposable, Slot } from '@blocksuite/global/utils';
import { computed, signal } from '@preact/signals-core';
import type { ExtensionType } from '../../extension/extension.js';
import { StoreSelectionExtension } from '../../extension/index.js';
import type { Schema } from '../../schema/index.js';
import {
BlockSchemaIdentifier,
StoreSelectionExtension,
} from '../../extension/index.js';
import { Schema } from '../../schema/index.js';
import {
Block,
type BlockModel,
@@ -20,7 +23,6 @@ import { type Query, runQuery } from './query.js';
import { syncBlockProps } from './utils.js';
export type StoreOptions = {
schema: Schema;
doc: Doc;
id?: string;
readonly?: boolean;
@@ -298,14 +300,7 @@ export class Store {
return this._doc.withoutTransact.bind(this._doc);
}
constructor({
schema,
doc,
readonly,
query,
provider,
extensions,
}: StoreOptions) {
constructor({ doc, readonly, query, provider, extensions }: StoreOptions) {
const container = new Container();
container.addImpl(StoreIdentifier, () => this);
@@ -331,8 +326,11 @@ export class Store {
yBlockUpdated: this._doc.slots.yBlockUpdated,
};
this._crud = new DocCRUD(this._yBlocks, doc.schema);
this._schema = schema;
this._schema = new Schema();
this._provider.getAll(BlockSchemaIdentifier).forEach(schema => {
this._schema.register([schema]);
});
this._crud = new DocCRUD(this._yBlocks, this._schema);
if (readonly !== undefined) {
this._readonly.value = readonly;
}

View File

@@ -1,7 +1,5 @@
import type { Slot } from '@blocksuite/global/utils';
import type { Workspace } from './workspace.js';
export type Tag = {
id: string;
value: string;
@@ -38,8 +36,6 @@ export interface WorkspaceMeta {
get name(): string | undefined;
setName(name: string): void;
hasVersion: boolean;
writeVersion(workspace: Workspace): void;
get docs(): unknown[] | undefined;
initialize(): void;

View File

@@ -3,7 +3,6 @@ import type { BlobEngine } from '@blocksuite/sync';
import type { Awareness } from 'y-protocols/awareness.js';
import type * as Y from 'yjs';
import type { Schema } from '../schema/schema.js';
import type { IdGenerator } from '../utils/id-generator.js';
import type { AwarenessStore } from '../yjs/awareness.js';
import type { CreateBlocksOptions, Doc, GetBlocksOptions } from './doc.js';
@@ -19,7 +18,6 @@ export interface Workspace {
readonly onLoadDoc?: (doc: Y.Doc) => void;
readonly onLoadAwareness?: (awareness: Awareness) => void;
get schema(): Schema;
get doc(): Y.Doc;
get docs(): Map<string, Doc>;

View File

@@ -162,10 +162,6 @@ export class TestDoc implements Doc {
return this._ready;
}
get schema() {
return this.workspace.schema;
}
get spaceDoc() {
return this._ySpaceDoc;
}
@@ -189,13 +185,6 @@ export class TestDoc implements Doc {
return (readonly?.toString() as 'true' | 'false') ?? 'false';
}
private _handleVersion() {
// Initialization from empty yDoc, indicating that the document is new.
if (!this.workspace.meta.hasVersion) {
this.workspace.meta.writeVersion(this.workspace);
}
}
private _handleYBlockAdd(id: string) {
this.slots.yBlockUpdated.emit({ type: 'add', id });
}
@@ -306,7 +295,6 @@ export class TestDoc implements Doc {
const doc = new Store({
doc: this,
schema: this.workspace.schema,
readonly,
query,
provider,
@@ -327,10 +315,6 @@ export class TestDoc implements Doc {
this._ySpaceDoc.load();
if ((this.workspace.meta.docs?.length ?? 0) <= 1) {
this._handleVersion();
}
this._initYBlocks();
this._yBlocks.forEach((_, id) => {

View File

@@ -4,20 +4,13 @@ import type * as Y from 'yjs';
import type {
DocMeta,
DocsPropertiesMeta,
Workspace,
WorkspaceMeta,
} from '../model/index.js';
import { createYProxy } from '../reactive/proxy.js';
const COLLECTION_VERSION = 2;
const PAGE_VERSION = 2;
type DocCollectionMetaState = {
pages?: unknown[];
properties?: DocsPropertiesMeta;
workspaceVersion?: number;
pageVersion?: number;
blockVersions?: Record<string, number>;
name?: string;
avatar?: string;
};
@@ -68,10 +61,6 @@ export class TestMeta implements WorkspaceMeta {
return this._proxy.avatar;
}
get blockVersions() {
return this._proxy.blockVersions;
}
get docMetas() {
if (!this._proxy.pages) {
return [] as DocMeta[];
@@ -83,21 +72,10 @@ export class TestMeta implements WorkspaceMeta {
return this._proxy.pages;
}
get hasVersion() {
if (!this.blockVersions || !this.pageVersion || !this.workspaceVersion) {
return false;
}
return Object.keys(this.blockVersions).length > 0;
}
get name() {
return this._proxy.name;
}
get pageVersion() {
return this._proxy.pageVersion;
}
get properties(): DocsPropertiesMeta {
const meta = this._proxy.properties;
if (!meta) {
@@ -110,10 +88,6 @@ export class TestMeta implements WorkspaceMeta {
return meta;
}
get workspaceVersion() {
return this._proxy.workspaceVersion;
}
get yDocs() {
return this._yMap.get('pages') as unknown as Y.Array<unknown>;
}
@@ -232,33 +206,4 @@ export class TestMeta implements WorkspaceMeta {
this._proxy.properties = meta;
this.docMetaUpdated.emit();
}
/**
* @internal Only for doc initialization
*/
writeVersion(collection: Workspace) {
const { blockVersions, pageVersion, workspaceVersion } = this._proxy;
if (!workspaceVersion) {
this._proxy.workspaceVersion = COLLECTION_VERSION;
} else {
console.error('Workspace version is already set');
}
if (!pageVersion) {
this._proxy.pageVersion = PAGE_VERSION;
} else {
console.error('Doc version is already set');
}
if (!blockVersions) {
const _versions: Record<string, number> = {};
collection.schema.flavourSchemaMap.forEach((schema, flavour) => {
_versions[flavour] = schema.version;
});
this._proxy.blockVersions = _versions;
} else {
console.error('Block versions is already set');
}
}
}

View File

@@ -21,14 +21,12 @@ import type {
Workspace,
WorkspaceMeta,
} from '../model/index.js';
import type { Schema } from '../schema/index.js';
import { type IdGenerator, nanoid } from '../utils/id-generator.js';
import { AwarenessStore } from '../yjs/index.js';
import { TestDoc } from './test-doc.js';
import { TestMeta } from './test-meta.js';
export type DocCollectionOptions = {
schema: Schema;
id?: string;
idGenerator?: IdGenerator;
docSources?: {
@@ -47,8 +45,6 @@ export type DocCollectionOptions = {
* Do not use this in production
*/
export class TestWorkspace implements Workspace {
protected readonly _schema: Schema;
storeExtensions: ExtensionType[] = [];
readonly awarenessStore: AwarenessStore;
@@ -79,13 +75,8 @@ export class TestWorkspace implements Workspace {
return this.blockCollections;
}
get schema() {
return this._schema;
}
constructor({
id,
schema,
idGenerator,
awarenessSources = [],
docSources = {
@@ -94,9 +85,7 @@ export class TestWorkspace implements Workspace {
blobSources = {
main: new MemoryBlobSource(),
},
}: DocCollectionOptions) {
this._schema = schema;
}: DocCollectionOptions = {}) {
this.id = id || '';
this.doc = new Y.Doc({ guid: id });
this.awarenessStore = new AwarenessStore(new Awareness(this.doc));
@@ -165,7 +154,12 @@ export class TestWorkspace implements Workspace {
* will be created in the doc simultaneously.
*/
createDoc(options: CreateBlocksOptions = {}) {
const { id: docId = this.idGenerator(), query, readonly } = options;
const {
id: docId = this.idGenerator(),
query,
readonly,
extensions,
} = options;
if (this._hasDoc(docId)) {
throw new BlockSuiteError(
ErrorCode.DocCollectionError,
@@ -184,6 +178,7 @@ export class TestWorkspace implements Workspace {
id: docId,
query,
readonly,
extensions,
}) as Store;
}