mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 05:42:30 +03:00
refactor: jwt internal version migration
This commit is contained in:
parent
eedb4864df
commit
6df2676c88
@ -12,7 +12,7 @@
|
||||
"file-selector": "^0.6.0",
|
||||
"flexsearch": "^0.7.21",
|
||||
"lib0": "^0.2.52",
|
||||
"lru-cache": "^7.13.2",
|
||||
"lru-cache": "^7.14.0",
|
||||
"ts-debounce": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -1,3 +1,10 @@
|
||||
import {
|
||||
BlockFlavorKeys,
|
||||
BlockFlavors,
|
||||
BlockTypeKeys,
|
||||
BlockTypes,
|
||||
} from '../types';
|
||||
import { getLogger } from '../utils';
|
||||
import {
|
||||
BlockInstance,
|
||||
BlockListener,
|
||||
@ -6,14 +13,7 @@ import {
|
||||
ContentTypes,
|
||||
HistoryManager,
|
||||
MapOperation,
|
||||
} from '../adapter';
|
||||
import {
|
||||
BlockFlavorKeys,
|
||||
BlockFlavors,
|
||||
BlockTypeKeys,
|
||||
BlockTypes,
|
||||
} from '../types';
|
||||
import { getLogger } from '../utils';
|
||||
} from '../yjs/types';
|
||||
|
||||
declare const JWT_DEV: boolean;
|
||||
const logger = getLogger('BlockDB:block');
|
||||
@ -29,10 +29,10 @@ export class AbstractBlock<
|
||||
private readonly _id: string;
|
||||
private readonly _block: BlockInstance<C>;
|
||||
private readonly _history: HistoryManager;
|
||||
private readonly _root?: AbstractBlock<B, C>;
|
||||
private readonly _root: AbstractBlock<B, C> | undefined;
|
||||
private readonly _parentListener: Map<string, BlockListener>;
|
||||
|
||||
private _parent?: AbstractBlock<B, C>;
|
||||
private _parent: AbstractBlock<B, C> | undefined;
|
||||
private _changeParent?: () => void;
|
||||
|
||||
constructor(
|
||||
@ -48,7 +48,9 @@ export class AbstractBlock<
|
||||
this._parentListener = new Map();
|
||||
|
||||
JWT_DEV && logger_debug(`init: exists ${this._id}`);
|
||||
if (parent) this._refreshParent(parent);
|
||||
if (parent) {
|
||||
this._refreshParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
public get root() {
|
||||
@ -146,7 +148,7 @@ export class AbstractBlock<
|
||||
return new Date(timestamp)
|
||||
.toISOString()
|
||||
.split('T')[0]
|
||||
.replace(/-/g, '');
|
||||
?.replace(/-/g, '');
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
@ -259,7 +261,7 @@ export class AbstractBlock<
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert sub-Block
|
||||
* Insert children block
|
||||
* @param block Block instance
|
||||
* @param position Insertion position, if it is empty, it will be inserted at the end. If the block already exists, the position will be moved
|
||||
* @returns
|
||||
@ -270,9 +272,12 @@ export class AbstractBlock<
|
||||
) {
|
||||
JWT_DEV && logger(`insertChildren: start`);
|
||||
|
||||
if (block.id === this._id) return; // avoid self-reference
|
||||
if (block.id === this._id) {
|
||||
// avoid self-reference
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.type !== BlockTypes.block || // binary cannot insert subblocks
|
||||
this.type !== BlockTypes.block || // binary cannot insert children blocks
|
||||
(block.type !== BlockTypes.block &&
|
||||
this.flavor !== BlockFlavors.workspace) // binary can only be inserted into workspace
|
||||
) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { BlockItem } from '../types';
|
||||
import { getLogger } from '../utils';
|
||||
import {
|
||||
ArrayOperation,
|
||||
BlockInstance,
|
||||
@ -5,9 +7,7 @@ import {
|
||||
ContentTypes,
|
||||
InternalPlainObject,
|
||||
MapOperation,
|
||||
} from '../adapter';
|
||||
import { BlockItem } from '../types';
|
||||
import { getLogger } from '../utils';
|
||||
} from '../yjs/types';
|
||||
|
||||
import { AbstractBlock } from './abstract';
|
||||
import { BlockCapability } from './capability';
|
||||
@ -23,8 +23,8 @@ export interface Decoration extends InternalPlainObject {
|
||||
type Validator = <T>(value: T | undefined) => boolean | void;
|
||||
|
||||
export type IndexMetadata = Readonly<{
|
||||
content?: string;
|
||||
reference?: string;
|
||||
content: string | undefined;
|
||||
reference: string | undefined;
|
||||
tags: string[];
|
||||
}>;
|
||||
export type QueryMetadata = Readonly<
|
||||
@ -51,7 +51,7 @@ export class BaseBlock<
|
||||
B extends BlockInstance<C>,
|
||||
C extends ContentOperation
|
||||
> extends AbstractBlock<B, C> {
|
||||
private readonly _exporters?: Exporters;
|
||||
private readonly _exporters: Exporters | undefined;
|
||||
private readonly _contentExportersGetter: () => Map<
|
||||
string,
|
||||
ReadableContentExporter<string, any>
|
||||
@ -68,7 +68,7 @@ export class BaseBlock<
|
||||
ReadableContentExporter<string[], any>
|
||||
>;
|
||||
|
||||
validators: Map<string, Validator> = new Map();
|
||||
private readonly _validators: Map<string, Validator> = new Map();
|
||||
|
||||
constructor(
|
||||
block: B,
|
||||
@ -157,14 +157,14 @@ export class BaseBlock<
|
||||
|
||||
setValidator(key: string, validator?: Validator) {
|
||||
if (validator) {
|
||||
this.validators.set(key, validator);
|
||||
this._validators.set(key, validator);
|
||||
} else {
|
||||
this.validators.delete(key);
|
||||
this._validators.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
private validate(key: string, value: unknown): boolean {
|
||||
const validate = this.validators.get(key);
|
||||
const validate = this._validators.get(key);
|
||||
if (validate) {
|
||||
return validate(value) === false ? false : true;
|
||||
}
|
||||
@ -179,7 +179,7 @@ export class BaseBlock<
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the child Block
|
||||
* Get an instance of the child block
|
||||
* @param blockId block id
|
||||
*/
|
||||
private get_children_instance(blockId?: string): BaseBlock<B, C>[] {
|
||||
@ -201,9 +201,15 @@ export class BaseBlock<
|
||||
}
|
||||
try {
|
||||
const parent_page = this._getParentPage(false);
|
||||
if (parent_page) metadata['page'] = parent_page;
|
||||
if (this.group) metadata['group'] = this.group.id;
|
||||
if (this.parent) metadata['parent'] = this.parent.id;
|
||||
if (parent_page) {
|
||||
metadata['page'] = parent_page;
|
||||
}
|
||||
if (this.group) {
|
||||
metadata['group'] = this.group.id;
|
||||
}
|
||||
if (this.parent) {
|
||||
metadata['parent'] = this.parent.id;
|
||||
}
|
||||
} catch (e) {
|
||||
logger(`Failed to export default metadata`, e);
|
||||
}
|
||||
@ -228,7 +234,9 @@ export class BaseBlock<
|
||||
for (const [name, exporter] of this._contentExportersGetter()) {
|
||||
try {
|
||||
const content = exporter(this.getContent());
|
||||
if (content) contents.push(content);
|
||||
if (content) {
|
||||
contents.push(content);
|
||||
}
|
||||
} catch (err) {
|
||||
logger(`Failed to export content: ${name}`, err);
|
||||
}
|
||||
|
@ -7,14 +7,14 @@ import produce from 'immer';
|
||||
import LRUCache from 'lru-cache';
|
||||
import sift, { Query } from 'sift';
|
||||
|
||||
import { BlockFlavors } from '../types';
|
||||
import { BlockEventBus, getLogger } from '../utils';
|
||||
import {
|
||||
AsyncDatabaseAdapter,
|
||||
BlockInstance,
|
||||
ChangedStates,
|
||||
ContentOperation,
|
||||
} from '../adapter';
|
||||
import { BlockFlavors } from '../types';
|
||||
import { BlockEventBus, getLogger } from '../utils';
|
||||
} from '../yjs/types';
|
||||
|
||||
import { BaseBlock, IndexMetadata, QueryMetadata } from './base';
|
||||
|
||||
@ -315,7 +315,9 @@ export class BlockIndexer<
|
||||
private _testMetaKey(key: string) {
|
||||
try {
|
||||
const metadata = this._blockMetadata.values().next().value;
|
||||
if (!metadata || typeof metadata !== 'object') return false;
|
||||
if (!metadata || typeof metadata !== 'object') {
|
||||
return false;
|
||||
}
|
||||
return !!(key in metadata);
|
||||
} catch (e) {
|
||||
return false;
|
||||
@ -324,8 +326,11 @@ export class BlockIndexer<
|
||||
|
||||
private _getSortedMetadata(sort: string, desc?: boolean) {
|
||||
const sorter = naturalSort(Array.from(this._blockMetadata.entries()));
|
||||
if (desc) return sorter.desc(([, m]) => m[sort]);
|
||||
else return sorter.asc(([, m]) => m[sort]);
|
||||
if (desc) {
|
||||
return sorter.desc(([, m]) => m[sort]);
|
||||
} else {
|
||||
return sorter.asc(([, m]) => m[sort]);
|
||||
}
|
||||
}
|
||||
|
||||
public query(query: QueryIndexMetadata) {
|
||||
@ -337,15 +342,23 @@ export class BlockIndexer<
|
||||
if ($sort && this._testMetaKey($sort)) {
|
||||
const metadata = this._getSortedMetadata($sort, $desc);
|
||||
metadata.forEach(([key, value]) => {
|
||||
if (matches.length > limit) return;
|
||||
if (filter(value)) matches.push(key);
|
||||
if (matches.length > limit) {
|
||||
return;
|
||||
}
|
||||
if (filter(value)) {
|
||||
matches.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
return matches;
|
||||
} else {
|
||||
this._blockMetadata.forEach((value, key) => {
|
||||
if (matches.length > limit) return;
|
||||
if (filter(value)) matches.push(key);
|
||||
if (matches.length > limit) {
|
||||
return;
|
||||
}
|
||||
if (filter(value)) {
|
||||
matches.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
return matches;
|
||||
|
@ -1,26 +1,7 @@
|
||||
/* eslint-disable max-lines */
|
||||
import { DocumentSearchOptions } from 'flexsearch';
|
||||
import LRUCache from 'lru-cache';
|
||||
import {
|
||||
AsyncDatabaseAdapter,
|
||||
BlockInstance,
|
||||
BlockListener,
|
||||
ChangedStates,
|
||||
Connectivity,
|
||||
ContentOperation,
|
||||
ContentTypes,
|
||||
DataExporter,
|
||||
getDataExporter,
|
||||
HistoryManager,
|
||||
YjsAdapter,
|
||||
YjsContentOperation,
|
||||
YjsInitOptions,
|
||||
} from './adapter';
|
||||
import {
|
||||
getYjsProviders,
|
||||
YjsBlockInstance,
|
||||
YjsProviderOptions,
|
||||
} from './adapter/yjs';
|
||||
|
||||
import {
|
||||
BaseBlock,
|
||||
BlockIndexer,
|
||||
@ -39,6 +20,26 @@ import {
|
||||
UUID,
|
||||
} from './types';
|
||||
import { BlockEventBus, genUUID, getLogger } from './utils';
|
||||
import {
|
||||
getYjsProviders,
|
||||
YjsAdapter,
|
||||
YjsBlockInstance,
|
||||
YjsContentOperation,
|
||||
YjsInitOptions,
|
||||
YjsProviderOptions,
|
||||
} from './yjs';
|
||||
import {
|
||||
AsyncDatabaseAdapter,
|
||||
BlockInstance,
|
||||
BlockListener,
|
||||
ChangedStates,
|
||||
Connectivity,
|
||||
ContentOperation,
|
||||
ContentTypes,
|
||||
DataExporter,
|
||||
getDataExporter,
|
||||
HistoryManager,
|
||||
} from './yjs/types';
|
||||
|
||||
declare const JWT_DEV: boolean;
|
||||
|
||||
@ -157,8 +158,11 @@ export class BlockClient<
|
||||
|
||||
public addBlockListener(tag: string, listener: BlockListener) {
|
||||
const bus = this._eventBus.topic<ChangedStates>('updated');
|
||||
if (tag !== 'index' || !bus.has(tag)) bus.on(tag, listener);
|
||||
else console.error(`block listener ${tag} is reserved or exists`);
|
||||
if (tag !== 'index' || !bus.has(tag)) {
|
||||
bus.on(tag, listener);
|
||||
} else {
|
||||
console.error(`block listener ${tag} is reserved or exists`);
|
||||
}
|
||||
}
|
||||
|
||||
public removeBlockListener(tag: string) {
|
||||
@ -170,8 +174,11 @@ export class BlockClient<
|
||||
listener: BlockListener<Set<string>>
|
||||
) {
|
||||
const bus = this._eventBus.topic<ChangedStates<Set<string>>>('editing');
|
||||
if (tag !== 'index' || !bus.has(tag)) bus.on(tag, listener);
|
||||
else console.error(`editing listener ${tag} is reserved or exists`);
|
||||
if (tag !== 'index' || !bus.has(tag)) {
|
||||
bus.on(tag, listener);
|
||||
} else {
|
||||
console.error(`editing listener ${tag} is reserved or exists`);
|
||||
}
|
||||
}
|
||||
|
||||
public removeEditingListener(tag: string) {
|
||||
@ -184,15 +191,18 @@ export class BlockClient<
|
||||
) {
|
||||
const bus =
|
||||
this._eventBus.topic<ChangedStates<Connectivity>>('connectivity');
|
||||
if (tag !== 'index' || !bus.has(tag)) bus.on(tag, listener);
|
||||
else
|
||||
if (tag !== 'index' || !bus.has(tag)) {
|
||||
bus.on(tag, listener);
|
||||
} else {
|
||||
console.error(`connectivity listener ${tag} is reserved or exists`);
|
||||
}
|
||||
}
|
||||
|
||||
public removeConnectivityListener(tag: string) {
|
||||
this._eventBus.topic('connectivity').off(tag);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
private inspector() {
|
||||
return {
|
||||
...this._adapter.inspector(),
|
||||
@ -220,7 +230,9 @@ export class BlockClient<
|
||||
this.addBlockListener('index', async states => {
|
||||
await Promise.allSettled(
|
||||
Array.from(states.entries()).map(([id, state]) => {
|
||||
if (state === 'delete') this._blockCaches.delete(id);
|
||||
if (state === 'delete') {
|
||||
this._blockCaches.delete(id);
|
||||
}
|
||||
return this._blockIndexer.refreshIndex(id, state);
|
||||
})
|
||||
);
|
||||
@ -255,7 +267,7 @@ export class BlockClient<
|
||||
}
|
||||
|
||||
/**
|
||||
* research all
|
||||
* Full text search
|
||||
* @param part_of_title_or_content Title or content keyword, support Chinese
|
||||
* @param part_of_title_or_content.index search range, optional values: title, ttl, content, reference
|
||||
* @param part_of_title_or_content.tag tag, string or array of strings, supports multiple tags
|
||||
@ -295,7 +307,9 @@ export class BlockClient<
|
||||
this.search(part_of_title_or_content).flatMap(({ result }) =>
|
||||
result.map(async id => {
|
||||
const page = this._pageMapping.get(id as string);
|
||||
if (page) return page;
|
||||
if (page) {
|
||||
return page;
|
||||
}
|
||||
const block = await this.get(id as BlockTypeKeys);
|
||||
return this.set_page(block);
|
||||
})
|
||||
@ -307,9 +321,10 @@ export class BlockClient<
|
||||
}
|
||||
return Promise.all(
|
||||
this._blockIndexer.getMetadata(pages).map(async page => ({
|
||||
content: this.get_decoded_content(
|
||||
await this._adapter.getBlock(page.id)
|
||||
),
|
||||
content:
|
||||
this.get_decoded_content(
|
||||
await this._adapter.getBlock(page.id)
|
||||
) || '',
|
||||
...page,
|
||||
}))
|
||||
);
|
||||
@ -327,9 +342,10 @@ export class BlockClient<
|
||||
const ids = this.query(query);
|
||||
return Promise.all(
|
||||
this._blockIndexer.getMetadata(ids).map(async page => ({
|
||||
content: this.get_decoded_content(
|
||||
await this._adapter.getBlock(page.id)
|
||||
),
|
||||
content:
|
||||
this.get_decoded_content(
|
||||
await this._adapter.getBlock(page.id)
|
||||
) || '',
|
||||
...page,
|
||||
}))
|
||||
);
|
||||
@ -378,7 +394,7 @@ export class BlockClient<
|
||||
|
||||
private async get_parent(id: string) {
|
||||
const parents = this._parentMapping.get(id);
|
||||
if (parents) {
|
||||
if (parents && parents[0]) {
|
||||
const parent_block_id = parents[0];
|
||||
if (!this._blockCaches.has(parent_block_id)) {
|
||||
this._blockCaches.set(
|
||||
@ -405,7 +421,9 @@ export class BlockClient<
|
||||
|
||||
private set_page(block: BaseBlock<B, C>) {
|
||||
const page = this._pageMapping.get(block.id);
|
||||
if (page) return page;
|
||||
if (page) {
|
||||
return page;
|
||||
}
|
||||
const parent_page = block.parent_page;
|
||||
if (parent_page) {
|
||||
this._pageMapping.set(block.id, parent_page);
|
||||
@ -472,8 +490,9 @@ export class BlockClient<
|
||||
matched += 1;
|
||||
}
|
||||
}
|
||||
if (matched === conditions.length)
|
||||
if (matched === conditions.length) {
|
||||
exporters.push([name, exporter] as const);
|
||||
}
|
||||
}
|
||||
|
||||
return exporters;
|
||||
@ -487,7 +506,9 @@ export class BlockClient<
|
||||
);
|
||||
if (exporter) {
|
||||
const op = block.content.asMap();
|
||||
if (op) return exporter[1](op);
|
||||
if (op) {
|
||||
return exporter[1](op);
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@ -566,7 +587,9 @@ export class BlockClient<
|
||||
this.set_page(abstract_block);
|
||||
abstract_block.on('parent', 'client_hook', state => {
|
||||
const [parent] = state.keys();
|
||||
this.set_parent(parent, abstract_block.id);
|
||||
if (parent) {
|
||||
this.set_parent(parent, abstract_block.id);
|
||||
}
|
||||
this.set_page(abstract_block);
|
||||
});
|
||||
this._blockCaches.set(abstract_block.id, abstract_block);
|
||||
@ -650,13 +673,6 @@ export type BlockInitOptions = NonNullable<
|
||||
Parameters<typeof BlockClient.init>[1]
|
||||
>;
|
||||
|
||||
export type {
|
||||
ArrayOperation,
|
||||
ChangedStates,
|
||||
Connectivity,
|
||||
MapOperation,
|
||||
TextOperation,
|
||||
} from './adapter';
|
||||
export type {
|
||||
BlockSearchItem,
|
||||
Decoration as BlockDecoration,
|
||||
@ -665,4 +681,11 @@ export type {
|
||||
export { BlockTypes, BucketBackend as BlockBackend } from './types';
|
||||
export type { BlockTypeKeys } from './types';
|
||||
export { isBlock } from './utils';
|
||||
export type {
|
||||
ArrayOperation,
|
||||
ChangedStates,
|
||||
Connectivity,
|
||||
MapOperation,
|
||||
TextOperation,
|
||||
} from './yjs/types';
|
||||
export type { QueryIndexMetadata };
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ContentOperation } from '../adapter';
|
||||
import { ContentOperation } from '../yjs/types';
|
||||
import { RefMetadata } from './metadata';
|
||||
import { UUID } from './uuid';
|
||||
// base type of block
|
||||
@ -60,8 +60,8 @@ export type BlockItem<C extends ContentOperation> = {
|
||||
flavor: typeof BlockFlavors[BlockFlavorKeys];
|
||||
children: string[];
|
||||
readonly created: number; // creation time, UTC timestamp
|
||||
readonly updated?: number; // update time, UTC timestamp
|
||||
readonly creator?: string; // creator id
|
||||
readonly updated?: number | undefined; // update time, UTC timestamp
|
||||
readonly creator?: string | undefined; // creator id
|
||||
content: C; // Essentially what is stored here is either Uint8Array (binary resource) or YDoc (structured resource)
|
||||
};
|
||||
|
||||
|
@ -81,7 +81,7 @@ class BlockScopedEventBus<T> extends BlockEventBus {
|
||||
options?: ListenerOptions
|
||||
) {
|
||||
if (options?.debounce) {
|
||||
const { wait, maxWait } = options.debounce;
|
||||
const { wait, maxWait = 500 } = options.debounce;
|
||||
const debounced = debounce(listener, wait, { maxWait });
|
||||
this.on_listener(this._topic, name, e => {
|
||||
debounced((e as CustomEvent)?.detail);
|
||||
|
@ -39,7 +39,9 @@ export function getLogger(namespace: string) {
|
||||
if (JWT_DEV) {
|
||||
const logger = debug(namespace);
|
||||
logger.log = console.log.bind(console);
|
||||
if (JWT_DEV === ('testing' as any)) logger.enabled = true;
|
||||
if (JWT_DEV === ('testing' as any)) {
|
||||
logger.enabled = true;
|
||||
}
|
||||
return logger;
|
||||
} else {
|
||||
return () => {};
|
||||
|
@ -25,7 +25,7 @@ export class YjsRemoteBinaries {
|
||||
} else {
|
||||
// TODO: Remote Load
|
||||
try {
|
||||
const file = await this._remoteStorage?.instance.getBuffData(
|
||||
const file = await this._remoteStorage?.instance?.getBuffData(
|
||||
name
|
||||
);
|
||||
console.log(file);
|
||||
@ -44,11 +44,11 @@ export class YjsRemoteBinaries {
|
||||
this._binaries.set(name, binary);
|
||||
if (this._remoteStorage) {
|
||||
// TODO: Remote Save, if there is an object with the same name remotely, the upload is skipped, because the file name is the hash of the file content
|
||||
const has_file = this._remoteStorage.instance.exist(name);
|
||||
const has_file = this._remoteStorage.instance?.exist(name);
|
||||
if (!has_file) {
|
||||
const upload_file = new File(binary.toArray(), name);
|
||||
await this._remoteStorage.instance
|
||||
.upload(upload_file)
|
||||
?.upload(upload_file)
|
||||
.catch(err => {
|
||||
throw new Error(`${err} upload error`);
|
||||
});
|
@ -5,8 +5,8 @@ import {
|
||||
transact,
|
||||
} from 'yjs';
|
||||
|
||||
import { BlockItem, BlockTypes } from '../../types';
|
||||
import { BlockInstance, BlockListener, HistoryManager } from '../index';
|
||||
import { BlockItem, BlockTypes } from '../types';
|
||||
import { BlockInstance, BlockListener, HistoryManager } from './types';
|
||||
|
||||
import { YjsHistoryManager } from './history';
|
||||
import { ChildrenListenerHandler, ContentListenerHandler } from './listener';
|
||||
@ -34,7 +34,7 @@ type YjsBlockInstanceProps = {
|
||||
export class YjsBlockInstance implements BlockInstance<YjsContentOperation> {
|
||||
private readonly _id: string;
|
||||
private readonly _block: YMap<unknown>;
|
||||
private readonly _binary?: YArray<ArrayBuffer>;
|
||||
private readonly _binary: YArray<ArrayBuffer> | undefined;
|
||||
private readonly _children: YArray<string>;
|
||||
private readonly _setBlock: (
|
||||
id: string,
|
||||
@ -48,8 +48,7 @@ export class YjsBlockInstance implements BlockInstance<YjsContentOperation> {
|
||||
private readonly _childrenListeners: Map<string, BlockListener>;
|
||||
private readonly _contentListeners: Map<string, BlockListener>;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
_childrenMap: Map<string, number>;
|
||||
private _childrenMap: Map<string, number>;
|
||||
|
||||
constructor(props: YjsBlockInstanceProps) {
|
||||
this._id = props.id;
|
||||
@ -184,7 +183,9 @@ export class YjsBlockInstance implements BlockInstance<YjsContentOperation> {
|
||||
}
|
||||
|
||||
hasChildren(id: string): boolean {
|
||||
if (this.children.includes(id)) return true;
|
||||
if (this.children.includes(id)) {
|
||||
return true;
|
||||
}
|
||||
return this.getChildren().some(block => block.hasChildren(id));
|
||||
}
|
||||
|
||||
@ -263,7 +264,9 @@ export class YjsBlockInstance implements BlockInstance<YjsContentOperation> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id) failed.push(id);
|
||||
if (id) {
|
||||
failed.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
this._childrenMap = getMapFromYArray(this._children);
|
@ -32,7 +32,7 @@ export class GateKeeper {
|
||||
return creator === this._userId || !!this._common.get(block_id);
|
||||
}
|
||||
|
||||
checkDeleteLists(block_ids: string[]) {
|
||||
checkDeleteLists(block_ids: string[]): [string[], string[]] {
|
||||
const success = [];
|
||||
const fail = [];
|
||||
for (const block_id of block_ids) {
|
@ -1,10 +1,11 @@
|
||||
import { Map as YMap, UndoManager } from 'yjs';
|
||||
|
||||
import { HistoryCallback, HistoryManager } from '../../adapter';
|
||||
import { HistoryCallback, HistoryManager } from './types';
|
||||
|
||||
type StackItem = UndoManager['undoStack'][0];
|
||||
|
||||
export class YjsHistoryManager implements HistoryManager {
|
||||
// @ts-ignore
|
||||
private readonly _blocks: YMap<any>;
|
||||
private readonly _historyManager: UndoManager;
|
||||
private readonly _pushListeners: Map<string, HistoryCallback<any>>;
|
||||
@ -56,7 +57,7 @@ export class YjsHistoryManager implements HistoryManager {
|
||||
}
|
||||
|
||||
break(): void {
|
||||
// this.#history_manager.
|
||||
// this._historyManager.
|
||||
}
|
||||
|
||||
undo<T = unknown>(): Map<string, T> | undefined {
|
@ -18,15 +18,15 @@ import {
|
||||
transact,
|
||||
} from 'yjs';
|
||||
|
||||
import { BlockItem, BlockTypes } from '../types';
|
||||
import { getLogger, sha3, sleep } from '../utils';
|
||||
import {
|
||||
AsyncDatabaseAdapter,
|
||||
BlockListener,
|
||||
ChangedStateKeys,
|
||||
Connectivity,
|
||||
HistoryManager,
|
||||
} from '../../adapter';
|
||||
import { BlockItem, BlockTypes } from '../../types';
|
||||
import { getLogger, sha3, sleep } from '../../utils';
|
||||
} from './types';
|
||||
|
||||
import { YjsRemoteBinaries } from './binary';
|
||||
import { YjsBlockInstance } from './block';
|
||||
@ -40,6 +40,7 @@ import {
|
||||
import { YjsProvider } from './provider';
|
||||
|
||||
declare const JWT_DEV: boolean;
|
||||
// @ts-ignore
|
||||
const logger = getLogger('BlockDB:yjs');
|
||||
|
||||
type ConnectivityListener = (
|
||||
@ -54,7 +55,7 @@ type YjsProviders = {
|
||||
gatekeeper: GateKeeper;
|
||||
connListener: { listeners?: ConnectivityListener };
|
||||
userId: string;
|
||||
remoteToken?: string; // remote storage token
|
||||
remoteToken: string | undefined; // remote storage token
|
||||
};
|
||||
|
||||
const _yjsDatabaseInstance = new Map<string, YjsProviders>();
|
||||
@ -70,8 +71,8 @@ async function _initYjsDatabase(
|
||||
workspace: string,
|
||||
options: {
|
||||
userId: string;
|
||||
token?: string;
|
||||
provider?: Record<string, YjsProvider>;
|
||||
token?: string | undefined;
|
||||
provider?: Record<string, YjsProvider> | undefined;
|
||||
}
|
||||
): Promise<YjsProviders> {
|
||||
if (_asyncInitLoading.has(workspace)) {
|
||||
@ -243,9 +244,10 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
||||
typeof updated === 'number' &&
|
||||
updated + 1000 * 10 > Date.now()
|
||||
) {
|
||||
if (!editing_mapping[editing])
|
||||
if (!editing_mapping[editing]) {
|
||||
editing_mapping[editing] = [];
|
||||
editing_mapping[editing].push(userId);
|
||||
}
|
||||
editing_mapping[editing]?.push(userId);
|
||||
}
|
||||
}
|
||||
listener(
|
||||
@ -338,7 +340,7 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
||||
],
|
||||
});
|
||||
const [file] = (await fromEvent(handles)) as File[];
|
||||
const binary = await file.arrayBuffer();
|
||||
const binary = await file?.arrayBuffer();
|
||||
// await this._provider.idb.clearData();
|
||||
const doc = new Doc({ autoLoad: true, shouldLoad: true });
|
||||
let updated = 0;
|
||||
@ -348,7 +350,9 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
||||
updated += 1;
|
||||
});
|
||||
setInterval(() => {
|
||||
if (updated > 0) updated -= 1;
|
||||
if (updated > 0) {
|
||||
updated -= 1;
|
||||
}
|
||||
}, 500);
|
||||
|
||||
const update_check = new Promise<void>(resolve => {
|
||||
@ -362,8 +366,10 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
||||
});
|
||||
// await new IndexedDBProvider(this._provider.idb.name, doc)
|
||||
// .whenSynced;
|
||||
applyUpdate(doc, new Uint8Array(binary));
|
||||
await update_check;
|
||||
if (binary) {
|
||||
applyUpdate(doc, new Uint8Array(binary));
|
||||
await update_check;
|
||||
}
|
||||
console.log('load success');
|
||||
},
|
||||
parse: () => this._doc.toJSON(),
|
||||
@ -409,8 +415,8 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
||||
|
||||
async createBlock(
|
||||
options: Pick<BlockItem<YjsContentOperation>, 'type' | 'flavor'> & {
|
||||
uuid?: string;
|
||||
binary?: ArrayBufferLike;
|
||||
uuid: string | undefined;
|
||||
binary: ArrayBufferLike | undefined;
|
||||
}
|
||||
): Promise<YjsBlockInstance> {
|
||||
const uuid = options.uuid || `affine${nanoid(16)}`;
|
||||
@ -481,7 +487,9 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
||||
|
||||
async getBlock(id: string): Promise<YjsBlockInstance | undefined> {
|
||||
const block_instance = this.get_block_sync(id);
|
||||
if (block_instance) return block_instance;
|
||||
if (block_instance) {
|
||||
return block_instance;
|
||||
}
|
||||
const block = this._blocks.get(id);
|
||||
if (block && block.get('type') === BlockTypes.binary) {
|
||||
const binary = await this._binaries.get(
|
||||
@ -540,7 +548,9 @@ export class YjsAdapter implements AsyncDatabaseAdapter<YjsContentOperation> {
|
||||
let uploaded: Promise<void> | undefined;
|
||||
if (!block.size) {
|
||||
const content = item.content[INTO_INNER]();
|
||||
if (!content) return reject();
|
||||
if (!content) {
|
||||
return reject();
|
||||
}
|
||||
|
||||
const children = new YArray();
|
||||
children.push(item.children);
|
@ -1,7 +1,8 @@
|
||||
import { produce } from 'immer';
|
||||
import { debounce } from 'ts-debounce';
|
||||
import { YEvent } from 'yjs';
|
||||
import { BlockListener, ChangedStateKeys } from '../index';
|
||||
|
||||
import { BlockListener, ChangedStateKeys } from './types';
|
||||
|
||||
let listener_suspend = false;
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
Text as YText,
|
||||
} from 'yjs';
|
||||
|
||||
import { ChildrenListenerHandler, ContentListenerHandler } from './listener';
|
||||
import {
|
||||
ArrayOperation,
|
||||
BaseTypes,
|
||||
@ -16,21 +17,29 @@ import {
|
||||
Operable,
|
||||
TextOperation,
|
||||
TextToken,
|
||||
} from '../index';
|
||||
import { ChildrenListenerHandler, ContentListenerHandler } from './listener';
|
||||
} from './types';
|
||||
|
||||
const INTO_INNER = Symbol('INTO_INNER');
|
||||
|
||||
export const DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED_SYMBOL_INTO_INNER: typeof INTO_INNER =
|
||||
INTO_INNER;
|
||||
|
||||
function auto_get(root: ContentOperation, key: string): unknown | undefined {
|
||||
function auto_get(
|
||||
root: ContentOperation,
|
||||
key: string | undefined
|
||||
): unknown | undefined {
|
||||
const array = root.asArray();
|
||||
if (array && !Number.isNaN(Number(key))) return array.get(Number(key));
|
||||
if (array && !Number.isNaN(Number(key))) {
|
||||
return array.get(Number(key));
|
||||
}
|
||||
const map = root.asMap();
|
||||
if (map) return map.get(key);
|
||||
if (map && key) {
|
||||
return map.get(key);
|
||||
}
|
||||
const text = root.asText();
|
||||
if (text) return text.toString();
|
||||
if (text) {
|
||||
return text.toString();
|
||||
}
|
||||
console.error('auto_get unknown root', root, key);
|
||||
return undefined;
|
||||
}
|
||||
@ -150,7 +159,9 @@ export class YjsContentOperation implements ContentOperation {
|
||||
if (root instanceof YjsContentOperation) {
|
||||
if (path.length === 1) {
|
||||
const [key] = path;
|
||||
if (key) return auto_set(root, key, data);
|
||||
if (key) {
|
||||
return auto_set(root, key, data);
|
||||
}
|
||||
console.error('autoSet unknown path', root, path, data);
|
||||
return;
|
||||
}
|
||||
@ -191,6 +202,7 @@ export class YjsContentOperation implements ContentOperation {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
// @ts-ignore
|
||||
private toJSON() {
|
||||
return this._content.toJSON();
|
||||
}
|
||||
@ -283,7 +295,9 @@ class YjsArrayOperation<T extends ContentTypes>
|
||||
|
||||
get(index: number): Operable<T> | undefined {
|
||||
const content = this._arrayContent.get(index);
|
||||
if (content) return this.to_operable(content);
|
||||
if (content) {
|
||||
return this.to_operable(content);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -368,7 +382,9 @@ class YjsMapOperation<T extends ContentTypes>
|
||||
set(key: string, value: Operable<T>): void {
|
||||
if (value instanceof YjsContentOperation) {
|
||||
const content = value[INTO_INNER]();
|
||||
if (content) this._mapContent.set(key, content as unknown as T);
|
||||
if (content) {
|
||||
this._mapContent.set(key, content as unknown as T);
|
||||
}
|
||||
} else {
|
||||
this._mapContent.set(key, value as T);
|
||||
}
|
||||
@ -376,7 +392,9 @@ class YjsMapOperation<T extends ContentTypes>
|
||||
|
||||
get(key: string): Operable<T> | undefined {
|
||||
const content = this._mapContent.get(key);
|
||||
if (content) return this.to_operable(content);
|
||||
if (content) {
|
||||
return this.to_operable(content);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
@ -7,13 +7,13 @@ import {
|
||||
WebsocketProvider,
|
||||
} from '@toeverything/datasource/jwt-rpc';
|
||||
|
||||
import { Connectivity } from '../../adapter';
|
||||
import { BucketBackend } from '../../types';
|
||||
import { BucketBackend } from '../types';
|
||||
import { Connectivity } from './types';
|
||||
|
||||
type YjsDefaultInstances = {
|
||||
awareness: Awareness;
|
||||
doc: Doc;
|
||||
token?: string;
|
||||
token?: string | undefined;
|
||||
workspace: string;
|
||||
emitState: (connectivity: Connectivity) => void;
|
||||
};
|
@ -139,8 +139,8 @@ interface AsyncDatabaseAdapter<C extends ContentOperation> {
|
||||
reload(): void;
|
||||
createBlock(
|
||||
options: Pick<BlockItem<C>, 'type' | 'flavor'> & {
|
||||
binary?: ArrayBuffer;
|
||||
uuid?: string;
|
||||
binary: ArrayBuffer | undefined;
|
||||
uuid: string | undefined;
|
||||
}
|
||||
): Promise<BlockInstance<C>>;
|
||||
getBlock(id: string): Promise<BlockInstance<C> | undefined>;
|
||||
@ -184,8 +184,6 @@ export const getDataExporter = () => {
|
||||
return { importData, exportData, hasExporter, installExporter };
|
||||
};
|
||||
|
||||
export { YjsAdapter } from './yjs';
|
||||
export type { YjsContentOperation, YjsInitOptions } from './yjs';
|
||||
export type {
|
||||
AsyncDatabaseAdapter,
|
||||
BlockPosition,
|
@ -184,7 +184,7 @@ importers:
|
||||
yjs: ^13.5.41
|
||||
dependencies:
|
||||
authing-js-sdk: 4.23.35
|
||||
firebase-admin: 11.0.1_@firebase+app-types@0.7.0
|
||||
firebase-admin: 11.0.1
|
||||
lib0: 0.2.52
|
||||
lru-cache: 7.13.2
|
||||
nanoid: 4.0.0
|
||||
@ -566,6 +566,9 @@ importers:
|
||||
dependencies:
|
||||
ffc-js-client-side-sdk: 1.1.5
|
||||
|
||||
libs/datasource/jwst/pkg:
|
||||
specifiers: {}
|
||||
|
||||
libs/datasource/jwt:
|
||||
specifiers:
|
||||
'@types/debug': ^4.1.7
|
||||
@ -583,7 +586,7 @@ importers:
|
||||
idb-keyval: ^6.2.0
|
||||
immer: ^9.0.15
|
||||
lib0: ^0.2.52
|
||||
lru-cache: ^7.13.2
|
||||
lru-cache: ^7.14.0
|
||||
nanoid: ^4.0.0
|
||||
sha3: ^2.1.4
|
||||
sift: ^16.0.0
|
||||
@ -614,7 +617,7 @@ importers:
|
||||
file-selector: 0.6.0
|
||||
flexsearch: 0.7.21
|
||||
lib0: 0.2.52
|
||||
lru-cache: 7.13.2
|
||||
lru-cache: 7.14.0
|
||||
ts-debounce: 4.0.0
|
||||
|
||||
libs/datasource/jwt-rpc:
|
||||
@ -3180,6 +3183,15 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/@firebase/auth-interop-types/0.1.6_@firebase+util@1.6.3:
|
||||
resolution: {integrity: sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==}
|
||||
peerDependencies:
|
||||
'@firebase/app-types': 0.x
|
||||
'@firebase/util': 1.x
|
||||
dependencies:
|
||||
'@firebase/util': 1.6.3
|
||||
dev: false
|
||||
|
||||
/@firebase/auth-interop-types/0.1.6_pbfwexsq7uf6mrzcwnikj3g37m:
|
||||
resolution: {integrity: sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==}
|
||||
peerDependencies:
|
||||
@ -3188,6 +3200,7 @@ packages:
|
||||
dependencies:
|
||||
'@firebase/app-types': 0.7.0
|
||||
'@firebase/util': 1.6.3
|
||||
dev: true
|
||||
|
||||
/@firebase/auth-types/0.11.0_pbfwexsq7uf6mrzcwnikj3g37m:
|
||||
resolution: {integrity: sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw==}
|
||||
@ -3223,6 +3236,19 @@ packages:
|
||||
'@firebase/util': 1.6.3
|
||||
tslib: 2.4.0
|
||||
|
||||
/@firebase/database-compat/0.2.4:
|
||||
resolution: {integrity: sha512-VtsGixO5mTjNMJn6PwxAJEAR70fj+3blCXIdQKel3q+eYGZAfdqxox1+tzZDnf9NWBJpaOgAHPk3JVDxEo9NFQ==}
|
||||
dependencies:
|
||||
'@firebase/component': 0.5.17
|
||||
'@firebase/database': 0.13.4
|
||||
'@firebase/database-types': 0.9.12
|
||||
'@firebase/logger': 0.3.3
|
||||
'@firebase/util': 1.6.3
|
||||
tslib: 2.4.0
|
||||
transitivePeerDependencies:
|
||||
- '@firebase/app-types'
|
||||
dev: false
|
||||
|
||||
/@firebase/database-compat/0.2.4_@firebase+app-types@0.7.0:
|
||||
resolution: {integrity: sha512-VtsGixO5mTjNMJn6PwxAJEAR70fj+3blCXIdQKel3q+eYGZAfdqxox1+tzZDnf9NWBJpaOgAHPk3JVDxEo9NFQ==}
|
||||
dependencies:
|
||||
@ -3234,6 +3260,7 @@ packages:
|
||||
tslib: 2.4.0
|
||||
transitivePeerDependencies:
|
||||
- '@firebase/app-types'
|
||||
dev: true
|
||||
|
||||
/@firebase/database-types/0.9.10:
|
||||
resolution: {integrity: sha512-2ji6nXRRsY+7hgU6zRhUtK0RmSjVWM71taI7Flgaw+BnopCo/lDF5HSwxp8z7LtiHlvQqeRA3Ozqx5VhlAbiKg==}
|
||||
@ -3248,6 +3275,19 @@ packages:
|
||||
'@firebase/app-types': 0.7.0
|
||||
'@firebase/util': 1.6.3
|
||||
|
||||
/@firebase/database/0.13.4:
|
||||
resolution: {integrity: sha512-NW7bOoiaC4sJCj6DY/m9xHoFNa0CK32YPMCh6FiMweLCDQbOZM8Ql/Kn6yyuxCb7K7ypz9eSbRlCWQJsJRQjhg==}
|
||||
dependencies:
|
||||
'@firebase/auth-interop-types': 0.1.6_@firebase+util@1.6.3
|
||||
'@firebase/component': 0.5.17
|
||||
'@firebase/logger': 0.3.3
|
||||
'@firebase/util': 1.6.3
|
||||
faye-websocket: 0.11.4
|
||||
tslib: 2.4.0
|
||||
transitivePeerDependencies:
|
||||
- '@firebase/app-types'
|
||||
dev: false
|
||||
|
||||
/@firebase/database/0.13.4_@firebase+app-types@0.7.0:
|
||||
resolution: {integrity: sha512-NW7bOoiaC4sJCj6DY/m9xHoFNa0CK32YPMCh6FiMweLCDQbOZM8Ql/Kn6yyuxCb7K7ypz9eSbRlCWQJsJRQjhg==}
|
||||
dependencies:
|
||||
@ -3259,6 +3299,7 @@ packages:
|
||||
tslib: 2.4.0
|
||||
transitivePeerDependencies:
|
||||
- '@firebase/app-types'
|
||||
dev: true
|
||||
|
||||
/@firebase/firestore-compat/0.1.23_53yvy43rwpg2c45kgeszsxtrca:
|
||||
resolution: {integrity: sha512-QfcuyMAavp//fQnjSfCEpnbWi7spIdKaXys1kOLu7395fLr+U6ykmto1HUMCSz8Yus9cEr/03Ujdi2SUl2GUAA==}
|
||||
@ -10332,12 +10373,12 @@ packages:
|
||||
semver-regex: 2.0.0
|
||||
dev: true
|
||||
|
||||
/firebase-admin/11.0.1_@firebase+app-types@0.7.0:
|
||||
/firebase-admin/11.0.1:
|
||||
resolution: {integrity: sha512-rL3wlZbi2Kb/KJgcmj1YHlD4ZhfmhfgRO2YJialxAllm0tj1IQea878hHuBLGmv4DpbW9t9nLvX9kddNR2Y65Q==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
'@fastify/busboy': 1.1.0
|
||||
'@firebase/database-compat': 0.2.4_@firebase+app-types@0.7.0
|
||||
'@firebase/database-compat': 0.2.4
|
||||
'@firebase/database-types': 0.9.10
|
||||
'@types/node': 18.0.1
|
||||
jsonwebtoken: 8.5.1
|
||||
@ -13573,6 +13614,12 @@ packages:
|
||||
/lru-cache/7.13.2:
|
||||
resolution: {integrity: sha512-VJL3nIpA79TodY/ctmZEfhASgqekbT574/c4j3jn4bKXbSCnTTCH/KltZyvL2GlV+tGSMtsWyem8DCX7qKTMBA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/lru-cache/7.14.0:
|
||||
resolution: {integrity: sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/lru-memoizer/2.1.4:
|
||||
resolution: {integrity: sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==}
|
||||
|
Loading…
Reference in New Issue
Block a user