jazz-tools
CoValues
type ID
type ID<T> = CojsonInternalTypes.RawCoID & IDMarker<T>
CoValue
s.Can be used with a type argument to refer to a specific
CoValue
type.CoFeed
s that contain binary data, collaborative versions of Blob
s.BinaryCoStream.loadAsBlob(id, options):
Promise<Blob | undefined>
id: ID<FileStream>,
options: { allowUnfinished: boolean }
FileStream
as a Blob
BinaryCoStream.loadAsBlob(id, as, options):
Promise<Blob | undefined>
id: ID<FileStream>,
as: Account,
options: { allowUnfinished: boolean }
BinaryCoStream.createFromBlob(blob, options):
Promise<FileStream>
blob: File | Blob,
options: { owner: Group | Account, onProgress: (progress: number) => void } | Group | Account
FileStream
from a Blob
or File
import { co, FileStream } from "jazz-tools";
const fileStream = await FileStream.createFromBlob(file, {owner: group})
.id:
ID<FileStream>
FileStream
.toJSON():
{ id: string, _type: "BinaryCoStream", mimeType: string, totalSizeBytes: number, fileName: string, chunks: Uint8Array[], finished: boolean }
FileStream
BinaryCoStream.load<C, Depth>(id, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends FileStream
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>
FileStream
BinaryCoStream.load<C, Depth>(id, as, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends FileStream
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>
BinaryCoStream.subscribe<C, Depth>(id, depth, listener):
() => void
C extends FileStream
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
FileStream
, when you have an ID but don't have a FileStream
instance yetBinaryCoStream.subscribe<C, Depth>(id, as, depth, listener):
() => void
C extends FileStream
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
.subscribe<B, Depth>(depth, listener):
() => void
B extends FileStream
this: B,
depth: Depth & DepthsIn<B>,
listener: (value: DeeplyLoaded<B, Depth>) => void
FileStream
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
FileStream
to be uploaded to the other peers.._owner:
Group | Account
BinaryCoStream.fromRaw<V>(raw):
V
V extends CoValue
this: CoValueClass<V>,
raw: RawCoValue
._instanceID:
string
._type:
"BinaryCoStream"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
BinaryCoStream.create<S>(options):
S
S extends FileStream
this: CoValueClass<S>,
options: { owner: Group | Account } | Group | Account
.constructor:
NO TYPE
.getChunks(options):
BinaryStreamInfo & { chunks: Uint8Array[], finished: boolean } | undefined
options: { allowUnfinished: boolean }
.isBinaryStreamEnded():
boolean
.start(options):
void
options: BinaryStreamInfo
.push(data):
void
data: Uint8Array
.end():
void
.toBlob(options):
Blob | undefined
options: { allowUnfinished: boolean }
.ensureLoaded<B, Depth>(depth):
Promise<DeeplyLoaded<B, Depth> | undefined>
B extends FileStream
this: B,
depth: Depth & DepthsIn<B>
._loadedAs:
AnonymousJazzAgent | Account
CoFeed.Of<Item>(item):
CoFeed
item: IfCo<Item, Item>
CoFeed
by subclassing CoFeed.Of(...)
and passing the item schema using a co
primitive or a co.ref
.class ColorFeed extends CoFeed.Of(co.string) {}
class AnimalFeed extends CoFeed.Of(co.ref(Animal)) {}
CoList
s, but with a few key differences:- They are append-only
- They consist of several internal append-only logs, one per account session (tab, device, app instance, etc.)
- They expose those as a per-account aggregated view (default) or a precise per-session view
favDog.push("Poodle");
favDog.push("Schnowzer");
.id:
ID<CoFeed<Item>>
CoFeed
.byMe:
CoFeedEntry<Item> | undefined
CoFeed
.perSession:
{ undefined }
CoFeed
.inCurrentSession:
CoFeedEntry<Item> | undefined
CoFeed
This is a shortcut for
this.perSession
where the session ID is the current session ID..push(items):
void
items: Item[]
CoFeed
Items are appended to the current session's log. Each session (tab, device, app instance)
maintains its own append-only log, which is then aggregated into the per-account view.
// Adds items to current session's log
feed.push("item1", "item2");
// View items from current session
console.log(feed.inCurrentSession);
// View aggregated items from all sessions for current account
console.log(feed.byMe);
CoFeed.create<S>(init, options):
S
S extends CoFeed<any>
this: CoValueClass<S>,
init: TODO type conditional,
options: { owner: Group | Account } | Group | Account
CoFeed
CoFeed.load<S, Depth>(id, depth):
Promise<DeeplyLoaded<S, Depth> | undefined>
S extends CoFeed<any>
this: CoValueClass<S>,
id: ID<S>,
depth: Depth & DepthsIn<S>
CoFeed
CoFeed.load<S, Depth>(id, as, depth):
Promise<DeeplyLoaded<S, Depth> | undefined>
S extends CoFeed<any>
this: CoValueClass<S>,
id: ID<S>,
as: Account,
depth: Depth & DepthsIn<S>
CoFeed.subscribe<S, Depth>(id, depth, listener):
() => void
S extends CoFeed<any>
this: CoValueClass<S>,
id: ID<S>,
depth: Depth & DepthsIn<S>,
listener: (value: DeeplyLoaded<S, Depth>) => void
CoFeed
, when you have an ID but don't have a CoFeed
instance yetCoFeed.subscribe<S, Depth>(id, as, depth, listener):
() => void
S extends CoFeed<any>
this: CoValueClass<S>,
id: ID<S>,
as: Account,
depth: Depth & DepthsIn<S>,
listener: (value: DeeplyLoaded<S, Depth>) => void
.ensureLoaded<S, Depth>(depth):
Promise<DeeplyLoaded<S, Depth> | undefined>
S extends CoFeed<any>
this: S,
depth: Depth & DepthsIn<S>
CoFeed
is loaded to the specified depth.subscribe<S, Depth>(depth, listener):
() => void
S extends CoFeed<any>
this: S,
depth: Depth & DepthsIn<S>,
listener: (value: DeeplyLoaded<S, Depth>) => void
CoFeed
No need to provide an ID or Account since they're already part of the instance.
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
CoFeed
to be uploaded to the other peers.._owner:
Group | Account
CoFeed.fromRaw<V>(raw):
V
V extends CoValue
this: CoValueClass<V>,
raw: RawCoValue
._raw:
RawCoStream<JsonValue, JsonObject | null>
._instanceID:
string
._type:
"CoStream"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
.constructor:
NO TYPE
.pushItem(item):
void
item: Item
.toJSON():
{ id: string, _type: "CoStream", in: { undefined } }
CoFeed
._loadedAs:
AnonymousJazzAgent | Account
CoList.Of<Item>(item):
CoList
item: Item
CoList
by subclassing CoList.Of(...)
and passing the item schema using co
.class ColorList extends CoList.Of(
co.string
) {}
class AnimalList extends CoList.Of(
co.ref(Animal)
) {}
CoList
as if they were normal items on a plain array, using []
notation, etc.Since
CoList
is a subclass of Array
, you can use all the normal array methods like push
, pop
, splice
, etc.colorList[0];
colorList[3] = "yellow";
colorList.push("Kawazaki Green");
colorList.splice(1, 1);
.id:
ID<CoList<Item>>
CoList
._refs:
{ undefined } & { length: number, [iterator]: NO TYPE }
CoList
's items are a co.ref(...)
, you can use coList._refs[i]
to accessthe
Ref
instead of the potentially loaded/null value.This allows you to always get the ID or load the value manually.
animals._refs[0].id; // => ID<Animal>
animals._refs[0].value;
// => Animal | null
const animal = await animals._refs[0].load();
CoList.create<L>(items, options):
L
L extends CoList<any>
this: CoValueClass<L>,
items: UnCo<L[number]>[],
options: { owner: Group | Account } | Group | Account
The owner (a Group or Account) determines access rights to the CoMap.
The CoList will immediately be persisted and synced to connected peers.
const colours = ColorList.create(
["red", "green", "blue"],
{ owner: me }
);
const animals = AnimalList.create(
[cat, dog, fish],
{ owner: me }
);
CoList.load<C, Depth>(id, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoList<any>
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>
CoList
with a given ID, as a given account.depth
specifies if item CoValue references should be loaded as well before resolving.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or for shallowly loading only this CoList, or [itemDepth]
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.const animalsWithVets =
await ListOfAnimals.load(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
me,
[{ vet: {} }]
);
CoList.load<C, Depth>(id, as, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoList<any>
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>
CoList.subscribe<C, Depth>(id, depth, listener):
() => void
C extends CoList<any>
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
CoList
with a given ID, as a given account.Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
depth
specifies if item CoValue references should be loaded as well before calling listener
for the first time.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or for shallowly loading only this CoList, or [itemDepth]
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.Returns an unsubscribe function that you should call when you no longer need updates.
Also see the
useCoState
hook to reactively subscribe to a CoValue in a React component.const unsub = ListOfAnimals.subscribe(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
me,
{ vet: {} },
(animalsWithVets) => console.log(animalsWithVets)
);
CoList.subscribe<C, Depth>(id, as, depth, listener):
() => void
C extends CoList<any>
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
.ensureLoaded<L, Depth>(depth):
Promise<DeeplyLoaded<L, Depth> | undefined>
L extends CoList<any>
this: L,
depth: Depth & DepthsIn<L>
CoList
, ensure that items are loaded to the specified depth.Works like
CoList.load()
, but you don't need to pass the ID or the account to load as again..subscribe<L, Depth>(depth, listener):
() => void
L extends CoList<any>
this: L,
depth: Depth & DepthsIn<L>,
listener: (value: DeeplyLoaded<L, Depth>) => void
CoList
, subscribe to updates to the CoList
and ensure that items are loaded to the specified depth.Works like
CoList.subscribe()
, but you don't need to pass the ID or the account to load as again.Returns an unsubscribe function that you should call when you no longer need updates.
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
CoList
to be uploaded to the other peers.._owner:
Group | Account
.toJSON(_key, seenAbove):
any[]
_key: string,
seenAbove: ID<CoValue>[]
.[inspect]():
any[]
CoList.fromRaw<V>(raw):
V & CoList<any>
V extends CoList<any>
this: CoValueClass<V> & CoList,
raw: RawCoList<JsonValue, JsonObject | null>
._raw:
RawCoList<JsonValue, JsonObject | null>
._instanceID:
string
._type:
"CoList"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
CoList[species]:
ArrayConstructor
CoList.isArray(arg):
arg is any[]
arg: any
CoList.from<T>(arrayLike):
T[]
arrayLike: ArrayLike<T>
CoList.from<T, U>(arrayLike, mapfn, thisArg):
U[]
arrayLike: ArrayLike<T>,
mapfn: (v: T, k: number) => U,
thisArg: any
CoList.from<T>(iterable):
T[]
iterable: ArrayLike<T> | Iterable<T>
CoList.from<T, U>(iterable, mapfn, thisArg):
U[]
iterable: ArrayLike<T> | Iterable<T>,
mapfn: (v: T, k: number) => U,
thisArg: any
.constructor:
NO TYPE
._edits:
{ undefined }
._loadedAs:
AnonymousJazzAgent | Account
.push(items):
number
items: Item[]
.unshift(items):
number
items: Item[]
.pop():
Item | undefined
.shift():
Item | undefined
.splice(start, deleteCount, items):
Item[]
start: number,
deleteCount: number,
items: Item[]
.length:
number
.toString():
string
.toLocaleString():
string
.concat(items):
Item[]
items: ConcatArray<Item>[]
This method returns a new array without modifying any existing arrays.
.concat(items):
Item[]
items: ConcatArray<Item> | Item[]
This method returns a new array without modifying any existing arrays.
.join(separator):
string
separator: string
.reverse():
Item[]
This method mutates the array and returns a reference to the same array.
.slice(start, end):
Item[]
start: number,
end: number
For both start and end, a negative index can be used to indicate an offset from the end of the array.
For example, -2 refers to the second to last element of the array.
.sort(compareFn):
this
compareFn: (a: Item, b: Item) => number
This method mutates the array and returns a reference to the same array.
.indexOf(searchElement, fromIndex):
number
searchElement: Item,
fromIndex: number
.lastIndexOf(searchElement, fromIndex):
number
searchElement: Item,
fromIndex: number
.every<S>(predicate, thisArg):
this is S[]
predicate: (value: Item, index: number, array: Item[]) => value is S,
thisArg: any
.every(predicate, thisArg):
boolean
predicate: (value: Item, index: number, array: Item[]) => unknown,
thisArg: any
.some(predicate, thisArg):
boolean
predicate: (value: Item, index: number, array: Item[]) => unknown,
thisArg: any
.forEach(callbackfn, thisArg):
void
callbackfn: (value: Item, index: number, array: Item[]) => void,
thisArg: any
.map<U>(callbackfn, thisArg):
U[]
callbackfn: (value: Item, index: number, array: Item[]) => U,
thisArg: any
.filter<S>(predicate, thisArg):
S[]
predicate: (value: Item, index: number, array: Item[]) => value is S,
thisArg: any
.filter(predicate, thisArg):
Item[]
predicate: (value: Item, index: number, array: Item[]) => unknown,
thisArg: any
.reduce(callbackfn):
Item
callbackfn: (previousValue: Item, currentValue: Item, currentIndex: number, array: Item[]) => Item
.reduce(callbackfn, initialValue):
Item
callbackfn: (previousValue: Item, currentValue: Item, currentIndex: number, array: Item[]) => Item,
initialValue: Item
.reduce<U>(callbackfn, initialValue):
U
callbackfn: (previousValue: U, currentValue: Item, currentIndex: number, array: Item[]) => U,
initialValue: U
.reduceRight(callbackfn):
Item
callbackfn: (previousValue: Item, currentValue: Item, currentIndex: number, array: Item[]) => Item
.reduceRight(callbackfn, initialValue):
Item
callbackfn: (previousValue: Item, currentValue: Item, currentIndex: number, array: Item[]) => Item,
initialValue: Item
.reduceRight<U>(callbackfn, initialValue):
U
callbackfn: (previousValue: U, currentValue: Item, currentIndex: number, array: Item[]) => U,
initialValue: U
.find<S>(predicate, thisArg):
S | undefined
predicate: (value: Item, index: number, obj: Item[]) => value is S,
thisArg: any
otherwise.
.find(predicate, thisArg):
Item | undefined
predicate: (value: Item, index: number, obj: Item[]) => unknown,
thisArg: any
.findIndex(predicate, thisArg):
number
predicate: (value: Item, index: number, obj: Item[]) => unknown,
thisArg: any
otherwise.
.fill(value, start, end):
this
value: Item,
start: number,
end: number
start
to end
index to a static value
and returns the modified array.copyWithin(target, start, end):
this
target: number,
start: number,
end: number
to the same array starting at position target
.entries():
IterableIterator<[number, Item]>
.keys():
IterableIterator<number>
.values():
IterableIterator<Item>
.includes(searchElement, fromIndex):
boolean
searchElement: Item,
fromIndex: number
.flatMap<U, This>(callback, thisArg):
U[]
callback: (this: This, value: Item, index: number, array: Item[]) => TODO type typeOperator | U,
thisArg: This
a new array.
This is identical to a map followed by flat with depth 1.
.flat<A, D>(depth):
FlatArray<A, D>[]
D extends number
this: A,
depth: D
specified depth.
.at(index):
Item | undefined
index: number
.findLast<S>(predicate, thisArg):
S | undefined
predicate: (value: Item, index: number, array: Item[]) => value is S,
thisArg: any
otherwise.
.findLast(predicate, thisArg):
Item | undefined
predicate: (value: Item, index: number, array: Item[]) => unknown,
thisArg: any
.findLastIndex(predicate, thisArg):
number
predicate: (value: Item, index: number, array: Item[]) => unknown,
thisArg: any
otherwise.
.toReversed():
Item[]
.toSorted(compareFn):
Item[]
compareFn: (a: Item, b: Item) => number
.toSpliced(start, deleteCount, items):
Item[]
start: number,
deleteCount: number,
items: Item[]
.toSpliced(start, deleteCount):
Item[]
start: number,
deleteCount: number
.with(index, value):
Item[]
index: number,
value: Item
given value. If the index is negative, then it replaces from the end
of the array.
.[iterator]():
IterableIterator<Item>
[unscopables]:
{ length: boolean, toString: NO TYPE, toLocaleString: NO TYPE, pop: NO TYPE, push: NO TYPE, concat: NO TYPE, join: NO TYPE, reverse: NO TYPE, shift: NO TYPE, slice: NO TYPE, sort: NO TYPE, splice: NO TYPE, unshift: NO TYPE, indexOf: NO TYPE, lastIndexOf: NO TYPE, every: NO TYPE, some: NO TYPE, forEach: NO TYPE, map: NO TYPE, filter: NO TYPE, reduce: NO TYPE, reduceRight: NO TYPE, find: NO TYPE, findIndex: NO TYPE, fill: NO TYPE, copyWithin: NO TYPE, entries: NO TYPE, keys: NO TYPE, values: NO TYPE, includes: NO TYPE, flatMap: NO TYPE, flat: NO TYPE, at: NO TYPE, findLast: NO TYPE, findLastIndex: NO TYPE, toReversed: NO TYPE, toSorted: NO TYPE, toSpliced: NO TYPE, with: NO TYPE, [iterator]: NO TYPE, [unscopables]: boolean }
when they will be absent when used in a 'with' statement.
CoMap
and assigning field schemas with co
.Optional
co.ref(...)
fields must be marked with { optional: true }
.import { co, CoMap } from "jazz-tools";
class Person extends CoMap {
name = co.string;
age = co.number;
pet = co.ref(Animal);
car = co.ref(Car, { optional: true });
}
CoMap.Record<Value>(value):
RecordLikeCoMap
value: IfCo<Value, Value>
CoMap.Record(...)
and passing the value schema using co
. Keys are always string
.import { co, CoMap } from "jazz-tools";
class ColorToFruitMap extends CoMap.Record(
co.ref(Fruit)
) {}
// assume we have map: ColorToFruitMap
// and strawberry: Fruit
map["red"] = strawberry;
CoMap
(using co
) as if they were normal properties on a plain object, using dot notation, Object.keys()
, etc.person.name;
person["age"];
person.age = 42;
person.pet?.name;
Object.keys(person);
// => ["name", "age", "pet"]
.id:
ID<CoMap>
CoMap
._refs:
{[Key in string]: IfCo<this[Key], RefIfCoValue<this[Key]>>}
prop
is a co.ref(...)
, you can use coMaps._refs.prop
to accessthe
Ref
instead of the potentially loaded/null value.This allows you to always get the ID or load the value manually.
person._refs.pet.id; // => ID<Animal>
person._refs.pet.value;
// => Animal | null
const pet = await person._refs.pet.load();
.toJSON(_key, seenAbove):
any[]
_key: string,
seenAbove: ID<CoValue>[]
CoMap
CoMap.create<M>(init, options):
M
M extends CoMap
this: CoValueClass<M>,
init: Simplify<CoMapInit<M>>,
options: { owner: Group | Account, unique: JsonValue } | Group | Account
The owner (a Group or Account) determines access rights to the CoMap.
The CoMap will immediately be persisted and synced to connected peers.
const person = Person.create({
name: "Alice",
age: 42,
pet: cat,
}, { owner: friendGroup });
CoMap.load<C, Depth>(id, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>
CoMap
with a given ID, as a given account.depth
specifies which (if any) fields that reference other CoValues to load as well before resolving.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or {}
for shallowly loading only this CoMap, or { fieldA: depthA, fieldB: depthB }
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.const person = await Person.load(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
{ pet: {} }
);
CoMap.load<C, Depth>(id, as, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>
CoMap.subscribe<C, Depth>(id, depth, listener):
() => void
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
CoMap
with a given ID, as a given account.Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
depth
specifies which (if any) fields that reference other CoValues to load as well before calling listener
for the first time.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or {}
for shallowly loading only this CoMap, or { fieldA: depthA, fieldB: depthB }
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.Returns an unsubscribe function that you should call when you no longer need updates.
Also see the
useCoState
hook to reactively subscribe to a CoValue in a React component.const unsub = Person.subscribe(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
{ pet: {} },
(person) => console.log(person)
);
CoMap.subscribe<C, Depth>(id, as, depth, listener):
() => void
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
.ensureLoaded<M, Depth>(depth):
Promise<DeeplyLoaded<M, Depth> | undefined>
M extends CoMap
this: M,
depth: Depth & DepthsIn<M>
CoMap
, ensure that the specified fields are loaded to the specified depth.Works like
CoMap.load()
, but you don't need to pass the ID or the account to load as again..subscribe<M, Depth>(depth, listener):
() => void
M extends CoMap
this: M,
depth: Depth & DepthsIn<M>,
listener: (value: DeeplyLoaded<M, Depth>) => void
CoMap
, subscribe to updates to the CoMap
and ensure that the specified fields are loaded to the specified depth.Works like
CoMap.subscribe()
, but you don't need to pass the ID or the account to load as again.Returns an unsubscribe function that you should call when you no longer need updates.
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
CoMap
to be uploaded to the other peers.._edits:
{[Key in string]: IfCo<this[Key], LastAndAllCoMapEdits<this[Key]>>}
._owner:
Group | Account
.[inspect]():
any[]
CoMap.fromRaw<V>(raw):
V
V extends CoValue
this: CoValueClass<V>,
raw: RawCoValue
._raw:
RawCoMap<{ undefined }, JsonObject | null>
._instanceID:
string
._type:
"CoMap"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
CoMap.findUnique<M>(unique, ownerID, as):
ID<M>
M extends CoMap
this: CoValueClass<M>,
unique: JsonValue,
ownerID: ID<Group> | ID<Account>,
as: Group | AnonymousJazzAgent | Account
.applyDiff<N>(newValues):
CoMap
N extends Partial<CoMapInit<CoMap>>
newValues: N
._loadedAs:
AnonymousJazzAgent | Account
SchemaUnion.Of(...)
and passing a discriminator function that determines which concrete type to use based on the raw data.import { SchemaUnion, CoMap } from "jazz-tools";
class BaseWidget extends CoMap {
type = co.string;
}
class ButtonWidget extends BaseWidget {
type = co.literal("button");
label = co.string;
}
class SliderWidget extends BaseWidget {
type = co.literal("slider");
min = co.number;
max = co.number;
}
const WidgetUnion = SchemaUnion.Of<BaseWidget>((raw) => {
switch (raw.get("type")) {
case "button": return ButtonWidget;
case "slider": return SliderWidget;
default: throw new Error("Unknown widget type");
}
});
SchemaUnion.Of<V>(discriminator):
CoValueClass<V> & SchemaUnion
V extends CoValue
discriminator: (raw: V["_raw"]) => CoValueClass<V> & CoValueFromRaw<V>
The discriminator function receives the raw data and should return the appropriate concrete class to use for that data.
When loading a SchemaUnion, the correct subclass will be instantiated based on the discriminator.
const WidgetUnion = SchemaUnion.Of<BaseWidget>((raw) => {
switch (raw.get("type")) {
case "button": return ButtonWidget;
case "slider": return SliderWidget;
default: throw new Error("Unknown widget type");
}
});
const widget = await loadCoValue(WidgetUnion, id, me, {});
// You can narrow the returned instance to a subclass by using `instanceof`
if (widget instanceof ButtonWidget) {
console.log(widget.label);
} else if (widget instanceof SliderWidget) {
console.log(widget.min, widget.max);
}
.id:
ID<SchemaUnion>
._owner:
Group | Account
.toJSON():
any[] | string | object
.[inspect]():
any[] | string | object
._raw:
RawCoValue
._instanceID:
string
._type:
string
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
.constructor:
NO TYPE
._loadedAs:
AnonymousJazzAgent | Account
Identity & Permissions
.id:
ID<Account>
Account.load<A, Depth>(id, depth):
Promise<DeeplyLoaded<A, Depth> | undefined>
A extends Account
this: CoValueClass<A>,
id: ID<A>,
depth: Depth & DepthsIn<A>
Account.load<A, Depth>(id, as, depth):
Promise<DeeplyLoaded<A, Depth> | undefined>
A extends Account
this: CoValueClass<A>,
id: ID<A>,
as: Account,
depth: Depth & DepthsIn<A>
Account.subscribe<A, Depth>(id, depth, listener):
() => void
A extends Account
this: CoValueClass<A>,
id: ID<A>,
depth: Depth & DepthsIn<A>,
listener: (value: DeeplyLoaded<A, Depth>) => void
Account.subscribe<A, Depth>(id, as, depth, listener):
() => void
A extends Account
this: CoValueClass<A>,
id: ID<A>,
as: Account,
depth: Depth & DepthsIn<A>,
listener: (value: DeeplyLoaded<A, Depth>) => void
.ensureLoaded<A, Depth>(depth):
Promise<DeeplyLoaded<A, Depth> | undefined>
A extends Account
this: A,
depth: Depth & DepthsIn<A>
.subscribe<A, Depth>(depth, listener):
() => void
A extends Account
this: A,
depth: Depth & DepthsIn<A>,
listener: (value: DeeplyLoaded<A, Depth>) => void
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
Account
to be uploaded to the other peers..waitForAllCoValuesSync(options):
Promise<unknown[][]>
options: { timeout: number }
CoValues
to be uploaded to the other peers.._owner:
Account
.toJSON():
any[] | object
.[inspect]():
any[] | object
Account.fromRaw<V>(raw):
V
V extends CoValue
this: CoValueClass<V>,
raw: RawCoValue
._raw:
RawControlledAccount<AccountMeta> | RawAccount<AccountMeta>
._instanceID:
string
._type:
"Account"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
Account._schema:
any
Account.create<A>(options):
Promise<A>
A extends Account
this: CoValueClass<A> & Account,
options: { creationProps: { name: string }, crypto: CryptoProvider<any>, initialAgentSecret: TODO type templateLiteral, peersToLoadFrom: Peer[] }
Account.getMe<A>():
A
A extends Account
this: CoValueClass<A> & Account
Account.createAs<A>(as, options):
Promise<A>
A extends Account
this: CoValueClass<A> & Account,
as: Account,
options: { creationProps: { name: string } }
Account.fromNode<A>(node):
A
A extends Account
this: CoValueClass<A>,
node: LocalNode
.constructor:
NO TYPE
._schema:
{ profile: Schema, root: Schema }
._loadedAs:
AnonymousJazzAgent | Account
.profile:
Profile | null
.root:
CoMap | null
._refs:
{ profile: Ref<Profile> | undefined, root: Ref<CoMap> | undefined }
.isMe:
boolean
.sessionID:
TODO type templateLiteral | TODO type templateLiteral | undefined
.myRole():
"admin" | undefined
.acceptInvite<V>(valueID, inviteSecret, coValueClass):
Promise<(TODO type conditional) | undefined>
V extends CoValue
valueID: ID<V>,
inviteSecret: TODO type templateLiteral,
coValueClass: CoValueClass<V>
.applyMigration(creationProps):
Promise<void>
creationProps: { name: string, onboarding: boolean }
.migrate(creationProps):
void
creationProps: { name: string }
isControlledAccount(account):
account is Account & { isMe: true, sessionID: TODO type templateLiteral | TODO type templateLiteral, _raw: RawControlledAccount<AccountMeta> }
account: Account
.id:
ID<Group>
Group.load<C, Depth>(id, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends Group
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>
Group.load<C, Depth>(id, as, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends Group
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>
Group.subscribe<C, Depth>(id, depth, listener):
() => void
C extends Group
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
Group.subscribe<C, Depth>(id, as, depth, listener):
() => void
C extends Group
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
.ensureLoaded<G, Depth>(depth):
Promise<DeeplyLoaded<G, Depth> | undefined>
G extends Group
this: G,
depth: Depth & DepthsIn<G>
.subscribe<G, Depth>(depth, listener):
() => void
G extends Group
this: G,
depth: Depth & DepthsIn<G>,
listener: (value: DeeplyLoaded<G, Depth>) => void
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
Group
to be uploaded to the other peers.._owner:
Group | Account
.toJSON():
any[] | string | object
.[inspect]():
any[] | string | object
Group.fromRaw<V>(raw):
V
V extends CoValue
this: CoValueClass<V>,
raw: RawCoValue
._raw:
RawGroup<JsonObject | null>
._instanceID:
string
._type:
"Group"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
Group._schema:
any
Group.create<G>(options):
G
G extends Group
this: CoValueClass<G>,
options: { owner: Account } | Account
.constructor:
NO TYPE
._schema:
{ profile: Schema, root: Schema, [MembersSym]: RefEncoded<Account> }
.profile:
Profile | null
.root:
CoMap | null
._refs:
{ profile: Ref<Profile> | undefined, root: Ref<CoMap> | undefined }
.myRole():
Role | undefined
.addMember(member, role):
Group
member: Account | "everyone",
role: Role
.removeMember(member):
Group
member: Account | "everyone"
.members:
{ id: ID<this[MembersSym]> | "everyone", role: Role | undefined, ref: Ref<NonNullable<this[MembersSym]>> | undefined, account: NO TYPE }[]
.extend(parent):
Group
parent: Group
[MembersSym]:
Account | null
._loadedAs:
AnonymousJazzAgent | Account
Profile.Record<Value>(value):
RecordLikeCoMap
value: IfCo<Value, Value>
CoMap.Record(...)
and passing the value schema using co
. Keys are always string
.import { co, CoMap } from "jazz-tools";
class ColorToFruitMap extends CoMap.Record(
co.ref(Fruit)
) {}
// assume we have map: ColorToFruitMap
// and strawberry: Fruit
map["red"] = strawberry;
.id:
ID<Profile>
CoMap
._refs:
{[Key in string]: IfCo<this[Key], RefIfCoValue<this[Key]>>}
prop
is a co.ref(...)
, you can use coMaps._refs.prop
to accessthe
Ref
instead of the potentially loaded/null value.This allows you to always get the ID or load the value manually.
person._refs.pet.id; // => ID<Animal>
person._refs.pet.value;
// => Animal | null
const pet = await person._refs.pet.load();
.toJSON(_key, seenAbove):
any[]
_key: string,
seenAbove: ID<CoValue>[]
CoMap
Profile.create<M>(init, options):
M
M extends CoMap
this: CoValueClass<M>,
init: Simplify<CoMapInit<M>>,
options: { owner: Group | Account, unique: JsonValue } | Group | Account
The owner (a Group or Account) determines access rights to the CoMap.
The CoMap will immediately be persisted and synced to connected peers.
const person = Person.create({
name: "Alice",
age: 42,
pet: cat,
}, { owner: friendGroup });
Profile.load<C, Depth>(id, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>
CoMap
with a given ID, as a given account.depth
specifies which (if any) fields that reference other CoValues to load as well before resolving.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or {}
for shallowly loading only this CoMap, or { fieldA: depthA, fieldB: depthB }
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.const person = await Person.load(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
{ pet: {} }
);
Profile.load<C, Depth>(id, as, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>
Profile.subscribe<C, Depth>(id, depth, listener):
() => void
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
CoMap
with a given ID, as a given account.Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
depth
specifies which (if any) fields that reference other CoValues to load as well before calling listener
for the first time.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or {}
for shallowly loading only this CoMap, or { fieldA: depthA, fieldB: depthB }
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.Returns an unsubscribe function that you should call when you no longer need updates.
Also see the
useCoState
hook to reactively subscribe to a CoValue in a React component.const unsub = Person.subscribe(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
{ pet: {} },
(person) => console.log(person)
);
Profile.subscribe<C, Depth>(id, as, depth, listener):
() => void
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
.ensureLoaded<M, Depth>(depth):
Promise<DeeplyLoaded<M, Depth> | undefined>
M extends CoMap
this: M,
depth: Depth & DepthsIn<M>
CoMap
, ensure that the specified fields are loaded to the specified depth.Works like
CoMap.load()
, but you don't need to pass the ID or the account to load as again..subscribe<M, Depth>(depth, listener):
() => void
M extends CoMap
this: M,
depth: Depth & DepthsIn<M>,
listener: (value: DeeplyLoaded<M, Depth>) => void
CoMap
, subscribe to updates to the CoMap
and ensure that the specified fields are loaded to the specified depth.Works like
CoMap.subscribe()
, but you don't need to pass the ID or the account to load as again.Returns an unsubscribe function that you should call when you no longer need updates.
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
CoMap
to be uploaded to the other peers.._edits:
{[Key in string]: IfCo<this[Key], LastAndAllCoMapEdits<this[Key]>>}
._owner:
Group | Account
.[inspect]():
any[]
Profile.fromRaw<V>(raw):
V
V extends CoValue
this: CoValueClass<V>,
raw: RawCoValue
._raw:
RawCoMap<{ undefined }, JsonObject | null>
._instanceID:
string
._type:
"CoMap"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
Profile.findUnique<M>(unique, ownerID, as):
ID<M>
M extends CoMap
this: CoValueClass<M>,
unique: JsonValue,
ownerID: ID<Group> | ID<Account>,
as: Group | AnonymousJazzAgent | Account
.name:
co<string>
.inbox:
co<CoID<InboxRoot> | undefined>
.inboxInvite:
co<TODO type templateLiteral | undefined>
.applyDiff<N>(newValues):
Profile
N extends Partial<CoMapInit<Profile>>
newValues: N
._loadedAs:
AnonymousJazzAgent | Account
Schema definition
Encoders reflection
co reflection
type co
type co<T> = T & CoMarker | T
Abstract interfaces
.id:
ID<CoValue>
._owner:
Group | Account
.toJSON(key, seenAbove):
any[] | string | object
key: string,
seenAbove: ID<CoValue>[]
.[inspect]():
any
._raw:
RawCoValue
._type:
string
.id:
ID<CoPlainText>
CoPlainText.load<T>(id, as):
Promise<T | undefined>
T extends CoPlainText
this: CoValueClass<T>,
id: ID<T>,
as: Account
CoPlainText
with a given ID, as a given account.CoPlainText.subscribe<T>(id, listener):
() => void
T extends CoPlainText
this: CoValueClass<T>,
id: ID<T>,
listener: (value: T) => void
CoPlainText
with a given ID, as a given account.Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
Check out the
load
methods on CoMap
/CoList
/CoStream
/Group
/Account
to see which depth structures are valid to nest.Returns an unsubscribe function that you should call when you no longer need updates.
Also see the
useCoState
hook to reactively subscribe to a CoValue in a React component.CoPlainText.subscribe<T>(id, as, listener):
() => void
T extends CoPlainText
this: CoValueClass<T>,
id: ID<T>,
as: Account,
listener: (value: T) => void
.subscribe<T>(listener):
() => void
T extends CoPlainText
this: T,
listener: (value: T) => void
CoPlainText
, subscribe to updates to the CoPlainText
and ensure that the specified fields are loaded to the specified depth.Works like
CoPlainText.subscribe()
, but you don't need to pass the ID or the account to load as again.Returns an unsubscribe function that you should call when you no longer need updates.
._owner:
Group | Account
.toJSON():
string
.[inspect]():
string
._raw:
RawCoPlainText<JsonObject | null>
._type:
"CoPlainText"
CoPlainText.create<T>(text, options):
T
T extends CoPlainText
this: CoValueClass<T>,
text: string,
options: { owner: Group | Account }
CoPlainText.fromRaw<V>(raw):
V & CoPlainText
V extends CoPlainText
this: CoValueClass<V> & CoPlainText,
raw: RawCoPlainText<JsonObject | null>
CoPlainText.fromCharCode(codes):
string
codes: number[]
CoPlainText.fromCodePoint(codePoints):
string
codePoints: number[]
If length is 0, the empty string is returned.
CoPlainText.raw(template, substitutions):
string
template: { raw: TODO type typeOperator | ArrayLike<string> },
substitutions: any[]
such, the first argument will be a well formed template call site object and the rest
parameter will contain the substitution values. It can also be called directly, for example,
to interleave strings and values from your own tag function, and in this case the only thing
it needs from the first argument is the raw property.
.constructor:
NO TYPE
._loadedAs:
Account
.length:
number
.toString():
string
.valueOf():
string
.insertAfter(idx, text):
void
idx: number,
text: string
.deleteRange(range):
void
range: { from: number, to: number }
.posBefore(idx):
OpID | undefined
idx: number
.posAfter(idx):
OpID | undefined
idx: number
.idxBefore(pos):
undefined | number
pos: OpID
.idxAfter(pos):
undefined | number
pos: OpID
.charAt(pos):
string
pos: number
.charCodeAt(index):
number
index: number
.concat(strings):
string
strings: string[]
.indexOf(searchString, position):
number
searchString: string,
position: number
.lastIndexOf(searchString, position):
number
searchString: string,
position: number
.localeCompare(that):
number
that: string
.localeCompare(that, locales, options):
number
that: string,
locales: string[] | string,
options: CollatorOptions
.localeCompare(that, locales, options):
number
that: string,
locales: LocalesArgument,
options: CollatorOptions
.match(regexp):
RegExpMatchArray | null
regexp: RegExp | string
.match(matcher):
RegExpMatchArray | null
matcher: { [match]: NO TYPE }
containing the results of that search, or null if no matches are found.
.replace(searchValue, replaceValue):
string
searchValue: RegExp | string,
replaceValue: string
.replace(searchValue, replacer):
string
searchValue: RegExp | string,
replacer: (substring: string, args: any[]) => string
.replace(searchValue, replaceValue):
string
searchValue: { [replace]: NO TYPE },
replaceValue: string
@linkcode replaceValue
to the [Symbol.replace]
method on @linkcode searchValue
. This method is expected to implement its own replacement algorithm..replace(searchValue, replacer):
string
searchValue: { [replace]: NO TYPE },
replacer: (substring: string, args: any[]) => string
.search(regexp):
number
regexp: RegExp | string
.search(searcher):
number
searcher: { [search]: NO TYPE }
.slice(start, end):
string
start: number,
end: number
.split(separator, limit):
string[]
separator: RegExp | string,
limit: number
.split(splitter, limit):
string[]
splitter: { [split]: NO TYPE },
limit: number
.substring(start, end):
string
start: number,
end: number
.toLowerCase():
string
.toLocaleLowerCase(locales):
string
locales: string[] | string
.toLocaleLowerCase(locales):
string
locales: LocalesArgument
.toUpperCase():
string
.toLocaleUpperCase(locales):
string
locales: string[] | string
.toLocaleUpperCase(locales):
string
locales: LocalesArgument
.trim():
string
.substr(from, length):
string
from: number,
length: number
.codePointAt(pos):
undefined | number
pos: number
value of the UTF-16 encoded code point starting at the string element at position pos in
the String resulting from converting this object to a String.
If there is no element at that position, the result is undefined.
If a valid UTF-16 surrogate pair does not begin at pos, the result is the code unit at pos.
.includes(searchString, position):
boolean
searchString: string,
position: number
object to a String, at one or more positions that are
greater than or equal to position; otherwise, returns false.
.endsWith(searchString, endPosition):
boolean
searchString: string,
endPosition: number
same as the corresponding elements of this object (converted to a String) starting at
endPosition – length(this). Otherwise returns false.
.normalize(form):
string
form: "NFKD" | "NFKC" | "NFD" | "NFC"
named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.
.normalize(form):
string
form: string
named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.
.repeat(count):
string
count: number
the empty string is returned.
.startsWith(searchString, position):
boolean
searchString: string,
position: number
same as the corresponding elements of this object (converted to a String) starting at
position. Otherwise returns false.
.anchor(name):
string
name: string
<a>
HTML anchor element and sets the name attribute to the text value.big():
string
<big>
HTML element.blink():
string
<blink>
HTML element.bold():
string
<b>
HTML element.fixed():
string
<tt>
HTML element.fontcolor(color):
string
color: string
<font>
HTML element and sets the color attribute value.fontsize(size):
string
size: number
<font>
HTML element and sets the size attribute value.fontsize(size):
string
size: string
<font>
HTML element and sets the size attribute value.italics():
string
<i>
HTML element.link(url):
string
url: string
<a>
HTML element and sets the href attribute value.small():
string
<small>
HTML element.strike():
string
<strike>
HTML element.sub():
string
<sub>
HTML element.sup():
string
<sup>
HTML element.padStart(maxLength, fillString):
string
maxLength: number,
fillString: string
The padding is applied from the start (left) of the current string.
.padEnd(maxLength, fillString):
string
maxLength: number,
fillString: string
The padding is applied from the end (right) of the current string.
.trimEnd():
string
.trimStart():
string
.trimLeft():
string
.trimRight():
string
.matchAll(regexp):
IterableIterator<RegExpExecArray>
regexp: RegExp
containing the results of that search.
.replaceAll(searchValue, replaceValue):
string
searchValue: RegExp | string,
replaceValue: string
.replaceAll(searchValue, replacer):
string
searchValue: RegExp | string,
replacer: (substring: string, args: any[]) => string
.at(index):
undefined | string
index: number
.[iterator]():
IterableIterator<string>
Value extends CoValue
Media
ImageDefinition.Record<Value>(value):
RecordLikeCoMap
value: IfCo<Value, Value>
CoMap.Record(...)
and passing the value schema using co
. Keys are always string
.import { co, CoMap } from "jazz-tools";
class ColorToFruitMap extends CoMap.Record(
co.ref(Fruit)
) {}
// assume we have map: ColorToFruitMap
// and strawberry: Fruit
map["red"] = strawberry;
.id:
ID<ImageDefinition>
CoMap
._refs:
{[Key in string]: IfCo<this[Key], RefIfCoValue<this[Key]>>}
prop
is a co.ref(...)
, you can use coMaps._refs.prop
to accessthe
Ref
instead of the potentially loaded/null value.This allows you to always get the ID or load the value manually.
person._refs.pet.id; // => ID<Animal>
person._refs.pet.value;
// => Animal | null
const pet = await person._refs.pet.load();
.toJSON(_key, seenAbove):
any[]
_key: string,
seenAbove: ID<CoValue>[]
CoMap
ImageDefinition.create<M>(init, options):
M
M extends CoMap
this: CoValueClass<M>,
init: Simplify<CoMapInit<M>>,
options: { owner: Group | Account, unique: JsonValue } | Group | Account
The owner (a Group or Account) determines access rights to the CoMap.
The CoMap will immediately be persisted and synced to connected peers.
const person = Person.create({
name: "Alice",
age: 42,
pet: cat,
}, { owner: friendGroup });
ImageDefinition.load<C, Depth>(id, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>
CoMap
with a given ID, as a given account.depth
specifies which (if any) fields that reference other CoValues to load as well before resolving.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or {}
for shallowly loading only this CoMap, or { fieldA: depthA, fieldB: depthB }
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.const person = await Person.load(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
{ pet: {} }
);
ImageDefinition.load<C, Depth>(id, as, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>
ImageDefinition.subscribe<C, Depth>(id, depth, listener):
() => void
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
CoMap
with a given ID, as a given account.Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
depth
specifies which (if any) fields that reference other CoValues to load as well before calling listener
for the first time.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or {}
for shallowly loading only this CoMap, or { fieldA: depthA, fieldB: depthB }
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.Returns an unsubscribe function that you should call when you no longer need updates.
Also see the
useCoState
hook to reactively subscribe to a CoValue in a React component.const unsub = Person.subscribe(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
{ pet: {} },
(person) => console.log(person)
);
ImageDefinition.subscribe<C, Depth>(id, as, depth, listener):
() => void
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
.ensureLoaded<M, Depth>(depth):
Promise<DeeplyLoaded<M, Depth> | undefined>
M extends CoMap
this: M,
depth: Depth & DepthsIn<M>
CoMap
, ensure that the specified fields are loaded to the specified depth.Works like
CoMap.load()
, but you don't need to pass the ID or the account to load as again..subscribe<M, Depth>(depth, listener):
() => void
M extends CoMap
this: M,
depth: Depth & DepthsIn<M>,
listener: (value: DeeplyLoaded<M, Depth>) => void
CoMap
, subscribe to updates to the CoMap
and ensure that the specified fields are loaded to the specified depth.Works like
CoMap.subscribe()
, but you don't need to pass the ID or the account to load as again.Returns an unsubscribe function that you should call when you no longer need updates.
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
CoMap
to be uploaded to the other peers.._edits:
{[Key in string]: IfCo<this[Key], LastAndAllCoMapEdits<this[Key]>>}
._owner:
Group | Account
.[inspect]():
any[]
ImageDefinition.fromRaw<V>(raw):
V
V extends CoValue
this: CoValueClass<V>,
raw: RawCoValue
._raw:
RawCoMap<{ undefined }, JsonObject | null>
._instanceID:
string
._type:
"CoMap"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
ImageDefinition.findUnique<M>(unique, ownerID, as):
ID<M>
M extends CoMap
this: CoValueClass<M>,
unique: JsonValue,
ownerID: ID<Group> | ID<Account>,
as: Group | AnonymousJazzAgent | Account
.originalSize:
co<[number, number]>
.highestResAvailable(options):
{ res: TODO type templateLiteral, stream: FileStream } | undefined
options: { maxWidth: number }
[ItemsSym]:
co<FileStream | null>
.applyDiff<N>(newValues):
ImageDefinition
N extends Partial<CoMapInit<ImageDefinition>>
newValues: N
._loadedAs:
AnonymousJazzAgent | Account
.placeholderDataURL:
co<string>
Other
type AgentID
type AgentIDundefined = TODO type templateLiteral
type CoValueUniqueness
type CoValueUniquenessundefined = { uniqueness: JsonValue, createdAt: null | TODO type templateLiteral }
type InviteSecret
type InviteSecretundefined = TODO type templateLiteral
type SessionID
type SessionIDundefined = TODO type templateLiteral
type SyncMessage
type SyncMessageundefined = DoneMessage | NewContentMessage | KnownStateMessage | LoadMessage
I extends CoValue,
O extends CoValue | undefined
type AccountClass
type AccountClass<Acc extends Account> = CoValueClass<Acc> & { fromNode: Account["fromNode"] }
CoStream
FileStream
type CoMapInit
type CoMapInit<Map extends object> = {[Key in CoKeys<Map>]: ForceRequiredRef<Map[Key]>} & {[Key in CoKeys<Map>]: ForceRequiredRef<Map[Key]>}
type TextPos
type TextPosundefined = OpID
Combines plain text with a list of marks for formatting and annotations.
Provides methods for text manipulation, mark insertion, and tree conversion.
CoRichText.Record<Value>(value):
RecordLikeCoMap
value: IfCo<Value, Value>
CoMap.Record(...)
and passing the value schema using co
. Keys are always string
.import { co, CoMap } from "jazz-tools";
class ColorToFruitMap extends CoMap.Record(
co.ref(Fruit)
) {}
// assume we have map: ColorToFruitMap
// and strawberry: Fruit
map["red"] = strawberry;
.id:
ID<CoRichText>
CoMap
._refs:
{[Key in string]: IfCo<this[Key], RefIfCoValue<this[Key]>>}
prop
is a co.ref(...)
, you can use coMaps._refs.prop
to accessthe
Ref
instead of the potentially loaded/null value.This allows you to always get the ID or load the value manually.
person._refs.pet.id; // => ID<Animal>
person._refs.pet.value;
// => Animal | null
const pet = await person._refs.pet.load();
.toJSON(_key, seenAbove):
any[]
_key: string,
seenAbove: ID<CoValue>[]
CoMap
CoRichText.create<M>(init, options):
M
M extends CoMap
this: CoValueClass<M>,
init: Simplify<CoMapInit<M>>,
options: { owner: Group | Account, unique: JsonValue } | Group | Account
The owner (a Group or Account) determines access rights to the CoMap.
The CoMap will immediately be persisted and synced to connected peers.
const person = Person.create({
name: "Alice",
age: 42,
pet: cat,
}, { owner: friendGroup });
CoRichText.load<C, Depth>(id, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>
CoMap
with a given ID, as a given account.depth
specifies which (if any) fields that reference other CoValues to load as well before resolving.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or {}
for shallowly loading only this CoMap, or { fieldA: depthA, fieldB: depthB }
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.const person = await Person.load(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
{ pet: {} }
);
CoRichText.load<C, Depth>(id, as, depth):
Promise<DeeplyLoaded<C, Depth> | undefined>
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>
CoRichText.subscribe<C, Depth>(id, depth, listener):
() => void
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
CoMap
with a given ID, as a given account.Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
depth
specifies which (if any) fields that reference other CoValues to load as well before calling listener
for the first time.The
DeeplyLoaded
return type guarantees that corresponding referenced CoValues are loaded to the specified depth.You can pass
[]
or {}
for shallowly loading only this CoMap, or { fieldA: depthA, fieldB: depthB }
for recursively loading referenced CoValues.Check out the
load
methods on CoMap
/CoList
/CoFeed
/Group
/Account
to see which depth structures are valid to nest.Returns an unsubscribe function that you should call when you no longer need updates.
Also see the
useCoState
hook to reactively subscribe to a CoValue in a React component.const unsub = Person.subscribe(
"co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
{ pet: {} },
(person) => console.log(person)
);
CoRichText.subscribe<C, Depth>(id, as, depth, listener):
() => void
C extends CoMap
this: CoValueClass<C>,
id: ID<C>,
as: Account,
depth: Depth & DepthsIn<C>,
listener: (value: DeeplyLoaded<C, Depth>) => void
.ensureLoaded<M, Depth>(depth):
Promise<DeeplyLoaded<M, Depth> | undefined>
M extends CoMap
this: M,
depth: Depth & DepthsIn<M>
CoMap
, ensure that the specified fields are loaded to the specified depth.Works like
CoMap.load()
, but you don't need to pass the ID or the account to load as again..subscribe<M, Depth>(depth, listener):
() => void
M extends CoMap
this: M,
depth: Depth & DepthsIn<M>,
listener: (value: DeeplyLoaded<M, Depth>) => void
CoMap
, subscribe to updates to the CoMap
and ensure that the specified fields are loaded to the specified depth.Works like
CoMap.subscribe()
, but you don't need to pass the ID or the account to load as again.Returns an unsubscribe function that you should call when you no longer need updates.
.waitForSync(options):
Promise<unknown[]>
options: { timeout: number }
CoMap
to be uploaded to the other peers.._edits:
{[Key in string]: IfCo<this[Key], LastAndAllCoMapEdits<this[Key]>>}
._owner:
Group | Account
.[inspect]():
any[]
CoRichText.fromRaw<V>(raw):
V
V extends CoValue
this: CoValueClass<V>,
raw: RawCoValue
._raw:
RawCoMap<{ undefined }, JsonObject | null>
._instanceID:
string
._type:
"CoMap"
.castAs<Cl>(cl):
InstanceType<Cl>
Cl extends CoValueClass<CoValue> & CoValueFromRaw<CoValue>
cl: Cl
CoRichText.createFromPlainText(text, options):
CoRichText
text: string,
options: { owner: Group | Account }
CoRichText.createFromPlainTextAndMark<MarkClass>(text, WrapIn, extraArgs, options):
CoRichText
MarkClass extends (args: any[]) => Mark
text: string,
WrapIn: MarkClass,
extraArgs: Omit<CoMapInit<InstanceType<MarkClass>>, "endBefore" | "endAfter" | "startBefore" | "startAfter">,
options: { owner: Group | Account }
CoRichText.findUnique<M>(unique, ownerID, as):
ID<M>
M extends CoMap
this: CoValueClass<M>,
unique: JsonValue,
ownerID: ID<Group> | ID<Account>,
as: Group | AnonymousJazzAgent | Account
.text:
co<CoPlainText | null>
.marks:
co<CoList<co<Mark | null>> | null>
.insertAfter(idx, text):
void
idx: number,
text: string
.deleteRange(range):
void
range: { from: number, to: number }
.posBefore(idx):
OpID | undefined
idx: number
.posAfter(idx):
OpID | undefined
idx: number
.idxBefore(pos):
undefined | number
pos: OpID
.idxAfter(pos):
undefined | number
pos: OpID
.insertMark<MarkClass>(start, end, RangeClass, extraArgs, options):
void
MarkClass extends (args: any[]) => Mark
start: number,
end: number,
RangeClass: MarkClass,
extraArgs: Omit<CoMapInit<InstanceType<MarkClass>>, "endBefore" | "endAfter" | "startBefore" | "startAfter">,
options: { markOwner: Group | Account }
.removeMark<MarkClass>(start, end, RangeClass, options):
void
MarkClass extends (args: any[]) => Mark
start: number,
end: number,
RangeClass: MarkClass,
options: { tag: string }
.resolveMarks():
ResolvedMark<Mark>[]
.resolveAndDiffuseMarks():
ResolvedAndDiffusedMark<Mark>[]
.resolveAndDiffuseAndFocusMarks():
ResolvedAndFocusedMark<Mark>[]
.toTree(tagPrecedence):
TreeNode
tagPrecedence: string[]
.length:
number
.toString():
string
.applyDiff<N>(newValues):
CoRichText
N extends Partial<CoMapInit<CoRichText>>
newValues: N
._loadedAs:
AnonymousJazzAgent | Account
Marks reflection
type TreeLeaf
type TreeLeafundefined = { type: "leaf", start: number, end: number }
Contains plain text without any marks.
type TreeNode
type TreeNodeundefined = { type: "node", tag: string, start: number, end: number, children: TreeLeaf | TreeNode[], range: ResolvedAndFocusedMark }
Can contain other nodes or leaves, and includes formatting information.
type ResolvedMark
type ResolvedMark<R extends Mark> = { startAfter: number, startBefore: number, endAfter: number, endBefore: number, sourceMark: R }
Contains both position information and reference to the source mark.
type DeeplyLoaded
type DeeplyLoaded<V, Depth, DepthLimit extends number, CurrentDepth extends number[]> = TODO type conditional
type DepthsIn
type DepthsIn<V, DepthLimit extends number, CurrentDepth extends number[]> = never[] | (TODO type conditional)
createCoValueObservable<V, Depth>(options):
{ getCurrentValue: () => DeeplyLoaded<V, Depth> | undefined, subscribe: (cls: CoValueClass<V>, id: ID<V>, as: AnonymousJazzAgent | Account, depth: Depth & DepthsIn<V>, listener: () => void, onUnavailable: () => void) => () => void }
options: { syncResolution: boolean }
loadCoValue<V, Depth>(cls, id, as, depth):
Promise<DeeplyLoaded<V, Depth> | undefined>
cls: CoValueClass<V>,
id: ID<V>,
as: AnonymousJazzAgent | Account,
depth: Depth & DepthsIn<V>
subscribeToCoValue<V, Depth>(cls, id, as, depth, listener, onUnavailable, syncResolution):
() => void
cls: CoValueClass<V>,
id: ID<V>,
as: AnonymousJazzAgent | Account,
depth: Depth & DepthsIn<V>,
listener: (value: DeeplyLoaded<V, Depth>, unsubscribe: () => void) => void,
onUnavailable: () => void,
syncResolution: boolean
createAnonymousJazzContext({ peersToLoadFrom, crypto }):
Promise<JazzContextWithAgent>
{ peersToLoadFrom: Peer[], crypto: CryptoProvider<any> }
createJazzContext<Acc>(__namedParameters):
Promise<JazzContextWithAccount<Acc>>
ContextParamsWithAuth<Acc>
createJazzContext(__namedParameters):
Promise<JazzContextWithAgent>
BaseContextParams
createJazzContext<Acc>(options):
Promise<JazzContext<Acc>>
options: ContextParamsWithAuth<Acc> | BaseContextParams
ephemeralCredentialsAuth():
AuthMethod
fixedCredentialsAuth(credentials):
AuthMethod
credentials: { accountID: ID<Account>, secret: TODO type templateLiteral }
randomSessionProvider(accountID, crypto):
Promise<{ sessionID: TODO type templateLiteral | TODO type templateLiteral, sessionDone: () => void }>
accountID: ID<Account>,
crypto: CryptoProvider<any>
type AuthResult
type AuthResultundefined = { type: "new", creationProps: { name: string, anonymous: boolean }, saveCredentials: (credentials: Credentials) => Promise<void>, onSuccess: () => void, onError: (error: Error | string) => void, logOut: () => void, initialSecret: AgentSecret } | { type: "existing", credentials: Credentials, onSuccess: () => void, onError: (error: Error | string) => void, logOut: () => void, saveCredentials: (credentials: Credentials) => Promise<void> }
type Credentials
type Credentialsundefined = { accountID: ID<Account>, secret: AgentSecret }