@tldraw/tlstore
Advanced tools
Comparing version 2.0.0-canary.5ba3e20cb to 2.0.0-canary.5cc04ef2fdc8
import { Atom } from 'signia'; | ||
import { Computed } from 'signia'; | ||
import { Signal } from 'signia'; | ||
@@ -58,3 +59,3 @@ /** | ||
/** @public */ | ||
export declare const compareSchemas: (a: SerializedSchema, b: SerializedSchema) => number; | ||
export declare const compareSchemas: (a: SerializedSchema, b: SerializedSchema) => -1 | 0 | 1; | ||
@@ -85,2 +86,3 @@ /** | ||
validator: StoreValidator<R>; | ||
scope: Scope; | ||
}): RecordType<R, keyof Omit<R, 'id' | 'typeName'>>; | ||
@@ -228,2 +230,3 @@ | ||
} | StoreValidator<R>; | ||
readonly scope: Scope; | ||
constructor( | ||
@@ -242,2 +245,3 @@ /** | ||
} | StoreValidator<R>; | ||
readonly scope?: Scope; | ||
}); | ||
@@ -353,2 +357,13 @@ /** | ||
/** | ||
* Defines the scope of the record | ||
* | ||
* instance: The record belongs to a single instance of the store. It should not be synced, and any persistence logic should 'de-instance-ize' the record before persisting it, and apply the reverse when rehydrating. | ||
* document: The record is persisted and synced. It is available to all store instances. | ||
* presence: The record belongs to a single instance of the store. It may be synced to other instances, but other instances should not make changes to it. It should not be persisted. | ||
* | ||
* @public | ||
* */ | ||
declare type Scope = 'document' | 'instance' | 'presence'; | ||
/** @public */ | ||
@@ -558,2 +573,3 @@ export declare interface SerializedSchema { | ||
createSelectedComputedCache: <T, J, V extends R = R>(name: string, selector: (record: V) => T | undefined, derive: (input: T) => J | undefined) => ComputedCache<J, V>; | ||
private _integrityChecker?; | ||
/* Excluded from this release type: ensureStoreIsUsable */ | ||
@@ -677,3 +693,4 @@ private _isPossiblyCorrupted; | ||
migrateStoreSnapshot(storeSnapshot: StoreSnapshot<R>, persistedSchema: SerializedSchema): MigrationResult<StoreSnapshot<R>>; | ||
/* Excluded from this release type: ensureStoreIsUsable */ | ||
/* Excluded from this release type: createIntegrityChecker */ | ||
/* Excluded from this release type: derivePresenceState */ | ||
serialize(): SerializedSchema; | ||
@@ -695,3 +712,4 @@ serializeEarliestVersion(): SerializedSchema; | ||
}) => R; | ||
/* Excluded from this release type: ensureStoreIsUsable */ | ||
/* Excluded from this release type: createIntegrityChecker */ | ||
/* Excluded from this release type: derivePresenceState */ | ||
}; | ||
@@ -698,0 +716,0 @@ |
@@ -34,2 +34,3 @@ "use strict"; | ||
this.validator = config.validator ?? { validate: (r) => r }; | ||
this.scope = config.scope ?? "document"; | ||
} | ||
@@ -39,2 +40,3 @@ createDefaultProperties; | ||
validator; | ||
scope; | ||
/** | ||
@@ -162,3 +164,4 @@ * Create a new record of this type. | ||
migrations: this.migrations, | ||
validator: this.validator | ||
validator: this.validator, | ||
scope: this.scope | ||
}); | ||
@@ -178,3 +181,4 @@ } | ||
migrations: config.migrations ?? { currentVersion: 0, firstVersion: 0, migrators: {} }, | ||
validator: config.validator | ||
validator: config.validator, | ||
scope: config.scope | ||
}); | ||
@@ -181,0 +185,0 @@ } |
@@ -26,2 +26,3 @@ "use strict"; | ||
module.exports = __toCommonJS(Store_exports); | ||
var import_utils = require("@tldraw/utils"); | ||
var import_signia = require("signia"); | ||
@@ -96,3 +97,3 @@ var import_Cache = require("./Cache"); | ||
}, | ||
{ scheduleEffect: (cb) => requestAnimationFrame(cb) } | ||
{ scheduleEffect: (cb) => (0, import_utils.throttledRaf)(cb) } | ||
); | ||
@@ -468,5 +469,7 @@ } | ||
}; | ||
_integrityChecker; | ||
/** @internal */ | ||
ensureStoreIsUsable() { | ||
this.schema.ensureStoreIsUsable(this); | ||
this._integrityChecker ??= this.schema.createIntegrityChecker(this); | ||
this._integrityChecker?.(); | ||
} | ||
@@ -577,13 +580,3 @@ _isPossiblyCorrupted = false; | ||
} | ||
/** | ||
* Ensure that the store is usable. A class that extends this store should override this method. | ||
* | ||
* @param config - The configuration object. This can be any object that allows the store to | ||
* validate that it is usable; the extending class should specify the type. | ||
* @public | ||
*/ | ||
ensureStoreIsUsable(_config = {}) { | ||
return; | ||
} | ||
} | ||
//# sourceMappingURL=Store.js.map |
@@ -150,5 +150,9 @@ "use strict"; | ||
/** @internal */ | ||
ensureStoreIsUsable(store) { | ||
this.options.ensureStoreIsUsable?.(store); | ||
createIntegrityChecker(store) { | ||
return this.options.createIntegrityChecker?.(store) ?? void 0; | ||
} | ||
/** @internal */ | ||
derivePresenceState(store) { | ||
return this.options.derivePresenceState?.(store); | ||
} | ||
serialize() { | ||
@@ -155,0 +159,0 @@ return { |
{ | ||
"name": "@tldraw/tlstore", | ||
"description": "A tiny little drawing app (store).", | ||
"version": "2.0.0-canary.5ba3e20cb", | ||
"version": "2.0.0-canary.5cc04ef2fdc8", | ||
"packageManager": "yarn@3.5.0", | ||
"author": { | ||
@@ -34,15 +35,15 @@ "name": "tldraw GB Ltd.", | ||
"scripts": { | ||
"test": "lazy :inherit", | ||
"test:coverage": "lazy :inherit", | ||
"build:package": "lazy :inherit", | ||
"build:api": "lazy :inherit", | ||
"test": "lazy inherit", | ||
"test-coverage": "lazy inherit", | ||
"build": "yarn run -T tsx ../../scripts/build-package.ts", | ||
"build-api": "yarn run -T tsx ../../scripts/build-api.ts", | ||
"prepack": "yarn run -T tsx ../../scripts/prepack.ts", | ||
"postpack": "../../scripts/postpack.sh", | ||
"pack-tarball": "yarn pack", | ||
"lint": "lazy :inherit" | ||
"lint": "yarn run -T tsx ../../scripts/lint.ts" | ||
}, | ||
"dependencies": { | ||
"@tldraw/utils": "2.0.0-canary.5ba3e20cb", | ||
"@tldraw/utils": "2.0.0-canary.5cc04ef2fdc8", | ||
"lodash.isequal": "^4.5.0", | ||
"nanoid": "^3.0.0" | ||
"nanoid": "4.0.2" | ||
}, | ||
@@ -55,3 +56,3 @@ "peerDependencies": { | ||
"@types/lodash.isequal": "^4.5.6", | ||
"lazyrepo": "0.0.0-alpha.10", | ||
"lazyrepo": "0.0.0-alpha.26", | ||
"raf": "^3.4.1" | ||
@@ -58,0 +59,0 @@ }, |
@@ -351,1 +351,5 @@ # @tldraw/tlstore | ||
A diff describing the changes to a collection. | ||
## License | ||
The source code in this repository (as well as our 2.0+ distributions and releases) are currently licensed under Apache-2.0. These licenses are subject to change in our upcoming 2.0 release. If you are planning to use tldraw in a commercial product, please reach out at [hello@tldraw.com](mailto://hello@tldraw.com). |
import { SerializedSchema } from './StoreSchema' | ||
/** @public */ | ||
export const compareSchemas = (a: SerializedSchema, b: SerializedSchema): number => { | ||
export const compareSchemas = (a: SerializedSchema, b: SerializedSchema): 0 | 1 | -1 => { | ||
if (a.schemaVersion > b.schemaVersion) { | ||
@@ -6,0 +6,0 @@ return 1 |
@@ -10,2 +10,13 @@ import { structuredClone } from '@tldraw/utils' | ||
/** | ||
* Defines the scope of the record | ||
* | ||
* instance: The record belongs to a single instance of the store. It should not be synced, and any persistence logic should 'de-instance-ize' the record before persisting it, and apply the reverse when rehydrating. | ||
* document: The record is persisted and synced. It is available to all store instances. | ||
* presence: The record belongs to a single instance of the store. It may be synced to other instances, but other instances should not make changes to it. It should not be persisted. | ||
* | ||
* @public | ||
* */ | ||
export type Scope = 'instance' | 'document' | 'presence' | ||
/** | ||
* A record type is a type that can be stored in a record store. It is created with | ||
@@ -24,2 +35,4 @@ * `createRecordType`. | ||
readonly scope: Scope | ||
constructor( | ||
@@ -37,2 +50,3 @@ /** | ||
readonly validator?: StoreValidator<R> | { validate: (r: unknown) => R } | ||
readonly scope?: Scope | ||
} | ||
@@ -43,2 +57,3 @@ ) { | ||
this.validator = config.validator ?? { validate: (r: unknown) => r as R } | ||
this.scope = config.scope ?? 'document' | ||
} | ||
@@ -181,2 +196,3 @@ | ||
validator: this.validator, | ||
scope: this.scope, | ||
}) | ||
@@ -212,2 +228,3 @@ } | ||
validator: StoreValidator<R> | ||
scope: Scope | ||
} | ||
@@ -219,2 +236,3 @@ ): RecordType<R, keyof Omit<R, 'id' | 'typeName'>> { | ||
validator: config.validator, | ||
scope: config.scope, | ||
}) | ||
@@ -221,0 +239,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import { throttledRaf } from '@tldraw/utils' | ||
import { atom, Atom, computed, Computed, Reactor, reactor, transact } from 'signia' | ||
@@ -175,3 +176,3 @@ import { BaseRecord, ID } from './BaseRecord' | ||
}, | ||
{ scheduleEffect: (cb) => requestAnimationFrame(cb) } | ||
{ scheduleEffect: (cb) => throttledRaf(cb) } | ||
) | ||
@@ -625,5 +626,8 @@ } | ||
private _integrityChecker?: () => void | undefined | ||
/** @internal */ | ||
ensureStoreIsUsable() { | ||
this.schema.ensureStoreIsUsable(this) | ||
this._integrityChecker ??= this.schema.createIntegrityChecker(this) | ||
this._integrityChecker?.() | ||
} | ||
@@ -771,13 +775,2 @@ | ||
} | ||
/** | ||
* Ensure that the store is usable. A class that extends this store should override this method. | ||
* | ||
* @param config - The configuration object. This can be any object that allows the store to | ||
* validate that it is usable; the extending class should specify the type. | ||
* @public | ||
*/ | ||
ensureStoreIsUsable(_config = {} as any): void { | ||
return | ||
} | ||
} |
import { getOwnProperty, objectMapValues } from '@tldraw/utils' | ||
import { Signal } from 'signia' | ||
import { BaseRecord } from './BaseRecord' | ||
@@ -50,3 +51,5 @@ import { RecordType } from './RecordType' | ||
/** @internal */ | ||
ensureStoreIsUsable?: (store: Store<R, P>) => void | ||
createIntegrityChecker?: (store: Store<R, P>) => void | ||
/** @internal */ | ||
derivePresenceState?: (store: Store<R, P>) => Signal<R | null> | ||
} | ||
@@ -241,6 +244,11 @@ | ||
/** @internal */ | ||
ensureStoreIsUsable(store: Store<R, P>): void { | ||
this.options.ensureStoreIsUsable?.(store) | ||
createIntegrityChecker(store: Store<R, P>): (() => void) | undefined { | ||
return this.options.createIntegrityChecker?.(store) ?? undefined | ||
} | ||
/** @internal */ | ||
derivePresenceState(store: Store<R, P>): Signal<R | null> | undefined { | ||
return this.options.derivePresenceState?.(store) | ||
} | ||
serialize(): SerializedSchema { | ||
@@ -247,0 +255,0 @@ return { |
@@ -13,3 +13,6 @@ import { Computed, react, RESET_VALUE, transact } from 'signia' | ||
const Book = createRecordType<Book>('book', { validator: { validate: (book) => book as Book } }) | ||
const Book = createRecordType<Book>('book', { | ||
validator: { validate: (book) => book as Book }, | ||
scope: 'document', | ||
}) | ||
@@ -23,2 +26,3 @@ interface Author extends BaseRecord<'author'> { | ||
validator: { validate: (author) => author as Author }, | ||
scope: 'document', | ||
}).withDefaultProperties(() => ({ | ||
@@ -470,22 +474,29 @@ isPseudonym: false, | ||
it('flushes history before attaching listeners', async () => { | ||
store.put([Author.create({ name: 'J.R.R Tolkein', id: Author.createCustomId('tolkein') })]) | ||
const firstListener = jest.fn() | ||
store.listen(firstListener) | ||
expect(firstListener).toHaveBeenCalledTimes(0) | ||
try { | ||
// @ts-expect-error | ||
globalThis.__FORCE_RAF_IN_TESTS__ = true | ||
store.put([Author.create({ name: 'J.R.R Tolkein', id: Author.createCustomId('tolkein') })]) | ||
const firstListener = jest.fn() | ||
store.listen(firstListener) | ||
expect(firstListener).toHaveBeenCalledTimes(0) | ||
store.put([Author.create({ name: 'Chips McCoy', id: Author.createCustomId('chips') })]) | ||
store.put([Author.create({ name: 'Chips McCoy', id: Author.createCustomId('chips') })]) | ||
expect(firstListener).toHaveBeenCalledTimes(0) | ||
expect(firstListener).toHaveBeenCalledTimes(0) | ||
const secondListener = jest.fn() | ||
const secondListener = jest.fn() | ||
store.listen(secondListener) | ||
store.listen(secondListener) | ||
expect(firstListener).toHaveBeenCalledTimes(1) | ||
expect(secondListener).toHaveBeenCalledTimes(0) | ||
expect(firstListener).toHaveBeenCalledTimes(1) | ||
expect(secondListener).toHaveBeenCalledTimes(0) | ||
await new Promise((resolve) => requestAnimationFrame(resolve)) | ||
await new Promise((resolve) => requestAnimationFrame(resolve)) | ||
expect(firstListener).toHaveBeenCalledTimes(1) | ||
expect(secondListener).toHaveBeenCalledTimes(0) | ||
expect(firstListener).toHaveBeenCalledTimes(1) | ||
expect(secondListener).toHaveBeenCalledTimes(0) | ||
} finally { | ||
// @ts-expect-error | ||
globalThis.__FORCE_RAF_IN_TESTS__ = false | ||
} | ||
}) | ||
@@ -492,0 +503,0 @@ |
@@ -24,2 +24,3 @@ import { atom, EffectScheduler, RESET_VALUE } from 'signia' | ||
}, | ||
scope: 'document', | ||
}).withDefaultProperties(() => ({ age: 23 })) | ||
@@ -42,2 +43,3 @@ | ||
}, | ||
scope: 'document', | ||
}) | ||
@@ -44,0 +46,0 @@ |
@@ -22,2 +22,3 @@ import { atom, RESET_VALUE } from 'signia' | ||
}, | ||
scope: 'document', | ||
}).withDefaultProperties(() => ({ age: 23 })) | ||
@@ -40,2 +41,3 @@ | ||
}, | ||
scope: 'document', | ||
}) | ||
@@ -42,0 +44,0 @@ const authors = { |
@@ -32,2 +32,3 @@ import { assert } from '@tldraw/utils' | ||
}, | ||
scope: 'document', | ||
}) | ||
@@ -94,2 +95,3 @@ | ||
}, | ||
scope: 'document', | ||
}) | ||
@@ -112,2 +114,3 @@ | ||
}, | ||
scope: 'document', | ||
}) | ||
@@ -114,0 +117,0 @@ |
@@ -65,2 +65,3 @@ import { assert } from '@tldraw/utils' | ||
}, | ||
scope: 'document', | ||
}).withDefaultProperties(() => ({ | ||
@@ -196,2 +197,3 @@ /* STEP 6: Add any new default values for properties here */ | ||
}, | ||
scope: 'document', | ||
}).withDefaultProperties(() => ({ | ||
@@ -198,0 +200,0 @@ x: 0, |
@@ -24,2 +24,3 @@ import { BaseRecord, ID } from '../BaseRecord' | ||
}, | ||
scope: 'document', | ||
}) | ||
@@ -43,2 +44,3 @@ | ||
}, | ||
scope: 'document', | ||
}).withDefaultProperties(() => ({ | ||
@@ -45,0 +47,0 @@ isPseudonym: false, |
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
533545
9167
355
+ Added@tldraw/utils@2.0.0-canary.5cc04ef2fdc8(transitive)
+ Addednanoid@4.0.2(transitive)
- Removed@tldraw/utils@2.0.0-canary.5ba3e20cb(transitive)
- Removednanoid@3.3.8(transitive)
Updatednanoid@4.0.2