Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@blocksuite/store

Package Overview
Dependencies
Maintainers
5
Versions
1249
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@blocksuite/store - npm Package Compare versions

Comparing version 0.3.0-alpha.18 to 0.3.0-alpha.19

dist/providers/indexeddb.d.ts

27

dist/blob/providers.js

@@ -8,6 +8,5 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {

var IndexedDBBlobProvider_1;
import { Buffer } from 'buffer';
import ky from 'ky';
import { Signal } from '../utils/signal.js';
import { getDatabase, sha3, sleep } from './utils.js';
import { getDatabase, sha, sleep } from './utils.js';
const RETRY_TIMEOUT = 500;

@@ -18,13 +17,2 @@ function staticImplements() {

let IndexedDBBlobProvider = IndexedDBBlobProvider_1 = class IndexedDBBlobProvider {
constructor(workspace, cloudApi) {
this.blobs = new Set();
this.signals = {
blobAdded: new Signal(),
blobDeleted: new Signal(),
};
this._database = getDatabase('blob', workspace);
if (cloudApi) {
this._cloud = new BlobCloudSync(workspace, cloudApi, this._database);
}
}
static async init(workspace, cloudApi) {

@@ -43,2 +31,13 @@ const provider = new IndexedDBBlobProvider_1(workspace, cloudApi);

}
constructor(workspace, cloudApi) {
this.blobs = new Set();
this.signals = {
blobAdded: new Signal(),
blobDeleted: new Signal(),
};
this._database = getDatabase('blob', workspace);
if (cloudApi) {
this._cloud = new BlobCloudSync(workspace, cloudApi, this._database);
}
}
async get(id) {

@@ -60,3 +59,3 @@ const blob = await this._database.get(id);

const buffer = await blob.arrayBuffer();
const hash = sha3(Buffer.from(buffer));
const hash = await sha(buffer);
if (!(await this._database.has(hash))) {

@@ -63,0 +62,0 @@ await this._database.set(hash, buffer);

import type { Signal } from '../utils/signal.js';
export declare type BlobId = string;
export declare type BlobURL = string;
export type BlobId = string;
export type BlobURL = string;
export interface BlobProvider {

@@ -14,3 +14,3 @@ blobs: Set<BlobId>;

}
export declare type IDBInstance<T = ArrayBufferLike> = {
export type IDBInstance<T = ArrayBufferLike> = {
get: (key: BlobId) => Promise<T | undefined>;

@@ -17,0 +17,0 @@ set: (key: BlobId, value: T) => Promise<void>;

@@ -1,7 +0,5 @@

/// <reference types="node" resolution-mode="require"/>
import type { Buffer } from 'buffer';
import type { IDBInstance } from './types.js';
export declare function sha3(buffer: Buffer): string;
export declare function sha(input: ArrayBuffer): Promise<string>;
export declare function getDatabase<T = ArrayBufferLike>(type: string, database: string): IDBInstance<T>;
export declare function sleep(ms: number): Promise<void>;
//# sourceMappingURL=utils.d.ts.map
import { createStore, del, get, keys, set, clear } from 'idb-keyval';
import { SHA3 } from 'sha3';
const hash = new SHA3(256);
export function sha3(buffer) {
hash.reset();
hash.update(buffer);
return hash
.digest('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
import { Buffer } from 'buffer';
export async function sha(input) {
const hash = await crypto.subtle.digest('SHA-256', input);
const buffer = Buffer.from(hash);
return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
}

@@ -13,0 +8,0 @@ export function getDatabase(type, database) {

import type * as Y from 'yjs';
import { WebrtcProvider } from 'y-webrtc';
import { IndexeddbPersistence } from 'y-indexeddb';
import { IndexeddbPersistence } from './providers/indexeddb.js';
import type { Awareness } from 'y-protocols/awareness';

@@ -5,0 +5,0 @@ /**

// @ts-ignore
import { WebrtcProvider } from 'y-webrtc';
import { IndexeddbPersistence } from 'y-indexeddb';
import { IndexeddbPersistence } from './providers/indexeddb.js';
// When using playground from blocksuite repo, this comes from "serve" script in "@blocksuite/store" package.

@@ -14,3 +14,5 @@ // We use our own sync server because a local service for sync makes everything much faster for dev.

const isWeb = typeof window !== 'undefined';
const isLocalhost = isWeb && window.location.hostname === 'localhost';
const isLocalhost = isWeb &&
(window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1');
const signaling = isLocalhost ? LOCAL_SIGNALING : DEFAULT_SIGNALING;

@@ -17,0 +19,0 @@ export class DebugDocProvider extends WebrtcProvider {

@@ -5,2 +5,4 @@ import type * as Y from 'yjs';

import type { RichTextAdapter } from './text-adapter.js';
import type { DataInitializer } from './yjs/proxy.js';
import type { BlockSuiteDoc } from './yjs/index.js';
export interface StackItem {

@@ -10,10 +12,20 @@ meta: Map<'cursor-location', SelectionRange | undefined>;

}
export declare class Space {
export declare class Space<Data extends Record<string, unknown> = Record<string, any>> {
/** unprefixed id */
readonly id: string;
readonly doc: Y.Doc;
readonly doc: BlockSuiteDoc;
/**
* @internal
* @protected
*/
protected readonly proxy: Data;
protected readonly origin: Y.Map<Data[keyof Data]>;
readonly awareness: AwarenessAdapter;
readonly richTextAdapters: Map<string, RichTextAdapter>;
constructor(id: string, doc: Y.Doc, awareness: Awareness);
constructor(id: string, doc: BlockSuiteDoc, awareness: Awareness, options?: {
valueInitializer?: DataInitializer<Partial<Data>>;
});
get prefixedId(): string;
transact(fn: () => void): void;
}
//# sourceMappingURL=space.d.ts.map
import { Awareness } from 'y-protocols/awareness.js';
import { AwarenessAdapter } from './awareness.js';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class Space {
constructor(id, doc, awareness) {
constructor(id, doc, awareness, options) {
this.richTextAdapters = new Map();
this.id = id;
this.doc = doc;
const targetId = this.id.startsWith('space:') ? this.id : this.prefixedId;
this.origin = this.doc.getMap(targetId);
this.proxy = this.doc.getMapProxy(targetId, {
initializer: options?.valueInitializer,
});
const aware = awareness ?? new Awareness(this.doc);
this.awareness = new AwarenessAdapter(this, aware);
}
get prefixedId() {
return `space:${this.id}`;
}
transact(fn) {

@@ -12,0 +21,0 @@ this.doc.transact(fn, this.doc.clientID);

import type { Space } from './space.js';
import type { IdGenerator } from './utils/id-generator.js';
import { Awareness } from 'y-protocols/awareness.js';
import * as Y from 'yjs';
import type { DocProvider, DocProviderConstructor } from './doc-providers.js';
import { BlockSuiteDoc } from './yjs/index.js';
export interface SerializedStore {

@@ -29,3 +29,12 @@ [key: string]: {

}
export interface StoreOptions {
/**
* @example
* const workspace = new Workspace({
* isSSR: typeof window === 'undefined'
* })
*/
export interface SSROptions {
isSSR?: boolean;
}
export interface StoreOptions extends SSROptions {
room?: string;

@@ -37,5 +46,5 @@ providers?: DocProviderConstructor[];

export declare class Store {
readonly doc: Y.Doc;
readonly doc: BlockSuiteDoc<import("./yjs/index.js").BlockSuiteDocData>;
readonly providers: DocProvider[];
readonly spaces: Map<string, Space>;
readonly spaces: Map<string, Space<Record<string, any>>>;
readonly awareness: Awareness;

@@ -42,0 +51,0 @@ readonly idGenerator: IdGenerator;

import { Awareness } from 'y-protocols/awareness.js';
import * as Y from 'yjs';
import { serializeYDoc, yDocToJSXNode } from './utils/jsx.js';
import { createAutoIncrementIdGenerator, createAutoIncrementIdGeneratorByClientId, uuidv4, } from './utils/id-generator.js';
import { BlockSuiteDoc } from './yjs/index.js';
export var Generator;

@@ -28,3 +28,3 @@ (function (Generator) {

constructor({ room = DEFAULT_ROOM, providers = [], awareness, idGenerator, } = {}) {
this.doc = new Y.Doc();
this.doc = new BlockSuiteDoc();
this.providers = [];

@@ -51,6 +51,6 @@ this.spaces = new Map();

addSpace(space) {
this.spaces.set(space.id, space);
this.spaces.set(space.prefixedId, space);
}
removeSpace(space) {
this.spaces.delete(space.id);
this.spaces.delete(space.prefixedId);
}

@@ -57,0 +57,0 @@ /**

@@ -5,4 +5,4 @@ import * as Y from 'yjs';

import type { Space } from './space.js';
declare type PrelimTextType = 'splitLeft' | 'splitRight';
export declare type TextType = PrelimText | Text;
type PrelimTextType = 'splitLeft' | 'splitRight';
export type TextType = PrelimText | Text;
export declare function normQuillDelta(delta: any): any;

@@ -9,0 +9,0 @@ export declare class PrelimText {

export interface Disposable {
dispose(): void;
}
export declare function flattenDisposable(a: Disposable[]): Disposable;
type DisposeLogic = Disposable | (() => void);
export declare class DisposableGroup implements Disposable {
private _disposables;
private _disposed;
get disposed(): boolean;
constructor(_disposables?: DisposeLogic[]);
/**
* Add to group to be disposed with others.
*
* This will be immediately disposed if this group has already been disposed.
*/
add(disposable: DisposeLogic | undefined | null | false): void;
dispose(): void;
}
export declare function flattenDisposable(disposables: Disposable[]): Disposable;
export {};
//# sourceMappingURL=disposable.d.ts.map

@@ -1,16 +0,51 @@

export function flattenDisposable(a) {
export class DisposableGroup {
get disposed() {
return this._disposed;
}
constructor(_disposables = []) {
this._disposables = _disposables;
this._disposed = false;
}
/**
* Add to group to be disposed with others.
*
* This will be immediately disposed if this group has already been disposed.
*/
add(disposable) {
if (disposable) {
if (this._disposed)
execDisposeLogic(disposable);
else
this._disposables.push(disposable);
}
}
dispose() {
disposeAllAndClearArray(this._disposables);
this._disposed = true;
}
}
export function flattenDisposable(disposables) {
return {
dispose: () => {
a.forEach(d => {
try {
d.dispose();
}
catch (err) {
console.error(err);
}
});
a.length = 0;
},
dispose: disposeAllAndClearArray.bind(null, disposables),
};
}
/** @internal */
function disposeAllAndClearArray(disposables) {
for (const disposable of disposables) {
try {
execDisposeLogic(disposable);
}
catch (err) {
console.error(err);
}
}
disposables.length = 0;
}
/** @internal */
function execDisposeLogic(disposable) {
if (typeof disposable === 'function')
disposable();
else
disposable.dispose();
}
//# sourceMappingURL=disposable.js.map

@@ -1,2 +0,2 @@

export declare type IdGenerator = () => string;
export type IdGenerator = () => string;
export declare function createAutoIncrementIdGenerator(): IdGenerator;

@@ -3,0 +3,0 @@ export declare function createAutoIncrementIdGeneratorByClientId(clientId: number): IdGenerator;

import { Disposable } from './disposable.js';
export declare class Signal<T = void> implements Disposable {
private emitting;
private callbacks;
private disposables;
static fromEvent<N extends keyof HTMLElementEventMap>(element: HTMLElement | Window, eventName: N, eventOptions?: boolean | AddEventListenerOptions): Signal<HTMLElementEventMap[N]>;
private _emitting;
private _callbacks;
private _disposables;
static fromEvent<N extends keyof WindowEventMap>(element: Window, eventName: N, options?: boolean | AddEventListenerOptions): Signal<WindowEventMap[N]>;
static fromEvent<N extends keyof HTMLElementEventMap>(element: HTMLElement, eventName: N, eventOptions?: boolean | AddEventListenerOptions): Signal<HTMLElementEventMap[N]>;
filter(testFun: (v: T) => boolean): Signal<T>;

@@ -8,0 +9,0 @@ on(callback: (v: T) => unknown): Disposable;

@@ -6,5 +6,5 @@ import { flattenDisposable } from './disposable.js';

constructor() {
this.emitting = false;
this.callbacks = [];
this.disposables = [];
this._emitting = false;
this._callbacks = [];
this._disposables = [];
}

@@ -17,3 +17,3 @@ static fromEvent(element, eventName, eventOptions) {

element.addEventListener(eventName, handler, eventOptions);
signal.disposables.push({
signal._disposables.push({
dispose: () => {

@@ -28,3 +28,3 @@ element.removeEventListener(eventName, handler);

// if result is disposed, dispose this too
result.disposables.push({ dispose: () => this.dispose() });
result._disposables.push({ dispose: () => this.dispose() });
this.on((v) => {

@@ -38,18 +38,18 @@ if (testFun(v)) {

on(callback) {
if (this.emitting) {
const newCallback = [...this.callbacks, callback];
this.callbacks = newCallback;
if (this._emitting) {
const newCallback = [...this._callbacks, callback];
this._callbacks = newCallback;
}
else {
this.callbacks.push(callback);
this._callbacks.push(callback);
}
return {
dispose: () => {
if (this.emitting) {
this.callbacks = this.callbacks.filter(v => v !== callback);
if (this._emitting) {
this._callbacks = this._callbacks.filter(v => v !== callback);
}
else {
const index = this.callbacks.indexOf(callback);
const index = this._callbacks.indexOf(callback);
if (index > -1) {
this.callbacks.splice(index, 1); // remove one item only
this._callbacks.splice(index, 1); // remove one item only
}

@@ -71,18 +71,18 @@ }

unshift(callback) {
if (this.emitting) {
const newCallback = [callback, ...this.callbacks];
this.callbacks = newCallback;
if (this._emitting) {
const newCallback = [callback, ...this._callbacks];
this._callbacks = newCallback;
}
else {
this.callbacks.unshift(callback);
this._callbacks.unshift(callback);
}
return {
dispose: () => {
if (this.emitting) {
this.callbacks = this.callbacks.filter(v => v !== callback);
if (this._emitting) {
this._callbacks = this._callbacks.filter(v => v !== callback);
}
else {
const index = this.callbacks.indexOf(callback);
const index = this._callbacks.indexOf(callback);
if (index > -1) {
this.callbacks.splice(index, 1); // remove one item only
this._callbacks.splice(index, 1); // remove one item only
}

@@ -94,5 +94,5 @@ }

emit(v) {
const prevEmitting = this.emitting;
this.emitting = true;
this.callbacks.forEach(f => {
const prevEmitting = this._emitting;
this._emitting = true;
this._callbacks.forEach(f => {
try {

@@ -105,11 +105,11 @@ f(v);

});
this.emitting = prevEmitting;
this._emitting = prevEmitting;
}
pipe(that) {
this.callbacks.push(v => that.emit(v));
this._callbacks.push(v => that.emit(v));
return this;
}
dispose() {
flattenDisposable(this.disposables).dispose();
this.callbacks.length = 0;
flattenDisposable(this._disposables).dispose();
this._callbacks.length = 0;
}

@@ -116,0 +116,0 @@ toDispose(disposables) {

@@ -10,6 +10,7 @@ import * as Y from 'yjs';

import type { PageMeta, Workspace } from './workspace.js';
export declare type YBlock = Y.Map<unknown>;
export declare type YBlocks = Y.Map<YBlock>;
import type { BlockSuiteDoc } from '../yjs/index.js';
export type YBlock = Y.Map<unknown>;
export type YBlocks = Y.Map<YBlock>;
/** JSON-serializable properties of a block */
export declare type BlockProps = Record<string, any> & {
export type BlockProps = Record<string, any> & {
id: string;

@@ -20,7 +21,10 @@ flavour: string;

};
export declare type PrefixedBlockProps = Record<string, unknown> & {
export type PrefixedBlockProps = Record<string, unknown> & {
'sys:id': string;
'sys:flavour': string;
};
export declare class Page extends Space {
export type PageData = {
[key: string]: YBlock;
};
export declare class Page extends Space<PageData> {
workspace: Workspace;

@@ -41,6 +45,5 @@ private _idGenerator;

};
constructor(workspace: Workspace, id: string, doc: Y.Doc, awareness: Awareness, idGenerator?: IdGenerator);
constructor(workspace: Workspace, id: string, doc: BlockSuiteDoc, awareness: Awareness, idGenerator?: IdGenerator);
get meta(): PageMeta;
get blobs(): Promise<import("../index.js").BlobStorage | null>;
get pageId(): string;
/** key-value store of blocks */

@@ -47,0 +50,0 @@ private get _yBlocks();

@@ -63,8 +63,5 @@ import * as Y from 'yjs';

}
get pageId() {
return this.id.replace('space:', '');
}
/** key-value store of blocks */
get _yBlocks() {
return this.doc.getMap(this.id);
return this.origin;
}

@@ -427,3 +424,3 @@ get root() {

if (this._yBlocks.size === 0) {
this.workspace.meta.writeVersion();
this.workspace.meta.writeVersion(this.workspace);
}

@@ -430,0 +427,0 @@ // Initialization from existing yDoc, indicating that the document is loaded from storage.

import type { DocumentSearchOptions } from 'flexsearch';
import { Doc } from 'yjs';
export declare type QueryContent = string | Partial<DocumentSearchOptions<boolean>>;
export declare type IndexMetadata = Readonly<{
export type QueryContent = string | Partial<DocumentSearchOptions<boolean>>;
export type IndexMetadata = Readonly<{
content: string;

@@ -6,0 +6,0 @@ reference?: string;

@@ -10,2 +10,3 @@ import * as Y from 'yjs';

import { BlobStorage } from '../blob/index.js';
import type { BlockSuiteDoc } from '../yjs/index.js';
export interface PageMeta {

@@ -17,4 +18,9 @@ id: string;

}
declare class WorkspaceMeta extends Space {
private _workspace;
type WorkspaceMetaData = {
pages: Y.Array<unknown>;
versions: Y.Map<unknown>;
name: string;
avatar: string;
};
declare class WorkspaceMeta extends Space<WorkspaceMetaData> {
private _prevPages;

@@ -24,10 +30,13 @@ pageAdded: Signal<string>;

pagesUpdated: Signal<void>;
constructor(id: string, workspace: Workspace, awareness: Awareness);
private get _yMetaRoot();
private get _yPages();
private get _yVersions();
commonFieldsUpdated: Signal<void>;
constructor(id: string, doc: BlockSuiteDoc, awareness: Awareness);
get pages(): Y.Array<unknown>;
get name(): string;
get avatar(): string;
setName(name: string): void;
setAvatar(avatar: string): void;
get pageMetas(): PageMeta[];
getPageMeta(id: string): PageMeta | undefined;
addPage(page: PageMeta, index?: number): void;
setPage(id: string, props: Partial<PageMeta>): void;
addPageMeta(page: PageMeta, index?: number): void;
setPageMeta(id: string, props: Partial<PageMeta>): void;
removePage(id: string): void;

@@ -37,3 +46,3 @@ /**

*/
writeVersion(): void;
writeVersion(workspace: Workspace): void;
/**

@@ -44,4 +53,8 @@ * @internal Only for page initialization

private _handlePageEvent;
private _handleCommonFieldsEvent;
private _handleEvents;
}
export declare class Workspace {
static Y: typeof Y;
readonly room: string | undefined;
private _store;

@@ -61,3 +74,3 @@ private _indexer;

private get _pages();
get doc(): Y.Doc;
get doc(): BlockSuiteDoc<import("../yjs/index.js").BlockSuiteDocData>;
register(blockSchema: Record<string, typeof BaseBlockModel>): this;

@@ -64,0 +77,0 @@ private _hasPage;

@@ -9,4 +9,11 @@ import * as Y from 'yjs';

class WorkspaceMeta extends Space {
constructor(id, workspace, awareness) {
super(id, workspace.doc, awareness);
constructor(id, doc, awareness) {
super(id, doc, awareness, {
valueInitializer: {
pages: () => new Y.Array(),
versions: () => new Y.Map(),
avatar: () => '',
name: () => '',
},
});
this._prevPages = new Set();

@@ -16,66 +23,59 @@ this.pageAdded = new Signal();

this.pagesUpdated = new Signal();
this._handlePageEvent = (_) => {
const { pageMetas, _prevPages } = this;
pageMetas.forEach(page => {
// newly added space can't be found
// unless explictly getMap after meta updated
this.doc.getMap('space:' + page.id);
if (!_prevPages.has(page.id)) {
// Ensure following YEvent handler could be triggered in correct order.
setTimeout(() => this.pageAdded.emit(page.id));
this.commonFieldsUpdated = new Signal();
this._handleEvents = (events) => {
events.forEach(e => {
const hasKey = (k) => e.target === this.origin && e.changes.keys.has(k);
if (e.target === this.pages ||
e.target.parent === this.pages ||
hasKey('pages')) {
this._handlePageEvent();
}
});
_prevPages.forEach(prevPageId => {
const isRemoved = !pageMetas.find(p => p.id === prevPageId);
if (isRemoved) {
this.pageRemoved.emit(prevPageId);
else if (hasKey('name') || hasKey('avatar')) {
this._handleCommonFieldsEvent();
}
});
_prevPages.clear();
pageMetas.forEach(page => _prevPages.add(page.id));
this.pagesUpdated.emit();
};
this._workspace = workspace;
this._yMetaRoot.observeDeep(this._handlePageEvent);
this.origin.observeDeep(this._handleEvents);
}
get _yMetaRoot() {
return this.doc.getMap(this.id);
get pages() {
return this.proxy.pages;
}
get _yPages() {
if (!this._yMetaRoot.has('pages')) {
this._yMetaRoot.set('pages', new Y.Array());
}
return this._yMetaRoot.get('pages');
get name() {
return this.proxy.name;
}
get _yVersions() {
if (!this._yMetaRoot.has('versions')) {
this._yMetaRoot.set('versions', new Y.Map());
}
return this._yMetaRoot.get('versions');
get avatar() {
return this.proxy.avatar;
}
setName(name) {
this.doc.transact(() => {
this.proxy.name = name;
});
}
setAvatar(avatar) {
this.doc.transact(() => {
this.proxy.avatar = avatar;
});
}
get pageMetas() {
return this._yPages.toJSON();
return this.proxy.pages.toJSON();
}
getPageMeta(id) {
if (id.startsWith('space:')) {
id = id.slice(6);
}
return this.pageMetas.find(page => page.id === id);
}
addPage(page, index) {
addPageMeta(page, index) {
const yPage = new Y.Map();
this.doc.transact(() => {
Object.entries(page).forEach(([key, value]) => {
yPage.set(key, value);
});
if (index === undefined) {
this._yPages.push([yPage]);
Object.entries(page).forEach(([key, value]) => {
yPage.set(key, value);
});
this.pages.push([yPage]);
}
else {
this._yPages.insert(index, [yPage]);
this.pages.insert(index, [yPage]);
}
});
}
setPage(id, props) {
const pages = this._yPages.toJSON();
setPageMeta(id, props) {
const pages = this.pages.toJSON();
const index = pages.findIndex((page) => id === page.id);

@@ -85,3 +85,3 @@ this.doc.transact(() => {

return;
const yPage = this._yPages.get(index);
const yPage = this.pages.get(index);
Object.entries(props).forEach(([key, value]) => {

@@ -93,7 +93,7 @@ yPage.set(key, value);

removePage(id) {
const pages = this._yPages.toJSON();
const pages = this.pages.toJSON();
const index = pages.findIndex((page) => id === page.id);
this.doc.transact(() => {
if (index !== -1) {
this._yPages.delete(index, 1);
this.pages.delete(index, 1);
}

@@ -105,9 +105,9 @@ });

*/
writeVersion() {
const { _yVersions, _workspace } = this;
_workspace.flavourMap.forEach((model, flavour) => {
writeVersion(workspace) {
const versions = this.proxy.versions;
workspace.flavourMap.forEach((model, flavour) => {
const yVersion = new Y.Array();
const [major, minor] = model.version;
yVersion.push([major, minor]);
_yVersions.set(flavour, yVersion);
versions.set(flavour, yVersion);
});

@@ -121,2 +121,26 @@ }

}
_handlePageEvent() {
const { pageMetas, _prevPages } = this;
pageMetas.forEach(pageMeta => {
// newly added space can't be found
// unless explictly getMap after meta updated
this.doc.getMap('space:' + pageMeta.id);
if (!_prevPages.has(pageMeta.id)) {
// Ensure following YEvent handler could be triggered in correct order.
setTimeout(() => this.pageAdded.emit(pageMeta.id));
}
});
_prevPages.forEach(prevPageId => {
const isRemoved = !pageMetas.find(p => p.id === prevPageId);
if (isRemoved) {
this.pageRemoved.emit(prevPageId);
}
});
_prevPages.clear();
pageMetas.forEach(page => _prevPages.add(page.id));
this.pagesUpdated.emit();
}
_handleCommonFieldsEvent() {
this.commonFieldsUpdated.emit();
}
}

@@ -128,4 +152,11 @@ export class Workspace {

this._indexer = new Indexer(this.doc);
this._blobStorage = getBlobStorage(options.room);
this.meta = new WorkspaceMeta('space:meta', this, this._store.awareness);
if (!options.isSSR) {
this._blobStorage = getBlobStorage(options.room);
}
else {
// blob storage is not reachable in server side
this._blobStorage = Promise.resolve(null);
}
this.room = options.room;
this.meta = new WorkspaceMeta('space:meta', this.doc, this._store.awareness);
this.signals = {

@@ -168,13 +199,10 @@ pagesUpdated: this.meta.pagesUpdated,

_handlePageEvent() {
this.signals.pageAdded.on(pageMeta => {
const page = new Page(this, 'space:' + pageMeta, this.doc, this._store.awareness, this._store.idGenerator);
this.signals.pageAdded.on(pageId => {
const page = new Page(this, pageId, this.doc, this._store.awareness, this._store.idGenerator);
this._store.addSpace(page);
page.syncFromExistingDoc();
this._indexer.onCreatePage(pageMeta);
this._indexer.onCreatePage(pageId);
});
this.signals.pageRemoved.on(id => {
if (!id.startsWith('space:')) {
id = 'space:' + id;
}
const page = this._pages.get(id);
const page = this.getPage(id);
page.dispose();

@@ -189,3 +217,3 @@ this._store.removeSpace(page);

}
this.meta.addPage({
this.meta.addPageMeta({
id: pageId,

@@ -198,11 +226,5 @@ title: '',

setPageMeta(pageId, props) {
if (pageId.startsWith('space:')) {
pageId = pageId.slice(6);
}
this.meta.setPage(pageId, props);
this.meta.setPageMeta(pageId, props);
}
removePage(pageId) {
if (pageId.startsWith('space:')) {
pageId = pageId.slice(6);
}
this.meta.removePage(pageId);

@@ -220,2 +242,3 @@ }

}
Workspace.Y = Y;
//# sourceMappingURL=workspace.js.map
{
"name": "@blocksuite/store",
"version": "0.3.0-alpha.18",
"version": "0.3.0-alpha.19",
"description": "BlockSuite data store built for general purpose state management.",

@@ -16,14 +16,15 @@ "main": "dist/index.js",

"idb-keyval": "^6.2.0",
"ky": "^0.32.2",
"lib0": "^0.2.52",
"sha3": "^2.1.4",
"y-indexeddb": "^9.0.9",
"ky": "^0.33.0",
"lib0": "^0.2.58",
"y-protocols": "^1.0.5",
"y-webrtc": "^10.2.3",
"yjs": "^13.5.41"
"y-webrtc": "^10.2.3"
},
"devDependencies": {
"cross-env": "^7.0.3",
"lit": "^2.3.1"
"lit": "^2.5.0",
"yjs": "^13.5.43"
},
"peerDependencies": {
"yjs": "^13"
},
"exports": {

@@ -38,3 +39,2 @@ "./src/*": "./dist/*.js",

"serve": "cross-env PORT=4444 node node_modules/y-webrtc/bin/server.js",
"serve:websocket": "cross-env HOST=localhost PORT=1234 npx y-websocket",
"build": "tsc",

@@ -41,0 +41,0 @@ "test:unit": "vitest --run",

@@ -5,3 +5,9 @@ /* eslint-disable @typescript-eslint/no-restricted-imports */

import { assert, describe, expect, it } from 'vitest';
import { BaseBlockModel, Signal, Workspace, Page, Generator } from '..';
import {
BaseBlockModel,
Signal,
Workspace,
Page,
Generator,
} from '../index.js';

@@ -14,3 +20,3 @@ // Use manual per-module import/export to support vitest environment on Node.js

import { DividerBlockModel } from '../../../blocks/src/divider-block/divider-model.js';
import type { PageMeta } from '../workspace';
import type { PageMeta } from '../workspace/index.js';
import { assertExists } from '../utils/utils.js';

@@ -203,2 +209,7 @@

let called = false;
workspace.meta.pagesUpdated.on(() => {
called = true;
});
workspace.setPageMeta('page0', { favorite: true });

@@ -219,3 +230,17 @@ assert.deepEqual(

);
assert.ok(called);
});
it('can set workspace common meta fields', async () => {
const options = createTestOptions();
const workspace = new Workspace(options);
queueMicrotask(() => workspace.meta.setName('hello'));
await waitOnce(workspace.meta.commonFieldsUpdated);
assert.deepEqual(workspace.meta.name, 'hello');
queueMicrotask(() => workspace.meta.setAvatar('gengar.jpg'));
await waitOnce(workspace.meta.commonFieldsUpdated);
assert.deepEqual(workspace.meta.avatar, 'gengar.jpg');
});
});

@@ -222,0 +247,0 @@

@@ -1,2 +0,1 @@

import { Buffer } from 'buffer';
import ky from 'ky';

@@ -6,3 +5,3 @@ import { Signal } from '../utils/signal.js';

import type { BlobId, BlobProvider, BlobURL, IDBInstance } from './types.js';
import { getDatabase, sha3, sleep } from './utils.js';
import { getDatabase, sha, sleep } from './utils.js';

@@ -74,3 +73,3 @@ const RETRY_TIMEOUT = 500;

const buffer = await blob.arrayBuffer();
const hash = sha3(Buffer.from(buffer));
const hash = await sha(buffer);
if (!(await this._database.has(hash))) {

@@ -77,0 +76,0 @@ await this._database.set(hash, buffer);

@@ -1,16 +0,10 @@

import type { Buffer } from 'buffer';
import { createStore, del, get, keys, set, clear } from 'idb-keyval';
import { SHA3 } from 'sha3';
import type { IDBInstance } from './types.js';
import { Buffer } from 'buffer';
const hash = new SHA3(256);
export async function sha(input: ArrayBuffer): Promise<string> {
const hash = await crypto.subtle.digest('SHA-256', input);
const buffer = Buffer.from(hash);
export function sha3(buffer: Buffer): string {
hash.reset();
hash.update(buffer);
return hash
.digest('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
}

@@ -17,0 +11,0 @@

import type * as Y from 'yjs';
// @ts-ignore
import { WebrtcProvider } from 'y-webrtc';
import { IndexeddbPersistence } from 'y-indexeddb';
import { IndexeddbPersistence } from './providers/indexeddb.js';
import type { Awareness } from 'y-protocols/awareness';

@@ -42,3 +42,6 @@

const isWeb = typeof window !== 'undefined';
const isLocalhost = isWeb && window.location.hostname === 'localhost';
const isLocalhost =
isWeb &&
(window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1');
const signaling = isLocalhost ? LOCAL_SIGNALING : DEFAULT_SIGNALING;

@@ -45,0 +48,0 @@

@@ -5,2 +5,4 @@ import type * as Y from 'yjs';

import type { RichTextAdapter } from './text-adapter.js';
import type { DataInitializer } from './yjs/proxy.js';
import type { BlockSuiteDoc } from './yjs/index.js';

@@ -12,16 +14,40 @@ export interface StackItem {

export class Space {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class Space<Data extends Record<string, unknown> = Record<string, any>> {
/** unprefixed id */
readonly id: string;
readonly doc: Y.Doc;
readonly doc: BlockSuiteDoc;
/**
* @internal
* @protected
*/
protected readonly proxy: Data;
protected readonly origin: Y.Map<Data[keyof Data]>;
readonly awareness!: AwarenessAdapter;
readonly richTextAdapters = new Map<string, RichTextAdapter>();
constructor(id: string, doc: Y.Doc, awareness: Awareness) {
constructor(
id: string,
doc: BlockSuiteDoc,
awareness: Awareness,
options?: {
valueInitializer?: DataInitializer<Partial<Data>>;
}
) {
this.id = id;
this.doc = doc;
const targetId = this.id.startsWith('space:') ? this.id : this.prefixedId;
this.origin = this.doc.getMap(targetId);
this.proxy = this.doc.getMapProxy<string, Data>(targetId, {
initializer: options?.valueInitializer,
});
const aware = awareness ?? new Awareness(this.doc);
this.awareness = new AwarenessAdapter(this, aware);
this.awareness = new AwarenessAdapter(this as Space, aware);
}
get prefixedId() {
return `space:${this.id}`;
}
transact(fn: () => void) {

@@ -28,0 +54,0 @@ this.doc.transact(fn, this.doc.clientID);

import type { Space } from './space.js';
import type { IdGenerator } from './utils/id-generator.js';
import { Awareness } from 'y-protocols/awareness.js';
import * as Y from 'yjs';
import type { DocProvider, DocProviderConstructor } from './doc-providers.js';

@@ -12,2 +11,3 @@ import { serializeYDoc, yDocToJSXNode } from './utils/jsx.js';

} from './utils/id-generator.js';
import { BlockSuiteDoc } from './yjs/index.js';

@@ -39,3 +39,13 @@ export interface SerializedStore {

export interface StoreOptions {
/**
* @example
* const workspace = new Workspace({
* isSSR: typeof window === 'undefined'
* })
*/
export interface SSROptions {
isSSR?: boolean;
}
export interface StoreOptions extends SSROptions {
room?: string;

@@ -50,3 +60,3 @@ providers?: DocProviderConstructor[];

export class Store {
readonly doc = new Y.Doc();
readonly doc = new BlockSuiteDoc();
readonly providers: DocProvider[] = [];

@@ -89,7 +99,7 @@ readonly spaces = new Map<string, Space>();

addSpace(space: Space) {
this.spaces.set(space.id, space);
this.spaces.set(space.prefixedId, space);
}
removeSpace(space: Space) {
this.spaces.delete(space.id);
this.spaces.delete(space.prefixedId);
}

@@ -96,0 +106,0 @@

@@ -5,15 +5,49 @@ export interface Disposable {

export function flattenDisposable(a: Disposable[]): Disposable {
type DisposeLogic = Disposable | (() => void);
export class DisposableGroup implements Disposable {
private _disposed = false;
get disposed() {
return this._disposed;
}
constructor(private _disposables: DisposeLogic[] = []) {}
/**
* Add to group to be disposed with others.
*
* This will be immediately disposed if this group has already been disposed.
*/
add(disposable: DisposeLogic | undefined | null | false): void {
if (disposable) {
if (this._disposed) execDisposeLogic(disposable);
else this._disposables.push(disposable);
}
}
dispose(): void {
disposeAllAndClearArray(this._disposables);
this._disposed = true;
}
}
export function flattenDisposable(disposables: Disposable[]): Disposable {
return {
dispose: () => {
a.forEach(d => {
try {
d.dispose();
} catch (err) {
console.error(err);
}
});
a.length = 0;
},
dispose: disposeAllAndClearArray.bind(null, disposables),
};
}
/** @internal */
function disposeAllAndClearArray(disposables: DisposeLogic[]) {
for (const disposable of disposables) {
try {
execDisposeLogic(disposable);
} catch (err) {
console.error(err);
}
}
disposables.length = 0;
}
/** @internal */
function execDisposeLogic(disposable: DisposeLogic) {
if (typeof disposable === 'function') disposable();
else disposable.dispose();
}

@@ -6,7 +6,16 @@ import { Disposable, flattenDisposable } from './disposable.js';

export class Signal<T = void> implements Disposable {
private emitting = false;
private callbacks: ((v: T) => unknown)[] = [];
private disposables: Disposable[] = [];
private _emitting = false;
private _callbacks: ((v: T) => unknown)[] = [];
private _disposables: Disposable[] = [];
static fromEvent<N extends keyof WindowEventMap>(
element: Window,
eventName: N,
options?: boolean | AddEventListenerOptions
): Signal<WindowEventMap[N]>;
static fromEvent<N extends keyof HTMLElementEventMap>(
element: HTMLElement,
eventName: N,
eventOptions?: boolean | AddEventListenerOptions
): Signal<HTMLElementEventMap[N]>;
static fromEvent<N extends keyof HTMLElementEventMap>(
element: HTMLElement | Window,

@@ -21,3 +30,3 @@ eventName: N,

(element as HTMLElement).addEventListener(eventName, handler, eventOptions);
signal.disposables.push({
signal._disposables.push({
dispose: () => {

@@ -33,3 +42,3 @@ (element as HTMLElement).removeEventListener(eventName, handler);

// if result is disposed, dispose this too
result.disposables.push({ dispose: () => this.dispose() });
result._disposables.push({ dispose: () => this.dispose() });

@@ -46,16 +55,16 @@ this.on((v: T) => {

on(callback: (v: T) => unknown): Disposable {
if (this.emitting) {
const newCallback = [...this.callbacks, callback];
this.callbacks = newCallback;
if (this._emitting) {
const newCallback = [...this._callbacks, callback];
this._callbacks = newCallback;
} else {
this.callbacks.push(callback);
this._callbacks.push(callback);
}
return {
dispose: () => {
if (this.emitting) {
this.callbacks = this.callbacks.filter(v => v !== callback);
if (this._emitting) {
this._callbacks = this._callbacks.filter(v => v !== callback);
} else {
const index = this.callbacks.indexOf(callback);
const index = this._callbacks.indexOf(callback);
if (index > -1) {
this.callbacks.splice(index, 1); // remove one item only
this._callbacks.splice(index, 1); // remove one item only
}

@@ -79,16 +88,16 @@ }

unshift(callback: (v: T) => unknown): Disposable {
if (this.emitting) {
const newCallback = [callback, ...this.callbacks];
this.callbacks = newCallback;
if (this._emitting) {
const newCallback = [callback, ...this._callbacks];
this._callbacks = newCallback;
} else {
this.callbacks.unshift(callback);
this._callbacks.unshift(callback);
}
return {
dispose: () => {
if (this.emitting) {
this.callbacks = this.callbacks.filter(v => v !== callback);
if (this._emitting) {
this._callbacks = this._callbacks.filter(v => v !== callback);
} else {
const index = this.callbacks.indexOf(callback);
const index = this._callbacks.indexOf(callback);
if (index > -1) {
this.callbacks.splice(index, 1); // remove one item only
this._callbacks.splice(index, 1); // remove one item only
}

@@ -101,5 +110,5 @@ }

emit(v: T) {
const prevEmitting = this.emitting;
this.emitting = true;
this.callbacks.forEach(f => {
const prevEmitting = this._emitting;
this._emitting = true;
this._callbacks.forEach(f => {
try {

@@ -111,7 +120,7 @@ f(v);

});
this.emitting = prevEmitting;
this._emitting = prevEmitting;
}
pipe(that: Signal<T>): Signal<T> {
this.callbacks.push(v => that.emit(v));
this._callbacks.push(v => that.emit(v));
return this;

@@ -121,4 +130,4 @@ }

dispose() {
flattenDisposable(this.disposables).dispose();
this.callbacks.length = 0;
flattenDisposable(this._disposables).dispose();
this._callbacks.length = 0;
}

@@ -125,0 +134,0 @@

@@ -24,2 +24,3 @@ import * as Y from 'yjs';

import type { PageMeta, Workspace } from './workspace.js';
import type { BlockSuiteDoc } from '../yjs/index.js';

@@ -49,3 +50,7 @@ export type YBlock = Y.Map<unknown>;

export class Page extends Space {
export type PageData = {
[key: string]: YBlock;
};
export class Page extends Space<PageData> {
public workspace: Workspace;

@@ -75,3 +80,3 @@ private _idGenerator: IdGenerator;

id: string,
doc: Y.Doc,
doc: BlockSuiteDoc,
awareness: Awareness,

@@ -93,9 +98,5 @@ idGenerator: IdGenerator = uuidv4

get pageId() {
return this.id.replace('space:', '');
}
/** key-value store of blocks */
private get _yBlocks() {
return this.doc.getMap(this.id) as YBlocks;
private get _yBlocks(): YBlocks {
return this.origin;
}

@@ -562,3 +563,3 @@

if (this._yBlocks.size === 0) {
this.workspace.meta.writeVersion();
this.workspace.meta.writeVersion(this.workspace);
}

@@ -565,0 +566,0 @@ // Initialization from existing yDoc, indicating that the document is loaded from storage.

@@ -10,2 +10,3 @@ import * as Y from 'yjs';

import { BlobStorage, getBlobStorage } from '../blob/index.js';
import type { BlockSuiteDoc } from '../yjs/index.js';

@@ -19,4 +20,10 @@ export interface PageMeta {

class WorkspaceMeta extends Space {
private _workspace: Workspace;
type WorkspaceMetaData = {
pages: Y.Array<unknown>;
versions: Y.Map<unknown>;
name: string;
avatar: string;
};
class WorkspaceMeta extends Space<WorkspaceMetaData> {
private _prevPages = new Set<string>();

@@ -26,51 +33,58 @@ pageAdded = new Signal<string>();

pagesUpdated = new Signal();
commonFieldsUpdated = new Signal();
constructor(id: string, workspace: Workspace, awareness: Awareness) {
super(id, workspace.doc, awareness);
this._workspace = workspace;
this._yMetaRoot.observeDeep(this._handlePageEvent);
constructor(id: string, doc: BlockSuiteDoc, awareness: Awareness) {
super(id, doc, awareness, {
valueInitializer: {
pages: () => new Y.Array(),
versions: () => new Y.Map(),
avatar: () => '',
name: () => '',
},
});
this.origin.observeDeep(this._handleEvents);
}
private get _yMetaRoot() {
return this.doc.getMap(this.id);
get pages() {
return this.proxy.pages;
}
private get _yPages() {
if (!this._yMetaRoot.has('pages')) {
this._yMetaRoot.set('pages', new Y.Array());
}
get name() {
return this.proxy.name;
}
return this._yMetaRoot.get('pages') as Y.Array<unknown>;
get avatar() {
return this.proxy.avatar;
}
private get _yVersions() {
if (!this._yMetaRoot.has('versions')) {
this._yMetaRoot.set('versions', new Y.Map());
}
setName(name: string) {
this.doc.transact(() => {
this.proxy.name = name;
});
}
return this._yMetaRoot.get('versions') as Y.Map<unknown>;
setAvatar(avatar: string) {
this.doc.transact(() => {
this.proxy.avatar = avatar;
});
}
get pageMetas() {
return this._yPages.toJSON() as PageMeta[];
return this.proxy.pages.toJSON() as PageMeta[];
}
getPageMeta(id: string) {
if (id.startsWith('space:')) {
id = id.slice(6);
}
return this.pageMetas.find(page => page.id === id);
}
addPage(page: PageMeta, index?: number) {
addPageMeta(page: PageMeta, index?: number) {
const yPage = new Y.Map();
this.doc.transact(() => {
Object.entries(page).forEach(([key, value]) => {
yPage.set(key, value);
});
if (index === undefined) {
this._yPages.push([yPage]);
Object.entries(page).forEach(([key, value]) => {
yPage.set(key, value);
});
this.pages.push([yPage]);
} else {
this._yPages.insert(index, [yPage]);
this.pages.insert(index, [yPage]);
}

@@ -80,4 +94,4 @@ });

setPage(id: string, props: Partial<PageMeta>) {
const pages = this._yPages.toJSON() as PageMeta[];
setPageMeta(id: string, props: Partial<PageMeta>) {
const pages = this.pages.toJSON() as PageMeta[];
const index = pages.findIndex((page: PageMeta) => id === page.id);

@@ -88,3 +102,3 @@

const yPage = this._yPages.get(index) as Y.Map<unknown>;
const yPage = this.pages.get(index) as Y.Map<unknown>;
Object.entries(props).forEach(([key, value]) => {

@@ -97,3 +111,3 @@ yPage.set(key, value);

removePage(id: string) {
const pages = this._yPages.toJSON() as PageMeta[];
const pages = this.pages.toJSON() as PageMeta[];
const index = pages.findIndex((page: PageMeta) => id === page.id);

@@ -103,3 +117,3 @@

if (index !== -1) {
this._yPages.delete(index, 1);
this.pages.delete(index, 1);
}

@@ -112,9 +126,9 @@ });

*/
writeVersion() {
const { _yVersions, _workspace } = this;
_workspace.flavourMap.forEach((model, flavour) => {
writeVersion(workspace: Workspace) {
const versions = this.proxy.versions;
workspace.flavourMap.forEach((model, flavour) => {
const yVersion = new Y.Array();
const [major, minor] = model.version;
yVersion.push([major, minor]);
_yVersions.set(flavour, yVersion);
versions.set(flavour, yVersion);
});

@@ -130,13 +144,13 @@ }

private _handlePageEvent = (_: Y.YEvent<Y.Array<unknown>>[]) => {
private _handlePageEvent() {
const { pageMetas, _prevPages } = this;
pageMetas.forEach(page => {
pageMetas.forEach(pageMeta => {
// newly added space can't be found
// unless explictly getMap after meta updated
this.doc.getMap('space:' + page.id);
this.doc.getMap('space:' + pageMeta.id);
if (!_prevPages.has(page.id)) {
if (!_prevPages.has(pageMeta.id)) {
// Ensure following YEvent handler could be triggered in correct order.
setTimeout(() => this.pageAdded.emit(page.id));
setTimeout(() => this.pageAdded.emit(pageMeta.id));
}

@@ -156,2 +170,25 @@ });

this.pagesUpdated.emit();
}
private _handleCommonFieldsEvent() {
this.commonFieldsUpdated.emit();
}
private _handleEvents = (
events: Y.YEvent<Y.Array<unknown> | Y.Text | Y.Map<unknown>>[]
) => {
events.forEach(e => {
const hasKey = (k: string) =>
e.target === this.origin && e.changes.keys.has(k);
if (
e.target === this.pages ||
e.target.parent === this.pages ||
hasKey('pages')
) {
this._handlePageEvent();
} else if (hasKey('name') || hasKey('avatar')) {
this._handleCommonFieldsEvent();
}
});
};

@@ -161,2 +198,5 @@ }

export class Workspace {
static Y = Y;
public readonly room: string | undefined;
private _store: Store;

@@ -179,5 +219,15 @@ private _indexer: Indexer;

this._indexer = new Indexer(this.doc);
this._blobStorage = getBlobStorage(options.room);
if (!options.isSSR) {
this._blobStorage = getBlobStorage(options.room);
} else {
// blob storage is not reachable in server side
this._blobStorage = Promise.resolve(null);
}
this.room = options.room;
this.meta = new WorkspaceMeta('space:meta', this, this._store.awareness);
this.meta = new WorkspaceMeta(
'space:meta',
this.doc,
this._store.awareness
);

@@ -231,6 +281,6 @@ this.signals = {

private _handlePageEvent() {
this.signals.pageAdded.on(pageMeta => {
this.signals.pageAdded.on(pageId => {
const page = new Page(
this,
'space:' + pageMeta,
pageId,
this.doc,

@@ -242,11 +292,7 @@ this._store.awareness,

page.syncFromExistingDoc();
this._indexer.onCreatePage(pageMeta);
this._indexer.onCreatePage(pageId);
});
this.signals.pageRemoved.on(id => {
if (!id.startsWith('space:')) {
id = 'space:' + id;
}
const page = this._pages.get(id) as Page;
const page = this.getPage(id) as Page;
page.dispose();

@@ -263,3 +309,3 @@ this._store.removeSpace(page);

this.meta.addPage({
this.meta.addPageMeta({
id: pageId,

@@ -273,14 +319,6 @@ title: '',

setPageMeta(pageId: string, props: Partial<PageMeta>) {
if (pageId.startsWith('space:')) {
pageId = pageId.slice(6);
}
this.meta.setPage(pageId, props);
this.meta.setPageMeta(pageId, props);
}
removePage(pageId: string) {
if (pageId.startsWith('space:')) {
pageId = pageId.slice(6);
}
this.meta.removePage(pageId);

@@ -287,0 +325,0 @@ }

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

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

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc