@yeti-cgi/aux-common
Advanced tools
Comparing version 0.3.11 to 0.3.12
@@ -1,2 +0,1 @@ | ||
import { WeaveReference } from '../causal-trees/Weave'; | ||
import { AuxOp, FileOp, TagOp, InsertOp, ValueOp, DeleteOp } from './AuxOpTypes'; | ||
@@ -21,3 +20,3 @@ import { CausalTree } from '../causal-trees/CausalTree'; | ||
*/ | ||
root(): WeaveReference<import("./AuxOpTypes").RootOp>; | ||
root(): Atom<import("./AuxOpTypes").RootOp>; | ||
/** | ||
@@ -27,3 +26,3 @@ * Creates a new file atom and adds it to the tree. | ||
*/ | ||
file(id: string): WeaveReference<FileOp>; | ||
file(id: string): Atom<FileOp>; | ||
/** | ||
@@ -34,3 +33,3 @@ * Creates a new tag for a file and adds it to the tree. | ||
*/ | ||
tag(name: string, fileAtom: Atom<FileOp> | AtomId): WeaveReference<TagOp>; | ||
tag(name: string, fileAtom: Atom<FileOp> | AtomId): Atom<TagOp>; | ||
/** | ||
@@ -41,3 +40,3 @@ * Creates a new value for a tag and adds it to the tree. | ||
*/ | ||
val(val: any, tagAtom: Atom<TagOp> | AtomId): WeaveReference<ValueOp>; | ||
val(val: any, tagAtom: Atom<TagOp> | AtomId): Atom<ValueOp>; | ||
/** | ||
@@ -49,3 +48,3 @@ * Creates a new delete operation for the given file, insertion, or value and adds it to the tree. | ||
*/ | ||
delete(atom: Atom<FileOp> | Atom<TagOp> | Atom<InsertOp> | Atom<ValueOp> | AtomId, start?: number, end?: number): WeaveReference<DeleteOp>; | ||
delete(atom: Atom<FileOp> | Atom<TagOp> | Atom<InsertOp> | Atom<ValueOp> | AtomId, start?: number, end?: number): Atom<DeleteOp>; | ||
/** | ||
@@ -57,3 +56,3 @@ * Creates a new insert operation for the given value or insertion and adds it to the tree. | ||
*/ | ||
insert(index: number, text: string, atom: Atom<ValueOp> | Atom<TagOp> | Atom<InsertOp> | AtomId): WeaveReference<InsertOp>; | ||
insert(index: number, text: string, atom: Atom<ValueOp> | Atom<TagOp> | Atom<InsertOp> | AtomId): Atom<InsertOp>; | ||
/** | ||
@@ -66,3 +65,3 @@ * Inserts the given text into the given tag or value on the given file. | ||
*/ | ||
insertIntoTagValue(file: AuxFile, tag: string, text: string, index: number): WeaveReference<InsertOp>; | ||
insertIntoTagValue(file: AuxFile, tag: string, text: string, index: number): Atom<InsertOp>; | ||
/** | ||
@@ -75,3 +74,3 @@ * Inserts the given text into the given tag name. | ||
*/ | ||
insertIntoTagName(file: AuxFile, tag: string, text: string, index: number): WeaveReference<InsertOp>; | ||
insertIntoTagName(file: AuxFile, tag: string, text: string, index: number): Atom<InsertOp>; | ||
/** | ||
@@ -84,3 +83,3 @@ * Deletes a segment of text from the given tag's value. | ||
*/ | ||
deleteFromTagValue(file: AuxFile, tag: string, index: number, length: number): WeaveReference<DeleteOp>[]; | ||
deleteFromTagValue(file: AuxFile, tag: string, index: number, length: number): Atom<DeleteOp>[]; | ||
/** | ||
@@ -93,3 +92,3 @@ * Deletes a segment of text from the given tag's name. | ||
*/ | ||
deleteFromTagName(file: AuxFile, tag: string, index: number, length: number): WeaveReference<DeleteOp>[]; | ||
deleteFromTagName(file: AuxFile, tag: string, index: number, length: number): Atom<DeleteOp>[]; | ||
/** | ||
@@ -100,3 +99,3 @@ * Adds the given events to the tree. | ||
*/ | ||
addEvents(events: FileEvent[], value?: AuxState): WeaveReference<AuxOp>[]; | ||
addEvents(events: FileEvent[], value?: AuxState): Atom<AuxOp>[]; | ||
/** | ||
@@ -106,3 +105,3 @@ * Removes the given file from the state by marking it as deleted. | ||
*/ | ||
removeFile(file: AuxFile): WeaveReference<AuxOp>[]; | ||
removeFile(file: AuxFile): Atom<AuxOp>[]; | ||
/** | ||
@@ -112,3 +111,3 @@ * Adds the given file to the tree. | ||
*/ | ||
addFile(file: File): WeaveReference<AuxOp>[]; | ||
addFile(file: File): Atom<AuxOp>[]; | ||
/** | ||
@@ -119,3 +118,3 @@ * Updates the given file. | ||
*/ | ||
updateFile(file: AuxFile, newData: PartialFile): WeaveReference<AuxOp>[]; | ||
updateFile(file: AuxFile, newData: PartialFile): Atom<AuxOp>[]; | ||
/** | ||
@@ -127,4 +126,5 @@ * Applies the given state to the tree. | ||
*/ | ||
applyState(state: FilesState, value?: AuxState): WeaveReference<AuxOp>[]; | ||
protected collectGarbage(refs: WeaveReference<AuxOp>[]): void; | ||
applyState(state: FilesState, value?: AuxState): Atom<AuxOp>[]; | ||
fork(): AuxCausalTree; | ||
protected collectGarbage(refs: Atom<AuxOp>[]): Atom<AuxOp>[]; | ||
} |
@@ -31,2 +31,5 @@ import { AuxOpType } from './AuxOpTypes'; | ||
file(id) { | ||
if (this.weave.atoms.length === 0) { | ||
throw new Error('Cannot add a file atom without a root atom.'); | ||
} | ||
return this.create(file(id), this.weave.atoms[0]); | ||
@@ -150,3 +153,3 @@ } | ||
} | ||
return [this.delete(file.metadata.ref.atom)]; | ||
return [this.delete(file.metadata.ref)]; | ||
} | ||
@@ -162,4 +165,4 @@ /** | ||
let refs = flatMap(tags, t => { | ||
const tag = this.tag(t, f.atom); | ||
const val = this.val(file.tags[t], tag.atom); | ||
const tag = this.tag(t, f); | ||
const val = this.val(file.tags[t], tag); | ||
return [tag, val]; | ||
@@ -193,3 +196,3 @@ }); | ||
// tag is on the file | ||
const val = this.val(newVal, tagMeta.ref.atom); | ||
const val = this.val(newVal, tagMeta.ref); | ||
return [val]; | ||
@@ -202,4 +205,4 @@ } | ||
else { | ||
const tag = this.tag(t, file.metadata.ref.atom); | ||
const val = this.val(newVal, tag.atom); | ||
const tag = this.tag(t, file.metadata.ref); | ||
const val = this.val(newVal, tag); | ||
return [tag, val]; | ||
@@ -232,11 +235,17 @@ } | ||
} | ||
fork() { | ||
const stored = this.export(); | ||
return new AuxCausalTree(stored); | ||
} | ||
collectGarbage(refs) { | ||
let removed = []; | ||
for (let i = 0; i < refs.length; i++) { | ||
const ref = refs[i]; | ||
if (ref.atom.value.type === AuxOpType.value) { | ||
this.weave.removeBefore(ref); | ||
const atom = refs[i]; | ||
if (atom.value.type === AuxOpType.value) { | ||
removed.push(...this.weave.removeBefore(atom)); | ||
} | ||
} | ||
return removed; | ||
} | ||
} | ||
//# sourceMappingURL=AuxCausalTree.js.map |
import { AtomReducer } from "../causal-trees/AtomReducer"; | ||
import { AuxOp } from "./AuxOpTypes"; | ||
import { Weave, WeaveReference } from '../causal-trees/Weave'; | ||
import { Weave } from '../causal-trees/Weave'; | ||
import { WeaveTraverser } from "../causal-trees/WeaveTraverser"; | ||
import { AuxState, AuxValueMetadata, AuxRef, AuxSequenceMetadata } from "./AuxState"; | ||
import { Atom } from "../causal-trees/Atom"; | ||
/** | ||
@@ -10,3 +11,3 @@ * Defines a type for a map from weave references to their calculated values. | ||
export interface AuxReducerMetadata { | ||
cache: Map<WeaveReference<AuxOp>, any>; | ||
cache: Map<Atom<AuxOp>, any>; | ||
} | ||
@@ -30,7 +31,7 @@ export interface AuxSequence { | ||
export declare class AuxReducer implements AtomReducer<AuxOp, AuxState, AuxReducerMetadata> { | ||
eval(weave: Weave<AuxOp>, refs?: WeaveReference<AuxOp>[], old?: AuxState, metadata?: AuxReducerMetadata): [AuxState, AuxReducerMetadata]; | ||
eval(weave: Weave<AuxOp>, refs?: Atom<AuxOp>[], old?: AuxState, metadata?: AuxReducerMetadata): [AuxState, AuxReducerMetadata]; | ||
private _evalFile; | ||
private _evalTag; | ||
private _evalTagValue; | ||
evalSequence(tree: WeaveTraverser<AuxOp>, parent: WeaveReference<AuxOp>, value: any, metadata: AuxReducerMetadata): AuxSequence; | ||
evalSequence(tree: WeaveTraverser<AuxOp>, parent: Atom<AuxOp>, value: any, metadata: AuxReducerMetadata): AuxSequence; | ||
} | ||
@@ -37,0 +38,0 @@ /** |
@@ -32,12 +32,12 @@ import { AuxOpType } from "./AuxOpTypes"; | ||
const ref = tree.next(); | ||
if (ref.atom.value.type === AuxOpType.file) { | ||
const id = ref.atom.value.id; | ||
if (ref.value.type === AuxOpType.file) { | ||
const id = ref.value.id; | ||
if (typeof value[id] === 'undefined') { | ||
let file = metadata.cache.get(ref); | ||
if (typeof file === 'undefined') { | ||
file = this._evalFile(tree, ref, ref.atom.value, metadata); | ||
file = this._evalFile(tree, ref, ref.value, metadata); | ||
metadata.cache.set(ref, file); | ||
} | ||
else { | ||
tree.skip(ref.atom.id); | ||
tree.skip(ref.id); | ||
} | ||
@@ -59,12 +59,12 @@ if (file) { | ||
}; | ||
while (tree.peek(parent.atom.id)) { | ||
while (tree.peek(parent.id)) { | ||
const ref = tree.next(); | ||
if (ref.atom.value.type === AuxOpType.delete) { | ||
tree.skip(parent.atom.id); | ||
if (ref.value.type === AuxOpType.delete) { | ||
tree.skip(parent.id); | ||
return null; | ||
} | ||
else if (ref.atom.value.type === AuxOpType.tag) { | ||
else if (ref.value.type === AuxOpType.tag) { | ||
let tag = metadata.cache.get(ref); | ||
if (typeof tag === 'undefined') { | ||
tag = this._evalTag(tree, ref, ref.atom.value, metadata); | ||
tag = this._evalTag(tree, ref, ref.value, metadata); | ||
if (typeof tag !== 'undefined') { | ||
@@ -75,3 +75,3 @@ metadata.cache.set(ref, tag); | ||
else { | ||
tree.skip(ref.atom.id); | ||
tree.skip(ref.id); | ||
} | ||
@@ -99,4 +99,4 @@ if (tag && tag.name.value && tag.value.hasValue && typeof meta.tags[tag.name.value] === 'undefined') { | ||
_evalTag(tree, parent, tag, metadata) { | ||
let name = this.evalSequence(tree.fork(), parent, parent.atom.value.name, metadata); | ||
let value = this._evalTagValue(tree, parent, parent.atom.value, metadata); | ||
let name = this.evalSequence(tree.fork(), parent, parent.value.name, metadata); | ||
let value = this._evalTagValue(tree, parent, parent.value, metadata); | ||
return { | ||
@@ -114,11 +114,11 @@ name, | ||
}; | ||
while (tree.peek(parent.atom.id)) { | ||
while (tree.peek(parent.id)) { | ||
const ref = tree.next(); | ||
if (hasValue) { | ||
tree.skip(parent.atom.id); | ||
tree.skip(parent.id); | ||
} | ||
else if (ref.atom.value.type === AuxOpType.value) { | ||
else if (ref.value.type === AuxOpType.value) { | ||
hasValue = true; | ||
meta.ref = ref; | ||
const { value: val, meta: valMeta } = this.evalSequence(tree, ref, ref.atom.value.value, metadata); | ||
const { value: val, meta: valMeta } = this.evalSequence(tree, ref, ref.value.value, metadata); | ||
value = val; | ||
@@ -134,3 +134,3 @@ meta.sequence = valMeta; | ||
let meta = null; | ||
if (parent.atom.value.type === AuxOpType.value || parent.atom.value.type === AuxOpType.tag) { | ||
if (parent.value.type === AuxOpType.value || parent.value.type === AuxOpType.tag) { | ||
if (typeof value === 'string') { | ||
@@ -140,6 +140,6 @@ meta = createSequenceMeta(parent, value); | ||
} | ||
else if (parent.atom.value.type === AuxOpType.insert) { | ||
else if (parent.value.type === AuxOpType.insert) { | ||
meta = createSequenceMeta(parent, value); | ||
} | ||
while (tree.peek(parent.atom.id)) { | ||
while (tree.peek(parent.id)) { | ||
const ref = tree.next(); | ||
@@ -150,5 +150,5 @@ if (typeof value !== 'string') { | ||
} | ||
if (ref.atom.value.type === AuxOpType.delete) { | ||
const start = Math.max(ref.atom.value.start || 0, 0); | ||
const end = ref.atom.value.end || value.length; | ||
if (ref.value.type === AuxOpType.delete) { | ||
const start = Math.max(ref.value.start || 0, 0); | ||
const end = ref.value.end || value.length; | ||
const length = end - start; | ||
@@ -163,13 +163,13 @@ const offset = calculateOffsetForIndex(start, offsets, true); | ||
} | ||
else if (ref.atom.value.type === AuxOpType.insert) { | ||
else if (ref.value.type === AuxOpType.insert) { | ||
let sequence = metadata.cache.get(ref); | ||
if (typeof sequence === 'undefined') { | ||
sequence = this.evalSequence(tree, ref, ref.atom.value.text, metadata); | ||
sequence = this.evalSequence(tree, ref, ref.value.text, metadata); | ||
metadata.cache.set(ref, sequence); | ||
} | ||
else { | ||
tree.skip(ref.atom.id); | ||
tree.skip(ref.id); | ||
} | ||
const offset = calculateOffsetForIndex(ref.atom.value.index, offsets, false); | ||
const index = ref.atom.value.index + offset; | ||
const offset = calculateOffsetForIndex(ref.value.index, offsets, false); | ||
const index = ref.value.index + offset; | ||
offsets.push(index, sequence.value.length); | ||
@@ -176,0 +176,0 @@ const newMetaRefs = sequence.meta.refs; |
import { Object } from '../Files'; | ||
import { WeaveReference } from '../causal-trees/Weave'; | ||
import { Atom } from '../causal-trees/Atom'; | ||
import { AuxOp, FileOp, ValueOp, TagOp } from './AuxOpTypes'; | ||
export declare type AuxFile = AuxObject; | ||
export declare type AuxRef = WeaveReference<AuxOp>; | ||
export declare type AuxRef = Atom<AuxOp>; | ||
/** | ||
@@ -19,3 +19,3 @@ * Defines an interface that contains state for an AUX Object. | ||
export interface AuxFileMetadata { | ||
ref: WeaveReference<FileOp>; | ||
ref: Atom<FileOp>; | ||
tags: { | ||
@@ -29,3 +29,3 @@ [key: string]: AuxTagMetadata; | ||
export interface AuxTagMetadata { | ||
ref: WeaveReference<TagOp>; | ||
ref: Atom<TagOp>; | ||
name: AuxSequenceMetadata; | ||
@@ -38,3 +38,3 @@ value: AuxValueMetadata; | ||
export interface AuxValueMetadata { | ||
ref: WeaveReference<ValueOp>; | ||
ref: Atom<ValueOp>; | ||
/** | ||
@@ -41,0 +41,0 @@ * The sequence that this value is using. |
@@ -1,2 +0,2 @@ | ||
import { WeaveReference, PrecalculatedOp, RealtimeCausalTree, Weave } from "../causal-trees"; | ||
import { Atom, PrecalculatedOp, RealtimeCausalTree, Weave } from "../causal-trees"; | ||
import { AuxFile, AuxTagMetadata } from "./AuxState"; | ||
@@ -18,3 +18,3 @@ import { InsertOp, DeleteOp, AuxOp, FileOp } from "./AuxOpTypes"; | ||
*/ | ||
export declare function getAtomFile(weave: Weave<AuxOp>, ref: WeaveReference<AuxOp>): WeaveReference<FileOp>; | ||
export declare function getAtomFile(weave: Weave<AuxOp>, ref: Atom<AuxOp>): Atom<FileOp>; | ||
/** | ||
@@ -21,0 +21,0 @@ * Gets the metadata for the given tag. |
@@ -18,4 +18,4 @@ import { precalculatedOp } from "../causal-trees"; | ||
events.forEach((e) => { | ||
if (e.atom.value.type === AuxOpType.file) { | ||
const id = e.atom.value.id; | ||
if (e.value.type === AuxOpType.file) { | ||
const id = e.value.id; | ||
const val = tree.tree.value[id]; | ||
@@ -27,6 +27,6 @@ if (val) { | ||
} | ||
else if (e.atom.value.type === AuxOpType.delete) { | ||
let cause = tree.tree.weave.getAtom(e.atom.cause); | ||
if (cause.atom.value.type === AuxOpType.file) { | ||
const id = cause.atom.value.id; | ||
else if (e.value.type === AuxOpType.delete) { | ||
let cause = tree.tree.weave.getAtom(e.cause); | ||
if (cause.value.type === AuxOpType.file) { | ||
const id = cause.value.id; | ||
deletedFiles.push(id); | ||
@@ -39,3 +39,3 @@ return; | ||
if (file) { | ||
const id = file.atom.value.id; | ||
const id = file.value.id; | ||
const val = tree.tree.value[id]; | ||
@@ -56,3 +56,3 @@ if (!updatedFiles[id] && val) { | ||
// TODO: Work with all domains | ||
return sortBy(diff.addedFiles, f => !f.tags['aux.builder.context'], f => !f.tags['aux.player.context'], f => f.id); | ||
return sortBy(diff.addedFiles, f => !f.tags['aux.builder.context'], f => f.id); | ||
})); | ||
@@ -71,9 +71,9 @@ const fileRemoved = stateDiffs.pipe(flatMap(diff => diff.removedFiles)); | ||
export function getAtomFile(weave, ref) { | ||
if (ref.atom.value.type === AuxOpType.file) { | ||
if (ref.value.type === AuxOpType.file) { | ||
return ref; | ||
} | ||
if (!ref.atom.cause) { | ||
if (!ref.cause) { | ||
return null; | ||
} | ||
const cause = weave.getAtom(ref.atom.cause); | ||
const cause = weave.getAtom(ref.cause); | ||
return getAtomFile(weave, cause); | ||
@@ -106,3 +106,3 @@ } | ||
const result = calculateSequenceRef(tagMeta.value.sequence, index); | ||
return precalculatedOp(insert(result.index, text), result.ref.atom); | ||
return precalculatedOp(insert(result.index, text), result.ref); | ||
} | ||
@@ -124,3 +124,3 @@ else { | ||
const result = calculateSequenceRef(tagMeta.name, index); | ||
return precalculatedOp(insert(result.index, text), result.ref.atom); | ||
return precalculatedOp(insert(result.index, text), result.ref); | ||
} | ||
@@ -142,3 +142,3 @@ else { | ||
const result = calculateSequenceRefs(tagMeta.value.sequence, index, length); | ||
return result.map(r => precalculatedOp(del(r.index, r.index + r.length), r.ref.atom, 1)); | ||
return result.map(r => precalculatedOp(del(r.index, r.index + r.length), r.ref, 1)); | ||
} | ||
@@ -160,3 +160,3 @@ else { | ||
const result = calculateSequenceRefs(tagMeta.name, index); | ||
return result.map(r => precalculatedOp(del(r.index, r.index + r.length), r.ref.atom, 1)); | ||
return result.map(r => precalculatedOp(del(r.index, r.index + r.length), r.ref, 1)); | ||
} | ||
@@ -163,0 +163,0 @@ else { |
@@ -76,2 +76,15 @@ /** | ||
/** | ||
* Defines an interface that represents an atom that has been archived. | ||
*/ | ||
export interface ArchivedAtom { | ||
/** | ||
* The key that relates this atom to a particular tree/weave. | ||
*/ | ||
key: string; | ||
/** | ||
* The atom that was archived. | ||
*/ | ||
atom: Atom<any>; | ||
} | ||
/** | ||
* Creates a new atom. | ||
@@ -83,1 +96,7 @@ * @param id | ||
export declare function atom<T extends AtomOp>(id: AtomId, cause: AtomId, value: T): Atom<T>; | ||
/** | ||
* Converts the given atom ID into a string that is suitable for | ||
* storage. | ||
* @param id The ID. | ||
*/ | ||
export declare function atomIdToString(id: AtomId): string; |
@@ -45,2 +45,10 @@ import { getHashBuffer } from "./Hash"; | ||
} | ||
/** | ||
* Converts the given atom ID into a string that is suitable for | ||
* storage. | ||
* @param id The ID. | ||
*/ | ||
export function atomIdToString(id) { | ||
return `${id.site}@${id.timestamp}:${id.priority}`; | ||
} | ||
//# sourceMappingURL=Atom.js.map |
import { AtomOp, Atom, AtomId } from "./Atom"; | ||
import { WeaveReference } from "./Weave"; | ||
import { SiteInfo } from "./SiteIdInfo"; | ||
@@ -26,6 +25,5 @@ /** | ||
* Updates the timestamp stored by this factory. | ||
* This should only be called upon receiving new never-seen events from a remote source. | ||
* @param newTimestamp The latest timestamp seen by the app. | ||
* @param atom The atom that is being added to the tree. | ||
*/ | ||
updateTime(newTimestamp: number): void; | ||
updateTime<T extends TOp>(atom: Atom<T>): void; | ||
/** | ||
@@ -36,3 +34,3 @@ * Creates a new Atom with the given op. | ||
*/ | ||
create<T extends TOp>(op: T, cause: WeaveReference<TOp> | Atom<TOp> | AtomId, priority?: number): Atom<T>; | ||
create<T extends TOp>(op: T, cause: Atom<TOp> | AtomId, priority?: number): Atom<T>; | ||
} |
@@ -29,7 +29,11 @@ import { atom, atomId } from "./Atom"; | ||
* Updates the timestamp stored by this factory. | ||
* This should only be called upon receiving new never-seen events from a remote source. | ||
* @param newTimestamp The latest timestamp seen by the app. | ||
* @param atom The atom that is being added to the tree. | ||
*/ | ||
updateTime(newTimestamp) { | ||
this._time = Math.max(this._time, newTimestamp) + 1; | ||
updateTime(atom) { | ||
if (atom.id.site !== this.site) { | ||
this._time = Math.max(this._time, atom.id.timestamp) + 1; | ||
} | ||
else { | ||
this._time = Math.max(this._time, atom.id.timestamp); | ||
} | ||
} | ||
@@ -44,4 +48,3 @@ /** | ||
if (cause) { | ||
causeId = (!!cause.atom ? cause.atom.id : | ||
cause.id ? cause.id : cause); | ||
causeId = (!!cause.id ? cause.id : cause); | ||
} | ||
@@ -48,0 +51,0 @@ this._time += 1; |
@@ -1,3 +0,3 @@ | ||
import { AtomOp } from "./Atom"; | ||
import { Weave, WeaveReference } from "./Weave"; | ||
import { AtomOp, Atom } from "./Atom"; | ||
import { Weave } from "./Weave"; | ||
/** | ||
@@ -17,3 +17,3 @@ * Defines an interface for a reducer that can convert a weave of Atom operations | ||
*/ | ||
eval(weave: Weave<TOp>, refs?: WeaveReference<TOp>[], value?: TValue, meta?: TMetadata): [TValue, TMetadata]; | ||
eval(weave: Weave<TOp>, refs?: Atom<TOp>[], value?: TValue, meta?: TMetadata): [TValue, TMetadata]; | ||
} |
import { AtomOp, Atom, AtomId } from "./Atom"; | ||
import { Weave, WeaveReference } from "./Weave"; | ||
import { Weave } from "./Weave"; | ||
import { AtomFactory } from "./AtomFactory"; | ||
@@ -23,2 +23,3 @@ import { AtomReducer } from "./AtomReducer"; | ||
private _atomAdded; | ||
private _atomArchived; | ||
private _isBatching; | ||
@@ -62,4 +63,8 @@ private _batch; | ||
*/ | ||
readonly atomAdded: Subject<WeaveReference<TOp>[]>; | ||
readonly atomAdded: Subject<Atom<TOp>[]>; | ||
/** | ||
* Gets an observable that resolves whenever one or more atoms are garbage collected and should be archived. | ||
*/ | ||
readonly atomsArchived: Subject<Atom<TOp>[]>; | ||
/** | ||
* Creates a new Causal Tree with the given site ID. | ||
@@ -73,3 +78,3 @@ * @param tree The stored tree that this causal tree should be made from. | ||
*/ | ||
root(): WeaveReference<TOp>; | ||
root(): Atom<TOp>; | ||
/** | ||
@@ -79,3 +84,3 @@ * Adds the given atom to this Causal Tree's history. | ||
*/ | ||
add<T extends TOp>(atom: Atom<T>): WeaveReference<T>; | ||
add<T extends TOp>(atom: Atom<T>): Atom<T>; | ||
/** | ||
@@ -85,3 +90,3 @@ * Adds the given list of references to this causal tree's history. | ||
*/ | ||
addMany(refs: WeaveReference<TOp>[]): WeaveReference<TOp>[]; | ||
addMany(refs: Atom<TOp>[]): Atom<TOp>[]; | ||
/** | ||
@@ -98,4 +103,9 @@ * Batches all the operations in the given function so that | ||
*/ | ||
importWeave<T extends TOp>(refs: WeaveReference<T>[]): WeaveReference<TOp>[]; | ||
importWeave<T extends TOp>(refs: Atom<T>[]): Atom<TOp>[]; | ||
/** | ||
* Imports the given tree into this one and returns the list of atoms that were imported. | ||
* @param tree The tree to import. | ||
*/ | ||
import<T extends TOp>(tree: StoredCausalTree<T>): Atom<TOp>[]; | ||
/** | ||
* Exports this tree into a storable format. | ||
@@ -110,3 +120,3 @@ */ | ||
*/ | ||
create<T extends TOp>(op: T, parent: WeaveReference<TOp> | Atom<TOp> | AtomId, priority?: number): WeaveReference<T>; | ||
create<T extends TOp>(op: T, parent: Atom<TOp> | Atom<TOp> | AtomId, priority?: number): Atom<T>; | ||
/** | ||
@@ -116,3 +126,3 @@ * Creates a new atom from the given precalculated operation and adds it to the tree's history. | ||
*/ | ||
createFromPrecalculated<T extends TOp>(precalc: PrecalculatedOp<T>): WeaveReference<T>; | ||
createFromPrecalculated<T extends TOp>(precalc: PrecalculatedOp<T>): Atom<T>; | ||
/** | ||
@@ -122,3 +132,3 @@ * Creates a new atom from the given precalculated operation and adds it to the tree's history. | ||
*/ | ||
createManyFromPrecalculated<T extends TOp>(precalc: PrecalculatedOp<T>[]): WeaveReference<T>[]; | ||
createManyFromPrecalculated<T extends TOp>(precalc: PrecalculatedOp<T>[]): Atom<T>[]; | ||
/** | ||
@@ -134,6 +144,16 @@ * Registers the given site in this tree's known sites list. | ||
/** | ||
* Forks the given causal tree and returns a new tree that contains the same state. | ||
* Note that this method does not copy over configuration options such as collectGarbage. | ||
* Also note that this method should be overridden in child classes to ensure that the proper type | ||
* is being created. | ||
* @param type The type of the tree that is being forked. | ||
* @param tree The tree to fork. | ||
*/ | ||
fork(): CausalTree<TOp, TValue, TMetadata>; | ||
/** | ||
* Performs garbage collection of the tree's weave after a set of atoms were added to the tree. | ||
* Returns the references that were removed from the tree. | ||
* @param refs The weave references that were added to the tree. | ||
*/ | ||
protected collectGarbage(refs: WeaveReference<TOp>[]): void; | ||
protected collectGarbage(refs: Atom<TOp>[]): Atom<TOp>[]; | ||
/** | ||
@@ -145,4 +165,4 @@ * Recalculates the values associated the given references. | ||
*/ | ||
protected recalculateValues(refs: WeaveReference<TOp>[]): void; | ||
protected recalculateValues(refs: Atom<TOp>[]): void; | ||
private _calculateValue; | ||
} |
@@ -26,8 +26,7 @@ import { Weave } from "./Weave"; | ||
this._atomAdded = new Subject(); | ||
this._atomArchived = new Subject(); | ||
this._isBatching = false; | ||
this._batch = []; | ||
this.garbageCollect = false; | ||
if (tree.weave) { | ||
this.importWeave(tree.weave); | ||
} | ||
this.import(tree); | ||
} | ||
@@ -84,2 +83,8 @@ /** | ||
/** | ||
* Gets an observable that resolves whenever one or more atoms are garbage collected and should be archived. | ||
*/ | ||
get atomsArchived() { | ||
return this._atomArchived; | ||
} | ||
/** | ||
* Creates a root element on this tree. | ||
@@ -95,5 +100,3 @@ */ | ||
add(atom) { | ||
if (atom.id.site !== this.site.id) { | ||
this.factory.updateTime(atom.id.timestamp); | ||
} | ||
this.factory.updateTime(atom); | ||
const ref = this.weave.insert(atom); | ||
@@ -107,3 +110,6 @@ if (ref) { | ||
if (this.garbageCollect) { | ||
this.collectGarbage(refs); | ||
const removed = this.collectGarbage(refs); | ||
if (removed.length > 0) { | ||
this._atomArchived.next(removed); | ||
} | ||
} | ||
@@ -121,8 +127,9 @@ [this._value, this._metadata] = this._calculateValue(refs); | ||
addMany(refs) { | ||
const atoms = sortBy(refs, a => a.id.timestamp); | ||
return this.batch(() => { | ||
let added = []; | ||
for (let i = 0; i < refs.length; i++) { | ||
let ref = refs[i]; | ||
if (ref) { | ||
let result = this.add(ref.atom); | ||
for (let i = 0; i < atoms.length; i++) { | ||
let atom = atoms[i]; | ||
if (atom) { | ||
let result = this.add(atom); | ||
if (result) { | ||
@@ -152,3 +159,6 @@ added.push(result); | ||
if (this.garbageCollect) { | ||
this.collectGarbage(this._batch); | ||
const removed = this.collectGarbage(this._batch); | ||
if (removed.length > 0) { | ||
this._atomArchived.next(removed); | ||
} | ||
} | ||
@@ -169,8 +179,6 @@ [this._value, this._metadata] = this._calculateValue(this._batch); | ||
const newAtoms = this.weave.import(refs); | ||
const sortedAtoms = sortBy(newAtoms, a => a.atom.id.timestamp); | ||
const sortedAtoms = sortBy(newAtoms, a => a.id.timestamp); | ||
for (let i = 0; i < sortedAtoms.length; i++) { | ||
const ref = sortedAtoms[i]; | ||
if (ref.atom.id.site !== this.site.id || ref.atom.id.timestamp >= this.time) { | ||
this.factory.updateTime(ref.atom.id.timestamp); | ||
} | ||
const atom = sortedAtoms[i]; | ||
this.factory.updateTime(atom); | ||
} | ||
@@ -181,2 +189,28 @@ [this._value, this._metadata] = this._calculateValue(newAtoms); | ||
/** | ||
* Imports the given tree into this one and returns the list of atoms that were imported. | ||
* @param tree The tree to import. | ||
*/ | ||
import(tree) { | ||
if (tree.weave) { | ||
if (tree.formatVersion === 2) { | ||
return this.importWeave(tree.weave); | ||
} | ||
else if (tree.formatVersion === 3) { | ||
if (tree.ordered) { | ||
return this.importWeave(tree.weave); | ||
} | ||
else { | ||
return this.addMany(tree.weave); | ||
} | ||
} | ||
else if (typeof tree.formatVersion === 'undefined') { | ||
return this.importWeave(tree.weave.map(ref => ref.atom)); | ||
} | ||
else { | ||
console.warn("[CausalTree] Don't know how to import tree version:", tree.formatVersion); | ||
return []; | ||
} | ||
} | ||
} | ||
/** | ||
* Exports this tree into a storable format. | ||
@@ -186,2 +220,3 @@ */ | ||
return { | ||
formatVersion: 2, | ||
site: this._site, | ||
@@ -243,6 +278,21 @@ knownSites: this.knownSites.slice(), | ||
/** | ||
* Forks the given causal tree and returns a new tree that contains the same state. | ||
* Note that this method does not copy over configuration options such as collectGarbage. | ||
* Also note that this method should be overridden in child classes to ensure that the proper type | ||
* is being created. | ||
* @param type The type of the tree that is being forked. | ||
* @param tree The tree to fork. | ||
*/ | ||
fork() { | ||
const stored = this.export(); | ||
return new CausalTree(stored, this._reducer); | ||
} | ||
/** | ||
* Performs garbage collection of the tree's weave after a set of atoms were added to the tree. | ||
* Returns the references that were removed from the tree. | ||
* @param refs The weave references that were added to the tree. | ||
*/ | ||
collectGarbage(refs) { } | ||
collectGarbage(refs) { | ||
return []; | ||
} | ||
/** | ||
@@ -249,0 +299,0 @@ * Recalculates the values associated the given references. |
@@ -1,2 +0,2 @@ | ||
import { AtomOp } from "./Atom"; | ||
import { AtomOp, Atom } from "./Atom"; | ||
import { StoredCausalTree } from "./StoredCausalTree"; | ||
@@ -12,8 +12,17 @@ /** | ||
/** | ||
* Updates the causal tree stored under the given ID with the new data. | ||
* @param id The ID that the weave should be stored under. | ||
* Updates the causal tree stored under the given ID with the new complete state. | ||
* @param id The ID that the tree should be stored under. | ||
* @param tree The tree to store. | ||
* @param fullUpdate Whether to update the entire stored tree. | ||
* If unspecified, then the entire tree is updated. | ||
* If false, then only the tree info (site, and known sites) will be updated. | ||
*/ | ||
update<T extends AtomOp>(id: string, tree: StoredCausalTree<T>): Promise<void>; | ||
put<T extends AtomOp>(id: string, tree: StoredCausalTree<T>, fullUpdate?: boolean): Promise<void>; | ||
/** | ||
* Adds the given atoms to the tree stored under the given ID. | ||
* @param id The ID of the tree that the atoms should be added to. | ||
* @param atoms The atoms to add. | ||
*/ | ||
add<T extends AtomOp>(id: string, atoms: Atom<T>[]): Promise<void>; | ||
/** | ||
* Gets the causal tree that is stored under the given ID. | ||
@@ -20,0 +29,0 @@ * @param id The ID that the tree is stored under. |
import { RealtimeChannel } from "./RealtimeChannel"; | ||
import { WeaveReference } from "./Weave"; | ||
import { AtomOp } from "./Atom"; | ||
import { AtomOp, Atom } from "./Atom"; | ||
import { CausalTree } from "./CausalTree"; | ||
@@ -25,3 +24,3 @@ import { CausalTreeStore } from "./CausalTreeStore"; | ||
*/ | ||
readonly channel: RealtimeChannel<WeaveReference<AtomOp>[]>; | ||
readonly channel: RealtimeChannel<Atom<AtomOp>[]>; | ||
/** | ||
@@ -42,3 +41,3 @@ * Gets the tree that this class is currently wrapping. | ||
*/ | ||
readonly onUpdated: Observable<WeaveReference<AtomOp>[]>; | ||
readonly onUpdated: Observable<Atom<AtomOp>[]>; | ||
/** | ||
@@ -55,3 +54,3 @@ * Gets an observable that resolves whenever an error happens in this tree. | ||
*/ | ||
constructor(factory: CausalTreeFactory, store: CausalTreeStore, channel: RealtimeChannel<WeaveReference<AtomOp>[]>); | ||
constructor(factory: CausalTreeFactory, store: CausalTreeStore, channel: RealtimeChannel<Atom<AtomOp>[]>); | ||
/** | ||
@@ -58,0 +57,0 @@ * Initializes the realtime causal tree. |
@@ -36,3 +36,5 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
this._subs = []; | ||
this._subs.push(this._updated.pipe(flatMap((u) => __awaiter(this, void 0, void 0, function* () { return yield this._store.update(this.id, this.tree.export()); }))).subscribe(null, err => this._errors.next(err))); | ||
// TODO: Get the causal tree to store the state | ||
// without tanking performance. | ||
this._subs.push(this._updated.pipe(flatMap((atoms) => __awaiter(this, void 0, void 0, function* () { return yield this._store.add(this.id, atoms); }))).subscribe(null, err => this._errors.next(err))); | ||
} | ||
@@ -83,6 +85,16 @@ /** | ||
this._setTree(this._factory.create(this.type, stored)); | ||
this._updated.next(stored.weave); | ||
if (stored.weave) { | ||
if (stored.formatVersion === 2) { | ||
this._updated.next(stored.weave); | ||
} | ||
else if (stored.formatVersion === 3) { | ||
this._updated.next(stored.weave); | ||
} | ||
else if (typeof stored.formatVersion === 'undefined') { | ||
this._updated.next(stored.weave.map(a => a.atom)); | ||
} | ||
} | ||
} | ||
this._subs.push(this._channel.connectionStateChanged.pipe(filter(connected => connected), takeWhile(connected => this.tree === null), flatMap(c => this._channel.exchangeInfo(this.getVersion())), flatMap(version => this._requestSiteId(version), (version, site) => ({ version, site })), map(data => this._factory.create(this.type, storedTree(data.site, data.version.knownSites))), flatMap(tree => this._channel.exchangeWeaves([], tree.weave.getVersion()), (tree, weave) => ({ tree, weave })), map(data => (Object.assign({}, data, { weave: data.tree.importWeave(data.weave) }))), tap(data => this._setTree(data.tree)), tap(data => this._updated.next(data.weave))).subscribe(null, err => this._errors.next(err))); | ||
this._subs.push(this._channel.connectionStateChanged.pipe(filter(connected => connected), skipWhile(connected => this.tree === null), map(c => this.getVersion()), flatMap(localVersion => this._channel.exchangeInfo(localVersion), (local, remote) => ({ local, remote })), filter(versions => !versionsEqual(versions.local.version, versions.remote.version)), flatMap(versions => this._channel.exchangeWeaves(this._tree.weave.atoms, versions.local.version), (versions, weave) => ({ versions, weave })), map(data => (Object.assign({}, data, { weave: this._tree.importWeave(data.weave) }))), tap(data => this._importKnownSites(data.versions.remote)), tap(data => this._updated.next(data.weave))).subscribe(null, err => this._errors.next(err))); | ||
this._subs.push(this._channel.connectionStateChanged.pipe(filter(connected => connected), takeWhile(connected => this.tree === null), flatMap(c => this._channel.exchangeInfo(this.getVersion())), flatMap(version => this._requestSiteId(version), (version, site) => ({ version, site })), map(data => this._factory.create(this.type, storedTree(data.site, data.version.knownSites))), flatMap(tree => this._channel.exchangeWeaves(tree.export()), (tree, imported) => ({ tree, imported })), map(data => (Object.assign({}, data, { added: data.tree.import(data.imported) }))), flatMap(data => this._store.put(this.id, data.tree.export(), true), (data) => data), tap(data => this._setTree(data.tree)), tap(data => this._updated.next(data.added))).subscribe(null, err => this._errors.next(err))); | ||
this._subs.push(this._channel.connectionStateChanged.pipe(filter(connected => connected), skipWhile(connected => this.tree === null), map(c => this.getVersion()), flatMap(localVersion => this._channel.exchangeInfo(localVersion), (local, remote) => ({ local, remote })), filter(versions => !versionsEqual(versions.local.version, versions.remote.version)), flatMap(versions => this._channel.exchangeWeaves(this.tree.export()), (versions, weave) => ({ versions, weave })), map(data => (Object.assign({}, data, { weave: this._tree.import(data.weave) }))), tap(data => this._importKnownSites(data.versions.remote)), flatMap(data => this._store.put(this.id, this._tree.export(), true), (data) => data), tap(data => this._updated.next(data.weave))).subscribe(null, err => this._errors.next(err))); | ||
this._subs.push(this._channel.events.pipe(filter(e => this.tree !== null), map(e => this.tree.addMany(e)), tap(refs => this._updated.next(refs))).subscribe(null, err => this._errors.next(err))); | ||
@@ -118,3 +130,3 @@ }); | ||
this._tree = tree; | ||
this._subs.push(this._tree.atomAdded.pipe(map(refs => refs.filter(ref => ref.atom.id.site === this._tree.site.id)), filter(refs => refs.length > 0), tap(refs => this._channel.emit(refs)), tap(ref => this._updated.next(ref))).subscribe()); | ||
this._subs.push(this._tree.atomAdded.pipe(map(refs => refs.filter(ref => ref.id.site === this._tree.site.id)), filter(refs => refs.length > 0), tap(refs => this._channel.emit(refs)), tap(ref => this._updated.next(ref))).subscribe(null, error => this._errors.next(error))); | ||
} | ||
@@ -121,0 +133,0 @@ _importKnownSites(version) { |
import { RealtimeChannelInfo } from "./RealtimeChannelInfo"; | ||
import { Observable, SubscriptionLike } from "rxjs"; | ||
import { Observable, SubscriptionLike, BehaviorSubject } from "rxjs"; | ||
import { RealtimeChannelConnection } from "./RealtimeChannelConnection"; | ||
import { SiteVersionInfo } from "./SiteVersionInfo"; | ||
import { SiteInfo } from "./SiteIdInfo"; | ||
import { WeaveVersion } from "./WeaveVersion"; | ||
import { WeaveReference } from "./Weave"; | ||
import { AtomOp } from "./Atom"; | ||
import { StoredCausalTree } from "./StoredCausalTree"; | ||
/** | ||
@@ -19,2 +18,3 @@ * Defines a class for a realtime event channel. | ||
private _connection; | ||
private _connectionStateChanged; | ||
private _emitName; | ||
@@ -68,3 +68,3 @@ private _infoName; | ||
*/ | ||
exchangeWeaves<T extends AtomOp>(weave: WeaveReference<T>[], currentVersion: WeaveVersion | null): Promise<ExchangeWeavesResponse<T>>; | ||
exchangeWeaves<T extends AtomOp>(message: StoredCausalTree<T>): Promise<StoredCausalTree<T>>; | ||
/** | ||
@@ -87,3 +87,3 @@ * Emits the given event to the remote peer. | ||
*/ | ||
readonly connectionStateChanged: Observable<boolean>; | ||
readonly connectionStateChanged: BehaviorSubject<boolean>; | ||
/** | ||
@@ -94,19 +94,1 @@ * Disposes of this channel. | ||
} | ||
/** | ||
* Defines an interface for a request to exchange weaves with a remote peer. | ||
*/ | ||
export interface ExchangeWeavesRequest<T extends AtomOp> { | ||
/** | ||
* The weave from the requester. | ||
*/ | ||
weave: WeaveReference<T>[]; | ||
/** | ||
* The current version that the requester is on. | ||
* If null then the requester does not have a version. | ||
*/ | ||
currentVersion: WeaveVersion | null; | ||
} | ||
/** | ||
* Defines the type of the response for exchanging weaves with a remote peer. | ||
*/ | ||
export declare type ExchangeWeavesResponse<T extends AtomOp> = WeaveReference<T>[]; |
@@ -1,2 +0,3 @@ | ||
import { filter, map, flatMap } from "rxjs/operators"; | ||
import { BehaviorSubject } from "rxjs"; | ||
import { filter, map, tap, flatMap } from "rxjs/operators"; | ||
/** | ||
@@ -23,2 +24,3 @@ * Defines a class for a realtime event channel. | ||
this._requestWeaveName = `weave_${info.id}`; | ||
this._connectionStateChanged = new BehaviorSubject(false); | ||
this._connection.init([ | ||
@@ -30,3 +32,4 @@ this._emitName, | ||
this.events = this._connection.events.pipe(filter(e => e.name === this._emitName), map(e => e.data)); | ||
this.connectionStateChanged.pipe(filter(connected => connected), flatMap(connected => this._connection.request('join_channel', this.info))).subscribe(); | ||
this._connection.connectionStateChanged.pipe(filter(connected => !connected), tap(_ => this._connectionStateChanged.next(false))).subscribe(); | ||
this._connection.connectionStateChanged.pipe(filter(connected => connected), flatMap(connected => this._connection.request('join_channel', this.info)), tap(_ => this._connectionStateChanged.next(true))).subscribe(); | ||
} | ||
@@ -69,8 +72,4 @@ /** | ||
*/ | ||
exchangeWeaves(weave, currentVersion) { | ||
const request = { | ||
weave: weave, | ||
currentVersion: currentVersion | ||
}; | ||
return this._connection.request(this._requestWeaveName, request); | ||
exchangeWeaves(message) { | ||
return this._connection.request(this._requestWeaveName, message); | ||
} | ||
@@ -102,3 +101,3 @@ /** | ||
get connectionStateChanged() { | ||
return this._connection.connectionStateChanged; | ||
return this._connectionStateChanged; | ||
} | ||
@@ -105,0 +104,0 @@ /** |
@@ -17,2 +17,8 @@ /** | ||
id: string; | ||
/** | ||
* If set to true, specifies that the server should not perform any special | ||
* logic to initialize the channel. This can be useful for scenarios where we already | ||
* have data we want to import into the channel and don't want the server to make any data automatically. | ||
*/ | ||
bare?: boolean; | ||
} |
@@ -1,13 +0,47 @@ | ||
import { AtomOp } from "./Atom"; | ||
import { AtomOp, Atom } from "./Atom"; | ||
import { SiteInfo } from "./SiteIdInfo"; | ||
import { WeaveReference } from "./Weave"; | ||
export declare const currentFormatVersion = 3; | ||
/** | ||
* Defines an interface for a causal tree that is in a storable format. | ||
*/ | ||
export interface StoredCausalTree<T extends AtomOp> { | ||
export declare type StoredCausalTree<T extends AtomOp> = StoredCausalTreeVersion1<T> | StoredCausalTreeVersion2<T> | StoredCausalTreeVersion3<T>; | ||
export interface StoredCausalTreeVersion3<T extends AtomOp> { | ||
formatVersion: 3; | ||
site: SiteInfo; | ||
knownSites: SiteInfo[]; | ||
weave: Atom<T>[]; | ||
/** | ||
* Whether the weave is ordered in the proper format. | ||
* If false, then the atoms aren't in any particular order and | ||
* cannot be imported but must instead be inserted. | ||
*/ | ||
ordered: boolean; | ||
} | ||
/** | ||
* Defines an interface for a causal tree that is in a storable format. | ||
* This interface represents the second version of the storable format. | ||
*/ | ||
export interface StoredCausalTreeVersion2<T extends AtomOp> { | ||
formatVersion: 2; | ||
site: SiteInfo; | ||
knownSites: SiteInfo[]; | ||
weave: Atom<T>[]; | ||
} | ||
/** | ||
* Defines an interface for a causal tree that is in a storable format. | ||
* This interface represents the first version of the storable format. | ||
*/ | ||
export interface StoredCausalTreeVersion1<T extends AtomOp> { | ||
formatVersion?: 1; | ||
site: SiteInfo; | ||
knownSites: SiteInfo[]; | ||
weave: WeaveReference<T>[]; | ||
} | ||
/** | ||
* @deprecated Use Atom. | ||
*/ | ||
export interface WeaveReference<T extends AtomOp> { | ||
atom: Atom<T>; | ||
} | ||
/** | ||
* Creates a stored causal tree with the given data. | ||
@@ -18,2 +52,7 @@ * @param site | ||
*/ | ||
export declare function storedTree<T extends AtomOp>(site: SiteInfo, knownSites?: SiteInfo[], weave?: WeaveReference<T>[]): StoredCausalTree<T>; | ||
export declare function storedTree<T extends AtomOp>(site: SiteInfo, knownSites?: SiteInfo[], weave?: Atom<T>[]): StoredCausalTreeVersion3<T>; | ||
/** | ||
* Upgrades the given stored causal tree to the latest version. | ||
* @param stored The stored tree. | ||
*/ | ||
export declare function upgrade<T extends AtomOp>(stored: StoredCausalTree<T>): StoredCausalTreeVersion3<T>; |
@@ -0,1 +1,2 @@ | ||
export const currentFormatVersion = 3; | ||
/** | ||
@@ -9,7 +10,42 @@ * Creates a stored causal tree with the given data. | ||
return { | ||
formatVersion: currentFormatVersion, | ||
site: site, | ||
knownSites: knownSites, | ||
weave: weave | ||
weave: weave, | ||
ordered: true | ||
}; | ||
} | ||
/** | ||
* Upgrades the given stored causal tree to the latest version. | ||
* @param stored The stored tree. | ||
*/ | ||
export function upgrade(stored) { | ||
if (!stored) { | ||
return null; | ||
} | ||
if (stored.formatVersion === 3) { | ||
return stored; | ||
} | ||
else if (stored.formatVersion === 2) { | ||
return { | ||
formatVersion: 3, | ||
knownSites: stored.knownSites, | ||
site: stored.site, | ||
weave: stored.weave, | ||
ordered: true | ||
}; | ||
} | ||
else if (typeof stored.formatVersion === 'undefined') { | ||
return { | ||
formatVersion: 3, | ||
knownSites: stored.knownSites, | ||
site: stored.site, | ||
weave: stored.weave ? stored.weave.map(a => a.atom) : null, | ||
ordered: true | ||
}; | ||
} | ||
else { | ||
throw new Error(`[StoredCausalTree] Unable to update the given tree version ${stored.formatVersion}`); | ||
} | ||
} | ||
//# sourceMappingURL=StoredCausalTree.js.map |
import { CausalTreeStore } from "../CausalTreeStore"; | ||
import { AtomOp } from "../Atom"; | ||
import { AtomOp, Atom } from "../Atom"; | ||
import { StoredCausalTree } from "../StoredCausalTree"; | ||
export declare class TestCausalTreeStore implements CausalTreeStore { | ||
private _store; | ||
private _atoms; | ||
init(): Promise<void>; | ||
update<T extends AtomOp>(id: string, tree: StoredCausalTree<T>): Promise<void>; | ||
put<T extends AtomOp>(id: string, tree: StoredCausalTree<T>): Promise<void>; | ||
add<T extends AtomOp>(id: string, atoms: Atom<T>[]): Promise<void>; | ||
get<T extends AtomOp>(id: string): Promise<StoredCausalTree<T>>; | ||
} |
@@ -0,4 +1,6 @@ | ||
import { upgrade } from "../StoredCausalTree"; | ||
export class TestCausalTreeStore { | ||
constructor() { | ||
this._store = {}; | ||
this._atoms = {}; | ||
} | ||
@@ -8,3 +10,3 @@ init() { | ||
} | ||
update(id, tree) { | ||
put(id, tree) { | ||
return new Promise((resolve, reject) => { | ||
@@ -15,5 +17,34 @@ this._store[id] = tree; | ||
} | ||
add(id, atoms) { | ||
return new Promise((resolve, reject) => { | ||
let list = this._atoms[id]; | ||
if (!list) { | ||
list = []; | ||
this._atoms[id] = list; | ||
} | ||
list.push(...atoms); | ||
}); | ||
} | ||
get(id) { | ||
return new Promise((resolve, reject) => { | ||
resolve(this._store[id]); | ||
const stored = this._store[id]; | ||
const list = this._atoms[id]; | ||
const upgraded = upgrade(stored); | ||
let atoms = null; | ||
if (stored) { | ||
if (stored.weave) { | ||
atoms = list ? [...(upgraded.weave), ...list] : upgraded.weave; | ||
} | ||
const ordered = !list; | ||
resolve({ | ||
formatVersion: 3, | ||
knownSites: stored.knownSites, | ||
site: stored.site, | ||
ordered: ordered && upgraded.ordered, | ||
weave: atoms | ||
}); | ||
} | ||
else { | ||
resolve(undefined); | ||
} | ||
}); | ||
@@ -20,0 +51,0 @@ } |
import { Atom, AtomId, AtomOp } from "./Atom"; | ||
import { WeaveVersion } from "./WeaveVersion"; | ||
import { WeaveVersion, WeaveSiteVersion } from "./WeaveVersion"; | ||
/** | ||
* Creates a weave reference. | ||
* @param atom | ||
* @param index | ||
* @param causeIndex | ||
*/ | ||
export declare function reference<T extends AtomOp>(atom: Atom<T>): WeaveReference<T>; | ||
/** | ||
* Defines a reference to an atom inside a weave. | ||
* Once created, this reference will always be valid. | ||
*/ | ||
export interface WeaveReference<TOp extends AtomOp> { | ||
/** | ||
* The atom that this reference refers to. | ||
*/ | ||
atom: Atom<TOp>; | ||
} | ||
/** | ||
* Defines a weave. | ||
@@ -27,3 +10,2 @@ * That is, the depth-first preorder traversal of a causal tree. | ||
private _sites; | ||
private _version; | ||
/** | ||
@@ -40,3 +22,3 @@ * A map of atom IDs to the total number of atoms that they contain. | ||
*/ | ||
readonly atoms: WeaveReference<TOp>[]; | ||
readonly atoms: Atom<TOp>[]; | ||
/** | ||
@@ -50,3 +32,3 @@ * Creates a new weave. | ||
*/ | ||
getSite(siteId: number): WeaveReference<TOp>[]; | ||
getSite(siteId: number): Atom<TOp>[]; | ||
/** | ||
@@ -56,3 +38,3 @@ * Inserts the given atom into the weave. | ||
*/ | ||
insert<T extends TOp>(atom: Atom<T>): WeaveReference<T>; | ||
insert<T extends TOp>(atom: Atom<T>): Atom<T>; | ||
/** | ||
@@ -65,10 +47,12 @@ * Inserts the given list of atoms into the weave. | ||
* Removes the given reference from the weave. | ||
* Returns the references that were removed. | ||
* @param ref The reference to remove. | ||
*/ | ||
remove(ref: WeaveReference<TOp>): boolean; | ||
remove(ref: Atom<TOp>): Atom<TOp>[]; | ||
/** | ||
* Removes all of the siblings of the given atom that happened before it. | ||
* @param ref The reference whose older siblings should be removed. | ||
* Returns the references that were removed. | ||
* @param atom The reference whose older siblings should be removed. | ||
*/ | ||
removeBefore(ref: WeaveReference<TOp>): boolean; | ||
removeBefore(atom: Atom<TOp>): Atom<TOp>[]; | ||
private _removeSpan; | ||
@@ -79,5 +63,6 @@ /** | ||
*/ | ||
getAtom<T extends TOp>(id: AtomId): WeaveReference<T>; | ||
getAtom<T extends TOp>(id: AtomId): Atom<T>; | ||
/** | ||
* Gets the total number of children that the given atom contains. | ||
* Gets the size of the atom with the given ID. | ||
* The size of an atom is defined as the number of children it has plus 1. | ||
* @param id The ID of the atom to find the size of. If the tree doesn't contain the given reference then undefined is returned. | ||
@@ -95,2 +80,11 @@ */ | ||
/** | ||
* Gets a new weave that contains only the atoms needed to keep the given version consistent. | ||
* @param version The version of the weave to get. | ||
*/ | ||
getWeft(version: WeaveSiteVersion, preserveChildren?: boolean): Weave<TOp>; | ||
/** | ||
* Copies this weave and returns the clone. | ||
*/ | ||
copy(): Weave<TOp>; | ||
/** | ||
* Imports the given list of atoms into this weave. | ||
@@ -101,3 +95,3 @@ * The atoms are assumed to be pre-sorted. | ||
*/ | ||
import(atoms: WeaveReference<TOp>[]): WeaveReference<TOp>[]; | ||
import(atoms: Atom<TOp>[]): Atom<TOp>[]; | ||
/** | ||
@@ -113,6 +107,12 @@ * Gets the list of site IDs that this weave contains. | ||
*/ | ||
referenceChain(ref: WeaveReference<TOp>): WeaveReference<TOp>[]; | ||
referenceChain(ref: Atom<TOp>): Atom<TOp>[]; | ||
/** | ||
* Trims the site map so that it only contains spaces for atoms that are currently in this weave. | ||
* As a result, getVersion() will no longer show the latest timestamp from each site but only | ||
* the latest timestamp that is currently in the site. | ||
*/ | ||
private _trimSites; | ||
/** | ||
* Updates the sizes of the given references in the map. | ||
* @param refs The references to update. | ||
* @param atoms The references to update. | ||
*/ | ||
@@ -166,3 +166,3 @@ private _updateAtomSizes; | ||
*/ | ||
static buildFromArray<TOp extends AtomOp>(refs: WeaveReference<TOp>[]): Weave<TOp>; | ||
static buildFromArray<TOp extends AtomOp>(refs: Atom<TOp>[]): Weave<TOp>; | ||
} | ||
@@ -175,3 +175,3 @@ /** | ||
export interface SiteMap<TOp extends AtomOp> { | ||
[site: number]: WeaveReference<TOp>[]; | ||
[site: number]: Atom<TOp>[]; | ||
} | ||
@@ -185,2 +185,2 @@ /** | ||
*/ | ||
export declare function weaveIndexOf<TOp extends AtomOp>(arr: WeaveReference<TOp>[], id: AtomId, start?: number): number; | ||
export declare function weaveIndexOf<TOp extends AtomOp>(arr: Atom<TOp>[], id: AtomId, start?: number): number; |
@@ -5,13 +5,2 @@ import { idEquals } from "./Atom"; | ||
/** | ||
* Creates a weave reference. | ||
* @param atom | ||
* @param index | ||
* @param causeIndex | ||
*/ | ||
export function reference(atom) { | ||
return { | ||
atom | ||
}; | ||
} | ||
/** | ||
* Defines a weave. | ||
@@ -57,10 +46,9 @@ * That is, the depth-first preorder traversal of a causal tree. | ||
if (this.atoms.length > 0) { | ||
return this.atoms[0]; | ||
throw new Error('Cannot add second root atom.'); | ||
} | ||
const ref = reference(atom); | ||
// Add the atom at the root of the weave. | ||
this._atoms.splice(0, 0, ref); | ||
site[ref.atom.id.timestamp] = ref; | ||
this._atoms.splice(0, 0, atom); | ||
site[atom.id.timestamp] = atom; | ||
this._sizeMap.set(atom.id, 1); | ||
return ref; | ||
return atom; | ||
} | ||
@@ -77,11 +65,10 @@ else { | ||
const existingAtom = site[siteIndex]; | ||
if (existingAtom && idEquals(existingAtom.atom.id, atom.id)) { | ||
if (existingAtom && idEquals(existingAtom.id, atom.id)) { | ||
return existingAtom; | ||
} | ||
} | ||
const ref = reference(atom); | ||
this._atoms.splice(weaveIndex, 0, ref); | ||
site[siteIndex] = ref; | ||
this._updateAtomSizes([ref]); | ||
return ref; | ||
this._atoms.splice(weaveIndex, 0, atom); | ||
site[siteIndex] = atom; | ||
this._updateAtomSizes([atom]); | ||
return atom; | ||
} | ||
@@ -100,2 +87,3 @@ } | ||
* Removes the given reference from the weave. | ||
* Returns the references that were removed. | ||
* @param ref The reference to remove. | ||
@@ -105,33 +93,33 @@ */ | ||
if (!ref) { | ||
return false; | ||
return []; | ||
} | ||
const span = this._getSpan(ref); | ||
if (!span) { | ||
return false; | ||
return []; | ||
} | ||
this._removeSpan(span.index, span.length); | ||
return true; | ||
return this._removeSpan(span.index, span.length); | ||
} | ||
/** | ||
* Removes all of the siblings of the given atom that happened before it. | ||
* @param ref The reference whose older siblings should be removed. | ||
* Returns the references that were removed. | ||
* @param atom The reference whose older siblings should be removed. | ||
*/ | ||
removeBefore(ref) { | ||
if (!ref) { | ||
return false; | ||
removeBefore(atom) { | ||
if (!atom) { | ||
return []; | ||
} | ||
if (!ref.atom.cause) { | ||
return false; | ||
if (!atom.cause) { | ||
return []; | ||
} | ||
const cause = this.getAtom(ref.atom.cause); | ||
const cause = this.getAtom(atom.cause); | ||
if (!cause) { | ||
return false; | ||
return []; | ||
} | ||
const causeSpan = this._getSpan(cause); | ||
if (!causeSpan) { | ||
return false; | ||
return []; | ||
} | ||
const refSpan = this._getSpan(ref, causeSpan.index); | ||
const refSpan = this._getSpan(atom, causeSpan.index); | ||
if (!refSpan) { | ||
return false; | ||
return []; | ||
} | ||
@@ -141,4 +129,3 @@ const startSplice = refSpan.index + refSpan.length; | ||
const spliceLength = (endSplice - startSplice); | ||
this._removeSpan(startSplice, spliceLength); | ||
return true; | ||
return this._removeSpan(startSplice, spliceLength); | ||
} | ||
@@ -151,10 +138,11 @@ _removeSpan(index, length) { | ||
for (let i = 1; i < chain.length; i++) { | ||
const id = chain[i].atom.id; | ||
const id = chain[i].id; | ||
const current = this.getAtomSize(id); | ||
this._sizeMap.set(id, current - 1); | ||
} | ||
this._sizeMap.delete(r.atom.id); | ||
const site = this.getSite(r.atom.id.site); | ||
delete site[r.atom.id.timestamp]; | ||
this._sizeMap.delete(r.id); | ||
const site = this.getSite(r.id.site); | ||
delete site[r.id.timestamp]; | ||
} | ||
return removed; | ||
} | ||
@@ -178,3 +166,4 @@ /** | ||
/** | ||
* Gets the total number of children that the given atom contains. | ||
* Gets the size of the atom with the given ID. | ||
* The size of an atom is defined as the number of children it has plus 1. | ||
* @param id The ID of the atom to find the size of. If the tree doesn't contain the given reference then undefined is returned. | ||
@@ -207,2 +196,52 @@ */ | ||
/** | ||
* Gets a new weave that contains only the atoms needed to keep the given version consistent. | ||
* @param version The version of the weave to get. | ||
*/ | ||
getWeft(version, preserveChildren = false) { | ||
let newWeave = this.copy(); | ||
if (preserveChildren) { | ||
// travel from leaf nodes to the root node | ||
for (let i = newWeave.atoms.length - 1; i >= 0; i--) { | ||
const atom = newWeave.atoms[i]; | ||
const id = atom.id; | ||
const site = id.site; | ||
const oldestAllowed = version[site]; | ||
if (!oldestAllowed || id.timestamp > oldestAllowed) { | ||
// When preserving children, | ||
// we only remove an atom if it has no children. | ||
if (newWeave.getAtomSize(id) === 1) { | ||
newWeave.remove(atom); | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
for (let i = 0; i < newWeave.atoms.length; i++) { | ||
const atom = newWeave.atoms[i]; | ||
const id = atom.id; | ||
const site = id.site; | ||
const oldestAllowed = version[site]; | ||
if (!oldestAllowed || id.timestamp > oldestAllowed) { | ||
newWeave.remove(atom); | ||
i -= 1; | ||
} | ||
} | ||
} | ||
newWeave._trimSites(); | ||
return newWeave; | ||
} | ||
/** | ||
* Copies this weave and returns the clone. | ||
*/ | ||
copy() { | ||
let newWeave = new Weave(); | ||
newWeave._atoms = this._atoms.slice(); | ||
newWeave._sizeMap = new Map(this._sizeMap); | ||
newWeave._sites = {}; | ||
for (let key in this._sites) { | ||
newWeave._sites[key] = this._sites[key].slice(); | ||
} | ||
return newWeave; | ||
} | ||
/** | ||
* Imports the given list of atoms into this weave. | ||
@@ -225,5 +264,5 @@ * The atoms are assumed to be pre-sorted. | ||
for (let b = 0; b < finalAtoms.length; b++) { | ||
const ref = finalAtoms[b]; | ||
if (ref.atom.cause) { | ||
const cause = this.getAtom(ref.atom.cause); | ||
const atom = finalAtoms[b]; | ||
if (atom.cause) { | ||
const cause = this.getAtom(atom.cause); | ||
if (!cause) { | ||
@@ -237,6 +276,6 @@ // prevent atoms without parents | ||
} | ||
this._atoms.push(ref); | ||
newAtoms.push(ref); | ||
const site = this.getSite(ref.atom.id.site); | ||
site[ref.atom.id.timestamp] = ref; | ||
this._atoms.push(atom); | ||
newAtoms.push(atom); | ||
const site = this.getSite(atom.id.site); | ||
site[atom.id.timestamp] = atom; | ||
} | ||
@@ -247,4 +286,4 @@ break; | ||
// Could either be the same, a new sibling, or a new child of the current subtree | ||
if (a.atom.cause) { | ||
const cause = this.getAtom(a.atom.cause); | ||
if (a.cause) { | ||
const cause = this.getAtom(a.cause); | ||
if (!cause) { | ||
@@ -258,3 +297,3 @@ // prevent atoms without parents | ||
} | ||
let order = this._compareAtoms(a.atom, local.atom); | ||
let order = this._compareAtoms(a, local); | ||
if (isNaN(order)) { | ||
@@ -271,4 +310,4 @@ break; | ||
newAtoms.push(a); | ||
const site = this.getSite(a.atom.id.site); | ||
site[a.atom.id.timestamp] = a; | ||
const site = this.getSite(a.id.site); | ||
site[a.id.timestamp] = a; | ||
} | ||
@@ -281,9 +320,9 @@ else if (order > 0) { | ||
local = this._atoms[i + localOffset]; | ||
} while (local && a.atom.id.timestamp <= local.atom.cause.timestamp); | ||
order = this._compareAtoms(a.atom, local.atom); | ||
} while (local && a.id.timestamp <= local.cause.timestamp); | ||
order = this._compareAtoms(a, local); | ||
if (order < 0) { | ||
this._atoms.splice(i + localOffset, 0, a); | ||
newAtoms.push(a); | ||
const site = this.getSite(a.atom.id.site); | ||
site[a.atom.id.timestamp] = a; | ||
const site = this.getSite(a.id.site); | ||
site[a.id.timestamp] = a; | ||
} | ||
@@ -310,7 +349,7 @@ } | ||
let chain = [ref]; | ||
let cause = ref.atom.cause; | ||
let cause = ref.cause; | ||
while (cause) { | ||
const causeRef = this.getAtom(cause); | ||
chain.push(causeRef); | ||
cause = causeRef.atom.cause; | ||
cause = causeRef.cause; | ||
} | ||
@@ -320,11 +359,32 @@ return chain; | ||
/** | ||
* Trims the site map so that it only contains spaces for atoms that are currently in this weave. | ||
* As a result, getVersion() will no longer show the latest timestamp from each site but only | ||
* the latest timestamp that is currently in the site. | ||
*/ | ||
_trimSites() { | ||
for (let siteId in this._sites) { | ||
let site = this._sites[siteId]; | ||
let i = site.length; | ||
while (i > 0) { | ||
if (typeof site[i - 1] !== 'undefined') { | ||
break; | ||
} | ||
i -= 1; | ||
} | ||
site.splice(i); | ||
if (site.length === 0) { | ||
delete this._sites[siteId]; | ||
} | ||
} | ||
} | ||
/** | ||
* Updates the sizes of the given references in the map. | ||
* @param refs The references to update. | ||
* @param atoms The references to update. | ||
*/ | ||
_updateAtomSizes(refs) { | ||
for (let i = 0; i < refs.length; i++) { | ||
const ref = refs[i]; | ||
const chain = this.referenceChain(ref); | ||
_updateAtomSizes(atoms) { | ||
for (let i = 0; i < atoms.length; i++) { | ||
const atom = atoms[i]; | ||
const chain = this.referenceChain(atom); | ||
for (let b = 0; b < chain.length; b++) { | ||
const id = chain[b].atom.id; | ||
const id = chain[b].id; | ||
const current = this.getAtomSize(id) || 0; | ||
@@ -343,7 +403,7 @@ this._sizeMap.set(id, current + 1); | ||
_getSpan(ref, start = 0) { | ||
const index = this._indexOf(ref.atom.id, start); | ||
const index = this._indexOf(ref.id, start); | ||
if (index < 0) { | ||
return null; | ||
} | ||
return { index, length: this.getAtomSize(ref.atom.id) }; | ||
return { index, length: this.getAtomSize(ref.id) }; | ||
} | ||
@@ -359,8 +419,8 @@ /** | ||
for (; index < this._atoms.length; index++) { | ||
const ref = this._atoms[index]; | ||
const order = this._compareAtomIds(atomId, ref.atom.id); | ||
const atom = this._atoms[index]; | ||
const order = this._compareAtomIds(atomId, atom.id); | ||
if (order < 0) { | ||
break; | ||
} | ||
if (!idEquals(ref.atom.cause, cause.atom.id)) { | ||
if (!idEquals(atom.cause, cause.id)) { | ||
break; | ||
@@ -463,4 +523,4 @@ } | ||
for (let i = start; i < arr.length; i++) { | ||
const ref = arr[i]; | ||
if (idEquals(ref.atom.id, id)) { | ||
const atom = arr[i]; | ||
if (idEquals(atom.id, id)) { | ||
return i; | ||
@@ -467,0 +527,0 @@ } |
@@ -1,3 +0,3 @@ | ||
import { AtomOp, AtomId } from "./Atom"; | ||
import { Weave, WeaveReference } from "./Weave"; | ||
import { AtomOp, AtomId, Atom } from "./Atom"; | ||
import { Weave } from "./Weave"; | ||
/** | ||
@@ -15,11 +15,11 @@ * Defines a class that helps with traversing weaves. | ||
*/ | ||
peek(parent?: AtomId): WeaveReference<TOp>; | ||
peek(parent?: AtomId): Atom<TOp>; | ||
/** | ||
* Consumes and returns the next atom in the tree. | ||
*/ | ||
next(): WeaveReference<TOp>; | ||
next(): Atom<TOp>; | ||
/** | ||
* Gets the current atom. | ||
*/ | ||
current(): WeaveReference<TOp>; | ||
current(): Atom<TOp>; | ||
/** | ||
@@ -26,0 +26,0 @@ * Skips atoms until the next atom is a sibling of the given parent. |
@@ -21,3 +21,3 @@ import { idEquals } from "./Atom"; | ||
} | ||
if (idEquals(parent, atom.atom.cause)) { | ||
if (idEquals(parent, atom.cause)) { | ||
return atom; | ||
@@ -48,3 +48,3 @@ } | ||
const current = this.current(); | ||
if (current && idEquals(parent, current.atom.id)) { | ||
if (current && idEquals(parent, current.id)) { | ||
const size = this._weave.getAtomSize(parent); | ||
@@ -55,6 +55,6 @@ this._index += size; | ||
while (this.peek(parent)) { | ||
const ref = this.next(); | ||
const atom = this.next(); | ||
const nextRef = this.peek(); | ||
if (nextRef && idEquals(ref.atom.id, nextRef.atom.cause)) { | ||
this.skip(ref.atom.id); | ||
if (nextRef && idEquals(atom.id, nextRef.cause)) { | ||
this.skip(atom.id); | ||
} | ||
@@ -61,0 +61,0 @@ } |
@@ -17,3 +17,2 @@ export declare type PartialFile = Partial<File>; | ||
_editingFile?: string; | ||
_lastActiveTime?: number; | ||
_lastEditedBy?: string; | ||
@@ -20,0 +19,0 @@ ['aux.scene.color']?: string; |
@@ -6,3 +6,3 @@ import { Object, File, Workspace, UserMode, AuxDomain } from './File'; | ||
import { FilesState } from './FilesChannel'; | ||
import { WeaveReference } from '../causal-trees'; | ||
import { Atom } from '../causal-trees'; | ||
import { AuxOp, AuxFile } from '../aux-format'; | ||
@@ -289,3 +289,3 @@ export declare var ShortId_Length: number; | ||
*/ | ||
export declare function calculateStateDiff(prev: FilesState, current: FilesState, events?: WeaveReference<AuxOp>[]): FilesStateDiff; | ||
export declare function calculateStateDiff(prev: FilesState, current: FilesState, events?: Atom<AuxOp>[]): FilesStateDiff; | ||
/** | ||
@@ -292,0 +292,0 @@ * Creates a new object that contains the tags that the given object has |
@@ -8,3 +8,3 @@ import { DEFAULT_WORKSPACE_SCALE, DEFAULT_WORKSPACE_HEIGHT, DEFAULT_WORKSPACE_GRID_SCALE, DEFAULT_USER_MODE, DEFAULT_WORKSPACE_COLOR } from './File'; | ||
import formulaLib from '../Formulas/formula-lib'; | ||
import { cleanFile } from './FilesChannel'; | ||
import { cleanFile, hasValue } from './FilesChannel'; | ||
import { merge } from '../utils'; | ||
@@ -108,3 +108,3 @@ export var ShortId_Length = 5; | ||
const o = object; | ||
return _calculateValue(context, object, tag, o[tag]); | ||
return o[tag]; | ||
} | ||
@@ -197,3 +197,3 @@ else { | ||
/_lastEditedBy/, | ||
/_lastActiveTime/, | ||
/\._lastActiveTime/, | ||
/^aux\._context_/, | ||
@@ -851,15 +851,15 @@ ]; | ||
return false; | ||
if (file.tags._user) { | ||
const result = calculateFileValue(context, file, '_userContext'); | ||
return result == contextId; | ||
let result; | ||
const contextValue = calculateFileValue(context, file, contextId); | ||
if (typeof contextValue === 'string') { | ||
result = (contextValue === 'true'); | ||
} | ||
else { | ||
const result = calculateFileValue(context, file, contextId); | ||
if (typeof result === 'string') { | ||
return result === 'true'; | ||
} | ||
else { | ||
return result === true; | ||
} | ||
result = (contextValue === true); | ||
} | ||
if (!result && hasValue(file.tags._user)) { | ||
const userContextValue = calculateFileValue(context, file, '_userContext'); | ||
result = (userContextValue == contextId); | ||
} | ||
return result; | ||
} | ||
@@ -964,4 +964,16 @@ function _parseFilterValue(value) { | ||
// Unwrap the proxy object | ||
if (result.success && result.result && result.result[isProxy]) { | ||
return Object.assign({}, result, { result: result.result[proxyObject] }); | ||
if (result.success && result.result) { | ||
if (result.result[isProxy]) { | ||
return Object.assign({}, result, { result: result.result[proxyObject] }); | ||
} | ||
else if (Array.isArray(result.result)) { | ||
return Object.assign({}, result, { result: result.result.map(v => { | ||
if (v && v[isProxy]) { | ||
return v[proxyObject]; | ||
} | ||
else { | ||
return v; | ||
} | ||
}) }); | ||
} | ||
} | ||
@@ -1028,3 +1040,3 @@ return result; | ||
else { | ||
return objs.filter(o => this._calculateValue(o, tag) === filter); | ||
return objs.filter(o => this._calculateValue(o, tag) == filter); | ||
} | ||
@@ -1031,0 +1043,0 @@ } |
@@ -15,2 +15,3 @@ import { Node } from 'acorn'; | ||
parseIdent(): Node; | ||
parseExprSubscripts(): Node; | ||
unexpected(): void; | ||
@@ -35,3 +36,3 @@ startNodeAt(start: number, startLoc: number): Node; | ||
/** | ||
* Defines a class that is able to compile code from File Simulator's custom JavaScript dialect | ||
* Defines a class that is able to compile code from AUX's custom JavaScript dialect | ||
* into pure ES6 JavaScript. Does not preserve spacing or comments. | ||
@@ -38,0 +39,0 @@ * |
@@ -61,3 +61,3 @@ import { Parser, TokenType, tokTypes } from 'acorn'; | ||
else if (this.type === tokTypes.name) { | ||
node.identifier = this.parseIdent(); | ||
node.identifier = this.parseExprSubscripts(); | ||
} | ||
@@ -84,3 +84,3 @@ else if (this.type === tokTypes.parenL) { | ||
else if (this.type === tokTypes.name) { | ||
node.identifier = this.parseIdent(); | ||
node.identifier = this.parseExprSubscripts(); | ||
} | ||
@@ -98,3 +98,3 @@ else if (this.type === tokTypes.parenL) { | ||
/** | ||
* Defines a class that is able to compile code from File Simulator's custom JavaScript dialect | ||
* Defines a class that is able to compile code from AUX's custom JavaScript dialect | ||
* into pure ES6 JavaScript. Does not preserve spacing or comments. | ||
@@ -128,37 +128,41 @@ * | ||
enter: ((n) => { | ||
// #tag syntax | ||
if (n.type === 'TagValue' && n.identifier) { | ||
// _listTagValues('tag') | ||
return callExpr('_listTagValues', [{ | ||
// #tag or #tag(filter) syntax | ||
// or @tag or @tag(filter) syntax | ||
if ((n.type === 'TagValue' || n.type === 'ObjectValue') && n.identifier) { | ||
// _listTagValues('tag', filter) | ||
let identifier; | ||
let args = []; | ||
if (n.identifier.type === 'CallExpression') { | ||
identifier = n.identifier.callee; | ||
args = n.identifier.arguments; | ||
} | ||
else { | ||
identifier = n.identifier; | ||
} | ||
let tag; | ||
if (identifier.type === 'MemberExpression') { | ||
tag = this._toJs(identifier); | ||
} | ||
else { | ||
tag = (identifier.name || identifier.value); | ||
} | ||
const funcName = n.type === 'TagValue' ? '_listTagValues' : '_listObjectsWithTag'; | ||
return callExpr(funcName, [{ | ||
type: 'Literal', | ||
value: (n.identifier.name || n.identifier.value) | ||
}]); | ||
// #tag(filter) syntax | ||
value: tag | ||
}, ...args]); | ||
} | ||
else if (n.type === 'CallExpression' && | ||
n.callee.type === 'TagValue' && | ||
n.callee.identifier) { | ||
return callExpr('_listTagValues', [{ | ||
type: 'Literal', | ||
value: (n.callee.identifier.name || n.callee.identifier.value) | ||
}, ...n.arguments]); | ||
// @tag syntax | ||
else if (n.type === 'CallExpression') { | ||
if (n.callee.type === 'TagValue' || n.callee.type === 'ObjectValue') { | ||
if (n.callee.identifier) { | ||
let identifier = n.callee.identifier; | ||
let tag = (identifier.name || identifier.value); | ||
const funcName = n.callee.type === 'TagValue' ? '_listTagValues' : '_listObjectsWithTag'; | ||
return callExpr(funcName, [{ | ||
type: 'Literal', | ||
value: tag | ||
}, ...n.arguments]); | ||
} | ||
} | ||
} | ||
else if (n.type === 'ObjectValue' && n.identifier) { | ||
// _listObjectsWithTag('tag') | ||
return callExpr('_listObjectsWithTag', [{ | ||
type: 'Literal', | ||
value: (n.identifier.name || n.identifier.value) | ||
}]); | ||
// @tag(filter) syntax | ||
} | ||
else if (n.type === 'CallExpression' && | ||
n.callee.type === 'ObjectValue' && | ||
n.callee.identifier) { | ||
// _listObjectsWithTag('tag', filter) | ||
return callExpr('_listObjectsWithTag', [{ | ||
type: 'Literal', | ||
value: (n.callee.identifier.name || n.callee.identifier.value) | ||
}, ...n.arguments]); | ||
} | ||
}) | ||
@@ -165,0 +169,0 @@ }); |
{ | ||
"name": "@yeti-cgi/aux-common", | ||
"version": "0.3.11", | ||
"version": "0.3.12", | ||
"description": "Common library for AUX projects", | ||
@@ -10,2 +10,3 @@ "main": "index.js", | ||
"watch": "npm run clean && tsc --watch", | ||
"watch:player": "npm run watch", | ||
"clean": "gulp clean", | ||
@@ -60,3 +61,3 @@ "build": "npm run clean && tsc", | ||
], | ||
"gitHead": "92470e11745516cdb56affa15d344db09f90293d" | ||
"gitHead": "82ca4757e0680ddd43eb3b4d8f46f548a3dd17c8" | ||
} |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
416688
8022