feat(editor): merge store and blocks (#9591)

This commit is contained in:
Saul-Mirone
2025-01-08 13:01:19 +00:00
parent a0cba55a5b
commit 5842d45ab1
124 changed files with 362 additions and 414 deletions
@@ -4,7 +4,7 @@ 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, Blocks, BlockSchemaType, DocMeta } from '../index.js';
import type { BlockModel, BlockSchemaType, DocMeta, Store } from '../index.js';
import { Schema } from '../index.js';
import { Text } from '../reactive/text.js';
import { createAutoIncrementIdGenerator } from '../test/index.js';
@@ -52,7 +52,7 @@ function waitOnce<T>(slot: Slot<T>) {
return new Promise<T>(resolve => slot.once(val => resolve(val)));
}
function createRoot(doc: Blocks) {
function createRoot(doc: Store) {
doc.addBlock('affine:page');
if (!doc.root) throw new Error('root not found');
return doc.root;
@@ -1,6 +1,6 @@
import { BlockSuiteError } from '@blocksuite/global/exceptions';
import type { Blocks, DraftModel } from '../model/index.js';
import type { DraftModel, Store } from '../model/index.js';
import type { AssetsManager } from '../transformer/assets.js';
import type { Job, Slice } from '../transformer/index.js';
import type {
@@ -93,7 +93,7 @@ export abstract class BaseAdapter<AdapterTarget = unknown> {
| Promise<FromBlockSnapshotResult<AdapterTarget>>
| FromBlockSnapshotResult<AdapterTarget>;
async fromDoc(doc: Blocks) {
async fromDoc(doc: Store) {
try {
const docSnapshot = this.job.docToSnapshot(doc);
if (!docSnapshot) return;
@@ -138,7 +138,7 @@ export abstract class BaseAdapter<AdapterTarget = unknown> {
async toBlock(
payload: ToBlockSnapshotPayload<AdapterTarget>,
doc: Blocks,
doc: Store,
parent?: string,
index?: number
) {
@@ -175,7 +175,7 @@ export abstract class BaseAdapter<AdapterTarget = unknown> {
async toSlice(
payload: ToSliceSnapshotPayload<AdapterTarget>,
doc: Blocks,
doc: Store,
parent?: string,
index?: number
) {
@@ -1,8 +1,8 @@
import { type Container, createIdentifier } from '@blocksuite/global/di';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { StoreIdentifier } from '../store/identifier';
import type { Store } from '../store/store';
import type { Store } from '../model/store';
import { StoreIdentifier } from '../model/store/identifier';
import { Extension } from './extension';
export const StoreExtensionIdentifier =
-1
View File
@@ -6,7 +6,6 @@ export * from './extension';
export * from './model';
export * from './reactive';
export * from './schema';
export * from './store';
export * from './transformer';
export { type IdGenerator, nanoid, uuidv4 } from './utils/id-generator';
export * from './yjs';
@@ -2,7 +2,7 @@ import { type Disposable, Slot } from '@blocksuite/global/utils';
import { computed, type Signal, signal } from '@preact/signals-core';
import type { Text } from '../../reactive/index.js';
import type { Blocks } from '../blocks/blocks.js';
import type { Store } from '../store/store.js';
import type { YBlock } from './types.js';
import type { RoleType } from './zod.js';
@@ -38,7 +38,7 @@ export class BlockModel<
/**
* @deprecated use doc instead
*/
page!: Blocks;
page!: Store;
private readonly _childModels = computed(() => {
const value: BlockModel[] = [];
@@ -102,7 +102,7 @@ export class BlockModel<
return this.page;
}
set doc(doc: Blocks) {
set doc(doc: Store) {
this.page = doc;
}
@@ -1,5 +1,5 @@
import type { Schema } from '../../schema/index.js';
import type { Blocks } from '../blocks/blocks.js';
import type { Store } from '../store/store.js';
import { SyncController } from './sync-controller.js';
import type { BlockOptions, YBlock } from './types.js';
@@ -37,7 +37,7 @@ export class Block {
constructor(
readonly schema: Schema,
readonly yBlock: YBlock,
readonly doc?: Blocks,
readonly doc?: Store,
readonly options: BlockOptions = {}
) {
const onChange = !options.onChange
@@ -11,7 +11,7 @@ import {
y2Native,
} from '../../reactive/index.js';
import type { Schema } from '../../schema/schema.js';
import type { Blocks } from '../blocks/blocks.js';
import type { Store } from '../store/store.js';
import { BlockModel } from './block-model.js';
import type { YBlock } from './types.js';
import { internalPrimitives } from './zod.js';
@@ -104,7 +104,7 @@ export class SyncController {
constructor(
readonly schema: Schema,
readonly yBlock: YBlock,
readonly doc?: Blocks,
readonly doc?: Store,
readonly onChange?: (key: string, value: unknown) => void
) {
const { id, flavour, version, yChildren, props } = this._parseYBlock();
@@ -1,2 +0,0 @@
export * from './blocks.js';
export * from './query.js';
+4 -7
View File
@@ -4,15 +4,12 @@ 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 { Blocks } from './blocks/blocks.js';
import type { Query } from './blocks/query.js';
import type { Query } from './store/query.js';
import type { Store, StoreOptions } from './store/store.js';
import type { Workspace } from './workspace.js';
import type { DocMeta } from './workspace-meta.js';
export type GetBlocksOptions = {
query?: Query;
readonly?: boolean;
};
export type GetBlocksOptions = Omit<StoreOptions, 'schema' | 'doc'>;
export type CreateBlocksOptions = GetBlocksOptions & {
id?: string;
};
@@ -53,7 +50,7 @@ export interface Doc {
captureSync(): void;
clear(): void;
getBlocks(options?: GetBlocksOptions): Blocks;
getStore(options?: GetBlocksOptions): Store;
clearQuery(query: Query, readonly?: boolean): void;
get loaded(): boolean;
@@ -1,8 +1,8 @@
import type { BlockModel } from './block/block-model.js';
export * from './block/index.js';
export * from './blocks/index.js';
export * from './doc.js';
export * from './store/index.js';
export * from './workspace.js';
export * from './workspace-meta.js';
@@ -0,0 +1,3 @@
export * from './identifier.js';
export * from './query.js';
export * from './store.js';
@@ -1,7 +1,9 @@
import { Container, type ServiceProvider } from '@blocksuite/global/di';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { type Disposable, Slot } from '@blocksuite/global/utils';
import { signal } from '@preact/signals-core';
import type { ExtensionType } from '../../extension/extension.js';
import type { Schema } from '../../schema/index.js';
import {
Block,
@@ -12,17 +14,22 @@ import {
} from '../block/index.js';
import type { Doc } from '../doc.js';
import { DocCRUD } from './crud.js';
import { StoreIdentifier } from './identifier.js';
import { type Query, runQuery } from './query.js';
import { syncBlockProps } from './utils.js';
type DocOptions = {
export type StoreOptions = {
schema: Schema;
blockCollection: Doc;
doc: Doc;
readonly?: boolean;
query?: Query;
provider?: ServiceProvider;
extensions?: ExtensionType[];
};
export class Blocks {
export class Store {
private readonly _provider: ServiceProvider;
private readonly _runQuery = (block: Block) => {
runQuery(this._query, block);
};
@@ -147,6 +154,10 @@ export class Blocks {
return this._doc.awarenessStore;
}
get provider() {
return this._provider;
}
get blobSync() {
return this.workspace.blobSync;
}
@@ -259,8 +270,24 @@ export class Blocks {
return this._doc.withoutTransact.bind(this._doc);
}
constructor({ schema, blockCollection, readonly, query }: DocOptions) {
this._doc = blockCollection;
constructor({
schema,
doc,
readonly,
query,
provider,
extensions,
}: StoreOptions) {
const container = new Container();
container.addImpl(StoreIdentifier, () => this);
const userExtensions = extensions ?? [];
userExtensions.forEach(extension => {
extension.setup(container);
});
this._provider = container.provider(undefined, provider);
this._doc = doc;
this.slots = {
ready: new Slot(),
@@ -271,7 +298,7 @@ export class Blocks {
yBlockUpdated: this._doc.slots.yBlockUpdated,
};
this._crud = new DocCRUD(this._yBlocks, blockCollection.schema);
this._crud = new DocCRUD(this._yBlocks, doc.schema);
this._schema = schema;
this._readonly = readonly;
if (query) {
@@ -565,7 +592,7 @@ export class Blocks {
return (this.getBlock(id)?.model ?? null) as Model | null;
}
getBlocks() {
getStore() {
return Object.values(this._blocks.peek()).map(block => block.model);
}
@@ -5,8 +5,8 @@ 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 { Blocks } from './blocks/blocks.js';
import type { CreateBlocksOptions, Doc, GetBlocksOptions } from './doc.js';
import type { Store } from './store/store.js';
import type { WorkspaceMeta } from './workspace-meta.js';
export interface Workspace {
@@ -26,8 +26,8 @@ export interface Workspace {
docRemoved: Slot<string>;
};
createDoc(options?: CreateBlocksOptions): Blocks;
getDoc(docId: string, options?: GetBlocksOptions): Blocks | null;
createDoc(options?: CreateBlocksOptions): Store;
getDoc(docId: string, options?: GetBlocksOptions): Store | null;
removeDoc(docId: string): void;
dispose(): void;
@@ -1,2 +0,0 @@
export * from './identifier';
export * from './store';
@@ -1,41 +0,0 @@
import { Container, type ServiceProvider } from '@blocksuite/global/di';
import type { Extension, StoreExtension } from '../extension';
import type { Blocks } from '../model';
import { StoreIdentifier } from './identifier';
export interface StoreOptions {
blocks: Blocks;
provider?: ServiceProvider;
extensions?: (typeof Extension | typeof StoreExtension)[];
}
export class Store {
private readonly _blocks: Blocks;
private readonly _provider: ServiceProvider;
get blocks() {
return this._blocks;
}
get provider() {
return this._provider;
}
get awareness() {
return this._blocks.awarenessStore;
}
constructor(options: StoreOptions) {
this._blocks = options.blocks;
const container = new Container();
container.addImpl(StoreIdentifier, () => this);
const userExtensions = options.extensions ?? [];
userExtensions.forEach(extension => {
extension.setup(container);
});
this._provider = container.provider(undefined, options.provider);
}
}
@@ -3,9 +3,9 @@ import { signal } from '@preact/signals-core';
import * as Y from 'yjs';
import type { YBlock } from '../model/block/types.js';
import { Blocks } from '../model/blocks/blocks.js';
import type { Query } from '../model/blocks/query.js';
import type { Doc, GetBlocksOptions, Workspace } from '../model/index.js';
import type { Query } from '../model/store/query.js';
import { Store } from '../model/store/store.js';
import type { AwarenessStore } from '../yjs/index.js';
type DocOptions = {
@@ -25,9 +25,9 @@ export class TestDoc implements Doc {
private readonly _collection: Workspace;
private readonly _docMap = {
undefined: new Map<string, Blocks>(),
true: new Map<string, Blocks>(),
false: new Map<string, Blocks>(),
undefined: new Map<string, Store>(),
true: new Map<string, Store>(),
false: new Map<string, Store>(),
};
// doc/space container.
@@ -279,7 +279,7 @@ export class TestDoc implements Doc {
}
}
getBlocks({ readonly, query }: GetBlocksOptions = {}) {
getStore({ readonly, query, provider, extensions }: GetBlocksOptions = {}) {
const readonlyKey = this._getReadonlyKey(readonly);
const key = JSON.stringify(query);
@@ -288,11 +288,13 @@ export class TestDoc implements Doc {
return this._docMap[readonlyKey].get(key)!;
}
const doc = new Blocks({
blockCollection: this,
const doc = new Store({
doc: this,
schema: this.workspace.schema,
readonly,
query,
provider,
extensions,
});
this._docMap[readonlyKey].set(key, doc);
@@ -17,9 +17,9 @@ import { Awareness } from 'y-protocols/awareness.js';
import * as Y from 'yjs';
import type {
Blocks,
CreateBlocksOptions,
GetBlocksOptions,
Store,
Workspace,
WorkspaceMeta,
} from '../model/index.js';
@@ -205,7 +205,7 @@ export class TestWorkspace implements Workspace {
tags: [],
});
this.slots.docCreated.emit(docId);
return this.getDoc(docId, { query, readonly }) as Blocks;
return this.getDoc(docId, { query, readonly }) as Store;
}
dispose() {
@@ -227,9 +227,9 @@ export class TestWorkspace implements Workspace {
return space ?? null;
}
getDoc(docId: string, options?: GetBlocksOptions): Blocks | null {
getDoc(docId: string, options?: GetBlocksOptions): Store | null {
const collection = this.getBlockCollection(docId);
return collection?.getBlocks(options) ?? null;
return collection?.getStore(options) ?? null;
}
removeDoc(docId: string) {
@@ -3,9 +3,9 @@ import { nextTick, Slot } from '@blocksuite/global/utils';
import type {
BlockModel,
Blocks,
BlockSchemaType,
DraftModel,
Store,
} from '../model/index.js';
import type { Schema } from '../schema/index.js';
import { AssetsManager } from './assets.js';
@@ -82,7 +82,7 @@ export class Job {
}
};
docToSnapshot = (doc: Blocks): DocSnapshot | undefined => {
docToSnapshot = (doc: Store): DocSnapshot | undefined => {
try {
this._slots.beforeExport.emit({
type: 'page',
@@ -158,7 +158,7 @@ export class Job {
snapshotToBlock = async (
snapshot: BlockSnapshot,
doc: Blocks,
doc: Store,
parent?: string,
index?: number
): Promise<BlockModel | undefined> => {
@@ -174,9 +174,7 @@ export class Job {
}
};
snapshotToDoc = async (
snapshot: DocSnapshot
): Promise<Blocks | undefined> => {
snapshotToDoc = async (snapshot: DocSnapshot): Promise<Store | undefined> => {
try {
this._slots.beforeImport.emit({
type: 'page',
@@ -228,7 +226,7 @@ export class Job {
snapshotToSlice = async (
snapshot: SliceSnapshot,
doc: Blocks,
doc: Store,
parent?: string,
index?: number
): Promise<Slice | undefined> => {
@@ -424,7 +422,7 @@ export class Job {
}
}
private _exportDocMeta(doc: Blocks): DocSnapshot['meta'] {
private _exportDocMeta(doc: Store): DocSnapshot['meta'] {
const docMeta = doc.meta;
if (!docMeta) {
@@ -472,7 +470,7 @@ export class Job {
private async _insertBlockTree(
nodes: DraftBlockTreeNode[],
doc: Blocks,
doc: Store,
parentId?: string,
startIndex?: number,
counter: number = 0
@@ -561,7 +559,7 @@ export class Job {
private async _snapshotToBlock(
snapshot: BlockSnapshot,
doc: Blocks,
doc: Store,
parent?: string,
index?: number
): Promise<BlockModel | null> {
@@ -1,6 +1,6 @@
import type { Slot } from '@blocksuite/global/utils';
import type { Blocks, DraftModel } from '../model/index.js';
import type { DraftModel, Store } from '../model/index.js';
import type { AssetsManager } from './assets.js';
import type { Slice } from './slice.js';
import type {
@@ -37,7 +37,7 @@ export type BeforeExportPayload =
type: 'block';
}
| {
page: Blocks;
page: Store;
type: 'page';
}
| {
@@ -59,7 +59,7 @@ export type FinalPayload =
| {
snapshot: DocSnapshot;
type: 'page';
page: Blocks;
page: Store;
}
| {
snapshot: SliceSnapshot;
@@ -1,4 +1,4 @@
import type { Blocks, DraftModel } from '../model/index.js';
import type { DraftModel, Store } from '../model/index.js';
type SliceData = {
content: DraftModel[];
@@ -21,7 +21,7 @@ export class Slice {
constructor(readonly data: SliceData) {}
static fromModels(doc: Blocks, models: DraftModel[]) {
static fromModels(doc: Store, models: DraftModel[]) {
return new Slice({
content: models,
workspaceId: doc.workspace.id,
@@ -1,6 +1,6 @@
import { z } from 'zod';
import type { Blocks } from '../model/blocks/blocks.js';
import type { Store } from '../model/store/store.js';
import type { DocMeta, DocsPropertiesMeta } from '../model/workspace-meta.js';
export type BlockSnapshot = {
@@ -75,7 +75,7 @@ export interface BlobCRUD {
}
export interface DocCRUD {
create: (id: string) => Blocks;
get: (id: string) => Blocks | null;
create: (id: string) => Store;
get: (id: string) => Store | null;
delete: (id: string) => void;
}