use rollup for yjs

This commit is contained in:
Kevin Jahns 2017-05-16 18:35:30 +02:00
parent edf47d3491
commit 0a321610aa
17 changed files with 509 additions and 877 deletions

12
.babelrc Normal file
View File

@ -0,0 +1,12 @@
{
"presets": [
["latest", {
"es2015": {
"modules": false
}
}]
],
"plugins": [
"external-helpers"
]
}

View File

@ -1,72 +0,0 @@
/* @flow */
type UserId = string
type Id = [UserId, number|string]
/*
type Struct = {
id: Id,
left?: Id,
right?: Id,
target?: Id,
struct: 'Insert' | 'Delete'
}*/
type Struct = Insertion | Deletion
type Operation = Struct
type Insertion = {
id: Id,
left: ?Id,
origin: ?Id,
right: ?Id,
parent: Id,
parentSub: ?Id,
opContent: ?Id,
content: ?any,
struct: 'Insert'
}
type Deletion = {
target: Id,
struct: 'Delete'
}
type MapStruct = {
id: Id,
type: TypeNames,
map: any
}
type ListStruct = {
id: Id,
type: TypeNames,
start: Id,
end: Id
}
type MessageSyncStep1 = {
type: 'sync step 1',
deleteSet: any,
stateSet: any
}
type MessageSyncStep2 = {
type: 'sync step 2',
os: Array<Operation>,
deleteSet: any,
stateSet: any
}
type MessageUpdate = {
type: 'update',
ops: Array<Operation>
}
type MessageSyncDone = {
type: 'sync done'
}
type Message = MessageSyncStep1 | MessageSyncStep2 | MessageUpdate | MessageSyncDone

View File

View File

@ -1,34 +0,0 @@
/* @flow */
type YGlobal = {
utils: Object,
Struct: any,
AbstractDatabase: any,
AbstractConnector: any,
Transaction: any
}
type YConfig = {
db: Object,
connector: Object,
root: Object
}
type TypeName = 'array' | 'map' | 'text'
declare var YConcurrency_TestingMode : boolean
type Transaction<A> = Generator<any, A, any>
type SyncRole = 'master' | 'slave'
declare class Store {
find: (id:Id) => Transaction<any>;
put: (n:any) => Transaction<void>;
delete: (id:Id) => Transaction<void>;
findWithLowerBound: (start:Id) => Transaction<any>;
findWithUpperBound: (end:Id) => Transaction<any>;
findNext: (id:Id) => Transaction<any>;
findPrev: (id:Id) => Transaction<any>;
iterate: (t:any,start:?Id,end:?Id,gen:any) => Transaction<any>;
}

View File

@ -1,214 +0,0 @@
var $ = require('gulp-load-plugins')()
var minimist = require('minimist')
var browserify = require('browserify')
var source = require('vinyl-source-stream')
var buffer = require('vinyl-buffer')
module.exports = function (gulp, helperOptions) {
var runSequence = require('run-sequence').use(gulp)
var options = minimist(process.argv.slice(2), {
string: ['modulename', 'export', 'name', 'port', 'testfiles', 'es6'],
default: {
moduleName: helperOptions.moduleName,
targetName: helperOptions.targetName,
export: 'ignore',
port: '8888',
testfiles: '**/*.spec.js',
es6: false,
browserify: helperOptions.browserify != null ? helperOptions.browserify : false,
includeRuntime: helperOptions.includeRuntime || false,
debug: false
}
})
if (options.es6 !== false) {
options.es6 = true
}
var files = {
dist: helperOptions.entry,
specs: helperOptions.specs,
src: './src/**/*.js'
}
if (options.includeRuntime) {
files.distEs5 = ['node_modules/regenerator/runtime.js', files.dist]
} else {
files.distEs5 = [files.dist]
}
var header = require('gulp-header')
var banner = ['/**',
' * <%= pkg.name %> - <%= pkg.description %>',
' * @version v<%= pkg.version %>',
' * @link <%= pkg.homepage %>',
' * @license <%= pkg.license %>',
' */',
''].join('\n')
gulp.task('dist:es5', function () {
var babelOptions = {
presets: ['es2015']
}
return (browserify({
entries: files.distEs5,
debug: true,
standalone: options.moduleName
}).transform('babelify', babelOptions)
.bundle()
.pipe(source(options.targetName))
.pipe(buffer())
.pipe($.sourcemaps.init({loadMaps: true}))
.pipe($.if(!options.debug, $.uglify().on('error', function (e) {
console.log('\x07', e.message, JSON.stringify(e)); return this.end()
})))
.pipe(header(banner, { pkg: require('./package.json') }))
.pipe($.sourcemaps.write('.'))
.pipe(gulp.dest('./dist/')))
})
gulp.task('dist:es6', function () {
return (browserify({
entries: files.dist,
debug: true,
standalone: options.moduleName
}).bundle()
.pipe(source(options.targetName))
.pipe(buffer())
.pipe($.sourcemaps.init({loadMaps: true}))
// .pipe($.uglify()) -- generators not yet supported see #448
.pipe($.rename({
extname: '.es6'
}))
.pipe(header(banner, { pkg: require('./package.json') }))
.pipe($.sourcemaps.write('.'))
.pipe(gulp.dest('./dist/')))
})
gulp.task('dist', ['dist:es6', 'dist:es5'])
gulp.task('watch:dist', function (cb) {
options.debug = true
gulp.src(['./README.md'])
.pipe($.watch('./README.md'))
.pipe(gulp.dest('./dist/'))
runSequence('dist', function () {
gulp.watch(files.src.concat('./README.md'), ['dist'])
cb()
})
})
gulp.task('dev:node', ['test'], function () {
gulp.watch(files.src, ['test'])
})
gulp.task('spec-build', function () {
var browserify = require('browserify')
var source = require('vinyl-source-stream')
var buffer = require('vinyl-buffer')
return browserify({
entries: files.specs, // .concat(files.distEs5),
debug: true
})// .transform('babelify', { presets: ['es2015'] })
.bundle()
.pipe(source('specs.js'))
.pipe(buffer())
// .pipe($.sourcemaps.init({loadMaps: true}))
// .pipe($.sourcemaps.write('.'))
.pipe(gulp.dest('./build/'))
})
gulp.task('dev:browser', ['spec-build'], function () {
gulp.watch(files.src, ['spec-build'])
return gulp.src('./build/specs.js')
.pipe($.jasmineBrowser.specRunner())
.pipe($.jasmineBrowser.server({port: options.port}))
})
gulp.task('test', function () {
return gulp.src(files.specs)
.pipe($.jasmine({
verbose: true,
includeStuckTrace: true
}))
})
gulp.task('updateSubmodule', function () {
return gulp.src('./package.json', {read: false})
.pipe($.shell([
'git submodule update --init',
'cd dist && git pull origin dist'
]))
})
gulp.task('bump', function (cb) {
gulp.src(['./package.json', './bower.json', './dist/bower.json'], {base: '.'})
.pipe($.prompt.prompt({
type: 'checkbox',
name: 'bump',
message: 'What type of bump would you like to do?',
choices: ['patch', 'minor', 'major']
}, function (res) {
if (res.bump.length === 0) {
console.info('You have to select a bump type. Now I\'m going to use "patch" as bump type..')
}
var bumptype = res.bump[0]
if (bumptype === 'major') {
runSequence('bump_major', cb)
} else if (bumptype === 'minor') {
runSequence('bump_minor', cb)
} else {
runSequence('bump_patch', cb)
}
}))
})
gulp.task('bump_patch', function () {
return gulp.src(['./package.json', './bower.json', './dist/bower.json'], {base: '.'})
.pipe($.bump({type: 'patch'}))
.pipe(gulp.dest('./'))
})
gulp.task('bump_minor', function () {
return gulp.src(['./package.json', './bower.json', './dist/bower.json'], {base: '.'})
.pipe($.bump({type: 'minor'}))
.pipe(gulp.dest('./'))
})
gulp.task('bump_major', function () {
return gulp.src(['./package.json', './bower.json', './dist/bower.json'], {base: '.'})
.pipe($.bump({type: 'major'}))
.pipe(gulp.dest('./'))
})
gulp.task('publish_commits', function () {
return gulp.src('./package.json')
.pipe($.prompt.confirm({
message: 'Are you sure you want to publish this release?',
default: false
}))
.pipe($.shell([
'cp README.md dist',
'standard',
'echo "Deploying version <%= getVersion(file.path) %>"',
'git pull',
'cd ./dist/ && git add -A',
'cd ./dist/ && git commit -am "Deploy <%= getVersion(file.path) %>" -n',
'cd ./dist/ && git push origin HEAD:dist',
'cd ./dist/ && git tag -a v<%= getVersion(file.path) %> -m "Release <%= getVersion(file.path) %>"',
'cd ./dist/ && git push origin --tags',
'git commit -am "Release <%= getVersion(file.path) %>" -n',
'git push',
'npm publish',
'echo Finished'
], {
templateData: {
getVersion: function () {
return JSON.parse(String.fromCharCode.apply(null, this.file._contents)).version
}
}
}))
})
gulp.task('publish', function (cb) {
/* TODO: include 'test',*/
runSequence('updateSubmodule', 'bump', 'dist', 'publish_commits', cb)
})
}

View File

@ -1,104 +0,0 @@
/* eslint-env node */
/** Gulp Commands
gulp command*
[--export ModuleType]
[--name ModuleName]
[--testport TestPort]
[--testfiles TestFiles]
Module name (ModuleName):
Compile this to "y.js" (default)
Supported module types (ModuleType):
- amd
- amdStrict
- common
- commonStrict
- ignore (default)
- system
- umd
- umdStrict
Test port (TestPort):
Serve the specs on port 8888 (default)
Test files (TestFiles):
Specify which specs to use!
Commands:
- build:deploy
Build this library for deployment (es6->es5, minified)
- dev:browser
Watch the ./src directory.
Builds the library on changes.
Starts an http-server and serves the test suite on http://127.0.0.1:8888.
- dev:node
Watch the ./src directory.
Builds and specs the library on changes.
Usefull to run with node-inspector.
`node-debug $(which gulp) dev:node
- test:
Test this library
*/
var gulp = require('gulp')
var $ = require('gulp-load-plugins')()
var runSequence = require('run-sequence').use(gulp)
require('./gulpfile.helper.js')(gulp, {
polyfills: [],
entry: './src/y.js',
targetName: 'y.js',
moduleName: 'Y',
includeRuntime: true,
specs: [
'./src/Database.spec.js',
'../y-array/src/Array.spec.js',
'../y-map/src/Map.spec.js'
]
})
gulp.task('dev:examples', ['watch:dist'], function () {
// watch all distfiles and copy them to bower_components
var distfiles = ['./dist/*.{js,es6}', './dist/*.{js,es6}.map', '../y-*/dist/*.{js,es6}', '../y-*/dist/*.{js,es6}.map']
gulp.src(distfiles)
.pipe($.watch(distfiles))
.pipe($.rename(function (path) {
var dir = path.dirname.split(/[\\\/]/)[0]
console.log(JSON.stringify(path))
path.dirname = dir === '.' ? 'yjs' : dir
}))
.pipe(gulp.dest('./dist/Examples/bower_components/'))
return $.serve('dist/Examples/')()
})
gulp.task('default', ['updateSubmodule'], function (cb) {
gulp.src('package.json')
.pipe($.prompt.prompt({
type: 'checkbox',
name: 'tasks',
message: 'Which tasks would you like to run?',
choices: [
'test Test this project',
'dev:examples Serve the examples directory in ./dist/',
'dev:browser Watch files & serve the testsuite for the browser',
'dev:nodejs Watch filse & test this project with nodejs',
'bump Bump the current state of the project',
'publish Publish this project. Creates a github tag',
'dist Build the distribution files'
]
}, function (res) {
var tasks = res.tasks.map(function (task) {
return task.split(' ')[0]
})
if (tasks.length > 0) {
console.info('gulp ' + tasks.join(' '))
runSequence(tasks, cb)
} else {
console.info('Ok, .. goodbye')
}
}))
})

View File

@ -4,19 +4,15 @@
"description": "A framework for real-time p2p shared editing on any data", "description": "A framework for real-time p2p shared editing on any data",
"main": "./src/y.js", "main": "./src/y.js",
"scripts": { "scripts": {
"test": "node --harmony ./node_modules/.bin/gulp test", "lint": "standard",
"lint": "./node_modules/.bin/standard" "dist": "rollup -c rollup.dist.js"
}, },
"pre-commit": [ "pre-commit": [
"lint", "lint",
"test" "test"
], ],
"standard": { "standard": {
"parser": "babel-eslint",
"ignore": [ "ignore": [
"build/**",
"dist/**",
"declarations/**",
"./y.js", "./y.js",
"./y.js.map" "./y.js.map"
] ]
@ -42,41 +38,22 @@
}, },
"homepage": "http://y-js.org", "homepage": "http://y-js.org",
"devDependencies": { "devDependencies": {
"babel-eslint": "^5.0.0-beta6", "babel-cli": "^6.24.1",
"babel-plugin-transform-runtime": "^6.1.18", "babel-plugin-external-helpers": "^6.22.0",
"babel-preset-es2015": "^6.1.18", "babel-plugin-transform-regenerator": "^6.24.1",
"babelify": "^7.2.0", "babel-plugin-transform-runtime": "^6.23.0",
"browserify": "^12.0.1", "rollup-plugin-babel": "^2.7.1",
"eslint": "^1.10.2", "rollup-plugin-commonjs": "^8.0.2",
"gulp": "^3.9.0", "rollup-plugin-multi-entry": "^2.0.1",
"gulp-bump": "^1.0.0", "rollup-plugin-node-resolve": "^3.0.0",
"gulp-concat": "^2.6.0", "rollup-plugin-uglify": "^1.0.2",
"gulp-filter": "^3.0.1", "rollup-regenerator-runtime": "^6.23.1",
"gulp-git": "^1.6.0", "rollup-watch": "^3.2.2",
"gulp-header": "^1.8.8", "standard": "^10.0.2"
"gulp-if": "^2.0.0",
"gulp-jasmine": "^2.0.1",
"gulp-jasmine-browser": "^0.2.3",
"gulp-load-plugins": "^1.0.0",
"gulp-prompt": "^0.1.2",
"gulp-rename": "^1.2.2",
"gulp-serve": "^1.2.0",
"gulp-shell": "^0.5.1",
"gulp-sourcemaps": "^1.5.2",
"gulp-tag-version": "^1.3.0",
"gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.6",
"gulp-watch": "^4.3.5",
"minimist": "^1.2.0",
"pre-commit": "^1.1.1",
"regenerator": "^0.8.42",
"run-sequence": "^1.1.4",
"seedrandom": "^2.4.2",
"standard": "^5.2.2",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
}, },
"dependencies": { "dependencies": {
"debug": "^2.6.3" "babel-plugin-external-helpers": "^6.22.0",
"babel-preset-latest": "^6.24.1",
"debug": "^2.6.6"
} }
} }

47
rollup.dist.js Normal file
View File

@ -0,0 +1,47 @@
import inject from 'rollup-plugin-inject'
import babel from 'rollup-plugin-babel'
import uglify from 'rollup-plugin-uglify'
import nodeResolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json')
export default {
entry: 'src/y.js',
moduleName: 'yjs',
format: 'umd',
plugins: [
nodeResolve({
main: true,
module: true,
browser: true
}),
commonjs(),
babel({
runtimeHelpers: true
}),
inject({
regeneratorRuntime: 'regenerator-runtime'
}),
uglify({
output: {
comments: function (node, comment) {
var text = comment.value
var type = comment.type
if (type === 'comment2') {
// multiline comment
return /@license/i.test(text)
}
}
}
})
],
dest: 'y.js',
sourceMap: true,
banner: `
/**
* ${pkg.name} - ${pkg.description}
* @version v${pkg.version}
* @license ${pkg.license}
*/
`
}

20
rollup.test.js Normal file
View File

@ -0,0 +1,20 @@
import nodeResolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import multiEntry from 'rollup-plugin-multi-entry'
export default {
entry: 'tests/*.js',
moduleName: 'y-array-tests',
format: 'umd',
plugins: [
nodeResolve({
main: true,
module: true,
browser: true
}),
commonjs(),
multiEntry()
],
dest: 'y-array.test.js',
sourceMap: true
}

View File

@ -4,7 +4,7 @@
function canRead (auth) { return auth === 'read' || auth === 'write' } function canRead (auth) { return auth === 'read' || auth === 'write' }
function canWrite (auth) { return auth === 'write' } function canWrite (auth) { return auth === 'write' }
module.exports = function (Y/* :any */) { export default function extendConnector (Y/* :any */) {
class AbstractConnector { class AbstractConnector {
/* :: /* ::
y: YConfig; y: YConfig;
@ -113,7 +113,7 @@ module.exports = function (Y/* :any */) {
this.userEventListeners.push(f) this.userEventListeners.push(f)
} }
removeUserEventListener (f) { removeUserEventListener (f) {
this.userEventListeners = this.userEventListeners.filter(g => { f !== g }) this.userEventListeners = this.userEventListeners.filter(g => f !== g)
} }
userLeft (user) { userLeft (user) {
if (this.connections[user] != null) { if (this.connections[user] != null) {
@ -181,9 +181,9 @@ module.exports = function (Y/* :any */) {
var conn = this var conn = this
if (syncUser != null) { if (syncUser != null) {
this.currentSyncTarget = syncUser this.currentSyncTarget = syncUser
this.y.db.requestTransaction(function *() { this.y.db.requestTransaction(function * () {
var stateSet = yield* this.getStateSet() var stateSet = yield * this.getStateSet()
var deleteSet = yield* this.getDeleteSet() var deleteSet = yield * this.getDeleteSet()
var answer = { var answer = {
type: 'sync step 1', type: 'sync step 1',
stateSet: stateSet, stateSet: stateSet,
@ -198,11 +198,11 @@ module.exports = function (Y/* :any */) {
}) })
} else { } else {
if (!conn.isSynced) { if (!conn.isSynced) {
this.y.db.requestTransaction(function *() { this.y.db.requestTransaction(function * () {
if (!conn.isSynced) { if (!conn.isSynced) {
// it is crucial that isSynced is set at the time garbageCollectAfterSync is called // it is crucial that isSynced is set at the time garbageCollectAfterSync is called
conn.isSynced = true conn.isSynced = true
yield* this.garbageCollectAfterSync() yield * this.garbageCollectAfterSync()
// call whensynced listeners // call whensynced listeners
for (var f of conn.whenSyncedListeners) { for (var f of conn.whenSyncedListeners) {
f() f()
@ -268,7 +268,7 @@ module.exports = function (Y/* :any */) {
type: 'sync stop', type: 'sync stop',
protocolVersion: this.protocolVersion protocolVersion: this.protocolVersion
}) })
return Promise.reject('Incompatible protocol version') return Promise.reject(new Error('Incompatible protocol version'))
} }
if (message.auth != null && this.connections[sender] != null) { if (message.auth != null && this.connections[sender] != null) {
// authenticate using auth in message // authenticate using auth in message
@ -293,13 +293,13 @@ module.exports = function (Y/* :any */) {
let conn = this let conn = this
let m = message let m = message
this.y.db.requestTransaction(function *() { this.y.db.requestTransaction(function * () {
var currentStateSet = yield* this.getStateSet() var currentStateSet = yield * this.getStateSet()
if (canWrite(auth)) { if (canWrite(auth)) {
yield* this.applyDeleteSet(m.deleteSet) yield * this.applyDeleteSet(m.deleteSet)
} }
var ds = yield* this.getDeleteSet() var ds = yield * this.getDeleteSet()
var answer = { var answer = {
type: 'sync step 2', type: 'sync step 2',
stateSet: currentStateSet, stateSet: currentStateSet,
@ -308,9 +308,9 @@ module.exports = function (Y/* :any */) {
auth: this.authInfo auth: this.authInfo
} }
if (message.preferUntransformed === true && Object.keys(m.stateSet).length === 0) { if (message.preferUntransformed === true && Object.keys(m.stateSet).length === 0) {
answer.osUntransformed = yield* this.getOperationsUntransformed() answer.osUntransformed = yield * this.getOperationsUntransformed()
} else { } else {
answer.os = yield* this.getOperations(m.stateSet) answer.os = yield * this.getOperations(m.stateSet)
} }
conn.send(sender, answer) conn.send(sender, answer)
if (this.forwardToSyncingClients) { if (this.forwardToSyncingClients) {
@ -338,9 +338,9 @@ module.exports = function (Y/* :any */) {
this.syncStep2 = defer.promise this.syncStep2 = defer.promise
let m /* :MessageSyncStep2 */ = message let m /* :MessageSyncStep2 */ = message
db.requestTransaction(function * () { db.requestTransaction(function * () {
yield* this.applyDeleteSet(m.deleteSet) yield * this.applyDeleteSet(m.deleteSet)
if (m.osUntransformed != null) { if (m.osUntransformed != null) {
yield* this.applyOperationsUntransformed(m.osUntransformed, m.stateSet) yield * this.applyOperationsUntransformed(m.osUntransformed, m.stateSet)
} else { } else {
this.store.apply(m.os) this.store.apply(m.os)
} }
@ -388,7 +388,7 @@ module.exports = function (Y/* :any */) {
} }
}) })
} else { } else {
return Promise.reject('Unable to deliver message') return Promise.reject(new Error('Unable to deliver message'))
} }
} }
_setSyncedWith (user) { _setSyncedWith (user) {

View File

@ -1,7 +1,7 @@
/* @flow */ /* @flow */
'use strict' 'use strict'
module.exports = function (Y /* :any */) { export default function extendDatabase (Y /* :any */) {
/* /*
Partial definition of an OperationStore. Partial definition of an OperationStore.
TODO: name it Database, operation store only holds operations. TODO: name it Database, operation store only holds operations.
@ -42,11 +42,11 @@ module.exports = function (Y /* :any */) {
this.dbOpts = opts this.dbOpts = opts
var os = this var os = this
this.userId = null this.userId = null
var resolve var resolve_
this.userIdPromise = new Promise(function (r) { this.userIdPromise = new Promise(function (resolve) {
resolve = r resolve_ = resolve
}) })
this.userIdPromise.resolve = resolve this.userIdPromise.resolve = resolve_
// whether to broadcast all applied operations (insert & delete hook) // whether to broadcast all applied operations (insert & delete hook)
this.forwardAppliedOperations = false this.forwardAppliedOperations = false
// E.g. this.listenersById[id] : Array<Listener> // E.g. this.listenersById[id] : Array<Listener>
@ -71,7 +71,7 @@ module.exports = function (Y /* :any */) {
this.waitingTransactions = [] this.waitingTransactions = []
this.transactionInProgress = false this.transactionInProgress = false
this.transactionIsFlushed = false this.transactionIsFlushed = false
if (typeof YConcurrency_TestingMode !== 'undefined') { if (typeof YConcurrencyTestingMode !== 'undefined') {
this.executeOrder = [] this.executeOrder = []
} }
this.gc1 = [] // first stage this.gc1 = [] // first stage
@ -88,7 +88,7 @@ module.exports = function (Y /* :any */) {
if (os.y.connector != null && os.y.connector.isSynced) { if (os.y.connector != null && os.y.connector.isSynced) {
for (var i = 0; i < os.gc2.length; i++) { for (var i = 0; i < os.gc2.length; i++) {
var oid = os.gc2[i] var oid = os.gc2[i]
yield* this.garbageCollectOperation(oid) yield * this.garbageCollectOperation(oid)
} }
os.gc2 = os.gc1 os.gc2 = os.gc1
os.gc1 = [] os.gc1 = []
@ -177,7 +177,7 @@ module.exports = function (Y /* :any */) {
}) })
} }
addToDebug () { addToDebug () {
if (typeof YConcurrency_TestingMode !== 'undefined') { if (typeof YConcurrencyTestingMode !== 'undefined') {
var command /* :string */ = Array.prototype.map.call(arguments, function (s) { var command /* :string */ = Array.prototype.map.call(arguments, function (s) {
if (typeof s === 'string') { if (typeof s === 'string') {
return s return s
@ -201,10 +201,10 @@ module.exports = function (Y /* :any */) {
self.gc1 = [] self.gc1 = []
self.gc2 = [] self.gc2 = []
for (var i = 0; i < ungc.length; i++) { for (var i = 0; i < ungc.length; i++) {
var op = yield* this.getOperation(ungc[i]) var op = yield * this.getOperation(ungc[i])
if (op != null) { if (op != null) {
delete op.gc delete op.gc
yield* this.setOperation(op) yield * this.setOperation(op)
} }
} }
resolve() resolve()
@ -234,12 +234,12 @@ module.exports = function (Y /* :any */) {
if (left != null && left.deleted === true) { if (left != null && left.deleted === true) {
gc = true gc = true
} else if (op.content != null && op.content.length > 1) { } else if (op.content != null && op.content.length > 1) {
op = yield* this.getInsertionCleanStart([op.id[0], op.id[1] + 1]) op = yield * this.getInsertionCleanStart([op.id[0], op.id[1] + 1])
gc = true gc = true
} }
if (gc) { if (gc) {
op.gc = true op.gc = true
yield* this.setOperation(op) yield * this.setOperation(op)
this.store.queueGarbageCollector(op.id) this.store.queueGarbageCollector(op.id)
return true return true
} }
@ -275,7 +275,7 @@ module.exports = function (Y /* :any */) {
var self = this var self = this
self.requestTransaction(function * () { self.requestTransaction(function * () {
self.userId = userId self.userId = userId
var state = yield* this.getState(userId) var state = yield * this.getState(userId)
self.opClock = state.clock self.opClock = state.clock
self.userIdPromise.resolve(userId) self.userIdPromise.resolve(userId)
}) })
@ -363,7 +363,7 @@ module.exports = function (Y /* :any */) {
for (let key = 0; key < exeNow.length; key++) { for (let key = 0; key < exeNow.length; key++) {
let o = exeNow[key].op let o = exeNow[key].op
yield* store.tryExecute.call(this, o) yield * store.tryExecute.call(this, o)
} }
for (var sid in ls) { for (var sid in ls) {
@ -371,9 +371,9 @@ module.exports = function (Y /* :any */) {
var id = JSON.parse(sid) var id = JSON.parse(sid)
var op var op
if (typeof id[1] === 'string') { if (typeof id[1] === 'string') {
op = yield* this.getOperation(id) op = yield * this.getOperation(id)
} else { } else {
op = yield* this.getInsertion(id) op = yield * this.getInsertion(id)
} }
if (op == null) { if (op == null) {
store.listenersById[sid] = l store.listenersById[sid] = l
@ -382,7 +382,7 @@ module.exports = function (Y /* :any */) {
let listener = l[i] let listener = l[i]
let o = listener.op let o = listener.op
if (--listener.missing === 0) { if (--listener.missing === 0) {
yield* store.tryExecute.call(this, o) yield * store.tryExecute.call(this, o)
} }
} }
} }
@ -402,12 +402,12 @@ module.exports = function (Y /* :any */) {
* tryExecute (op) { * tryExecute (op) {
this.store.addToDebug('yield* this.store.tryExecute.call(this, ', JSON.stringify(op), ')') this.store.addToDebug('yield* this.store.tryExecute.call(this, ', JSON.stringify(op), ')')
if (op.struct === 'Delete') { if (op.struct === 'Delete') {
yield* Y.Struct.Delete.execute.call(this, op) yield * Y.Struct.Delete.execute.call(this, op)
// this is now called in Transaction.deleteOperation! // this is now called in Transaction.deleteOperation!
// yield* this.store.operationAdded(this, op) // yield* this.store.operationAdded(this, op)
} else { } else {
// check if this op was defined // check if this op was defined
var defined = yield* this.getInsertion(op.id) var defined = yield * this.getInsertion(op.id)
while (defined != null && defined.content != null) { while (defined != null && defined.content != null) {
// check if this op has a longer content in the case it is defined // check if this op has a longer content in the case it is defined
if (defined.id[1] + defined.content.length < op.id[1] + op.content.length) { if (defined.id[1] + defined.content.length < op.id[1] + op.content.length) {
@ -416,23 +416,23 @@ module.exports = function (Y /* :any */) {
op.id = [op.id[0], op.id[1] + overlapSize] op.id = [op.id[0], op.id[1] + overlapSize]
op.left = Y.utils.getLastId(defined) op.left = Y.utils.getLastId(defined)
op.origin = op.left op.origin = op.left
defined = yield* this.getOperation(op.id) // getOperation suffices here defined = yield * this.getOperation(op.id) // getOperation suffices here
} else { } else {
break break
} }
} }
if (defined == null) { if (defined == null) {
var opid = op.id var opid = op.id
var isGarbageCollected = yield* this.isGarbageCollected(opid) var isGarbageCollected = yield * this.isGarbageCollected(opid)
if (!isGarbageCollected) { if (!isGarbageCollected) {
// TODO: reduce number of get / put calls for op .. // TODO: reduce number of get / put calls for op ..
yield* Y.Struct[op.struct].execute.call(this, op) yield * Y.Struct[op.struct].execute.call(this, op)
yield* this.addOperation(op) yield * this.addOperation(op)
yield* this.store.operationAdded(this, op) yield * this.store.operationAdded(this, op)
// operationAdded can change op.. // operationAdded can change op..
op = yield* this.getOperation(opid) op = yield * this.getOperation(opid)
// if insertion, try to combine with left // if insertion, try to combine with left
yield* this.tryCombineWithLeft(op) yield * this.tryCombineWithLeft(op)
} }
} }
} }
@ -453,11 +453,11 @@ module.exports = function (Y /* :any */) {
if (op.struct === 'Delete') { if (op.struct === 'Delete') {
var type = this.initializedTypes[JSON.stringify(op.targetParent)] var type = this.initializedTypes[JSON.stringify(op.targetParent)]
if (type != null) { if (type != null) {
yield* type._changed(transaction, op) yield * type._changed(transaction, op)
} }
} else { } else {
// increase SS // increase SS
yield* transaction.updateState(op.id[0]) yield * transaction.updateState(op.id[0])
var opLen = op.content != null ? op.content.length : 1 var opLen = op.content != null ? op.content.length : 1
for (let i = 0; i < opLen; i++) { for (let i = 0; i < opLen; i++) {
// notify whenOperation listeners (by id) // notify whenOperation listeners (by id)
@ -477,9 +477,9 @@ module.exports = function (Y /* :any */) {
// if parent is deleted, mark as gc'd and return // if parent is deleted, mark as gc'd and return
if (op.parent != null) { if (op.parent != null) {
var parentIsDeleted = yield* transaction.isDeleted(op.parent) var parentIsDeleted = yield * transaction.isDeleted(op.parent)
if (parentIsDeleted) { if (parentIsDeleted) {
yield* transaction.deleteList(op.id) yield * transaction.deleteList(op.id)
return return
} }
} }
@ -487,7 +487,7 @@ module.exports = function (Y /* :any */) {
// notify parent, if it was instanciated as a custom type // notify parent, if it was instanciated as a custom type
if (t != null) { if (t != null) {
let o = Y.utils.copyOperation(op) let o = Y.utils.copyOperation(op)
yield* t._changed(transaction, o) yield * t._changed(transaction, o)
} }
if (!op.deleted) { if (!op.deleted) {
// Delete if DS says this is actually deleted // Delete if DS says this is actually deleted
@ -496,13 +496,13 @@ module.exports = function (Y /* :any */) {
// TODO: !! console.log('TODO: change this before commiting') // TODO: !! console.log('TODO: change this before commiting')
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
var id = [startId[0], startId[1] + i] var id = [startId[0], startId[1] + i]
var opIsDeleted = yield* transaction.isDeleted(id) var opIsDeleted = yield * transaction.isDeleted(id)
if (opIsDeleted) { if (opIsDeleted) {
var delop = { var delop = {
struct: 'Delete', struct: 'Delete',
target: id target: id
} }
yield* this.tryExecute.call(transaction, delop) yield * this.tryExecute.call(transaction, delop)
} }
} }
} }
@ -511,12 +511,12 @@ module.exports = function (Y /* :any */) {
whenTransactionsFinished () { whenTransactionsFinished () {
if (this.transactionInProgress) { if (this.transactionInProgress) {
if (this.transactionsFinished == null) { if (this.transactionsFinished == null) {
var resolve var resolve_
var promise = new Promise(function (r) { var promise = new Promise(function (resolve) {
resolve = r resolve_ = resolve
}) })
this.transactionsFinished = { this.transactionsFinished = {
resolve: resolve, resolve: resolve_,
promise: promise promise: promise
} }
} }
@ -540,7 +540,7 @@ module.exports = function (Y /* :any */) {
} else { } else {
this.transactionIsFlushed = true this.transactionIsFlushed = true
return function * () { return function * () {
yield* this.flush() yield * this.flush()
} }
} }
} else { } else {
@ -571,9 +571,9 @@ module.exports = function (Y /* :any */) {
var sid = JSON.stringify(id) var sid = JSON.stringify(id)
var t = this.store.initializedTypes[sid] var t = this.store.initializedTypes[sid]
if (t == null) { if (t == null) {
var op/* :MapStruct | ListStruct */ = yield* this.getOperation(id) var op/* :MapStruct | ListStruct */ = yield * this.getOperation(id)
if (op != null) { if (op != null) {
t = yield* Y[op.type].typeDefinition.initType.call(this, this.store, op, args) t = yield * Y[op.type].typeDefinition.initType.call(this, this.store, op, args)
this.store.initializedTypes[sid] = t this.store.initializedTypes[sid] = t
} }
} }
@ -590,9 +590,9 @@ module.exports = function (Y /* :any */) {
this.requestTransaction(function * () { this.requestTransaction(function * () {
if (op.id[0] === '_') { if (op.id[0] === '_') {
yield* this.setOperation(op) yield * this.setOperation(op)
} else { } else {
yield* this.applyCreatedOperations([op]) yield * this.applyCreatedOperations([op])
} }
}) })
var t = Y[op.type].typeDefinition.createType(this, op, typedefinition[1]) var t = Y[op.type].typeDefinition.createType(this, op, typedefinition[1])

View File

@ -17,141 +17,141 @@ for (let database of databases) {
}) })
afterEach(function (done) { afterEach(function (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.store.destroy() yield * this.store.destroy()
done() done()
}) })
}) })
it('Deleted operation is deleted', async(function * (done) { it('Deleted operation is deleted', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.markDeleted(['u1', 10], 1) yield * this.markDeleted(['u1', 10], 1)
expect(yield* this.isDeleted(['u1', 10])).toBeTruthy() expect(yield * this.isDeleted(['u1', 10])).toBeTruthy()
expect(yield* this.getDeleteSet()).toEqual({'u1': [[10, 1, false]]}) expect(yield * this.getDeleteSet()).toEqual({'u1': [[10, 1, false]]})
done() done()
}) })
})) }))
it('Deleted operation extends other deleted operation', async(function * (done) { it('Deleted operation extends other deleted operation', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.markDeleted(['u1', 10], 1) yield * this.markDeleted(['u1', 10], 1)
yield* this.markDeleted(['u1', 11], 1) yield * this.markDeleted(['u1', 11], 1)
expect(yield* this.isDeleted(['u1', 10])).toBeTruthy() expect(yield * this.isDeleted(['u1', 10])).toBeTruthy()
expect(yield* this.isDeleted(['u1', 11])).toBeTruthy() expect(yield * this.isDeleted(['u1', 11])).toBeTruthy()
expect(yield* this.getDeleteSet()).toEqual({'u1': [[10, 2, false]]}) expect(yield * this.getDeleteSet()).toEqual({'u1': [[10, 2, false]]})
done() done()
}) })
})) }))
it('Deleted operation extends other deleted operation', async(function * (done) { it('Deleted operation extends other deleted operation', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.markDeleted(['0', 3], 1) yield * this.markDeleted(['0', 3], 1)
yield* this.markDeleted(['0', 4], 1) yield * this.markDeleted(['0', 4], 1)
yield* this.markDeleted(['0', 2], 1) yield * this.markDeleted(['0', 2], 1)
expect(yield* this.getDeleteSet()).toEqual({'0': [[2, 3, false]]}) expect(yield * this.getDeleteSet()).toEqual({'0': [[2, 3, false]]})
done() done()
}) })
})) }))
it('Debug #1', async(function * (done) { it('Debug #1', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.markDeleted(['166', 0], 1) yield * this.markDeleted(['166', 0], 1)
yield* this.markDeleted(['166', 2], 1) yield * this.markDeleted(['166', 2], 1)
yield* this.markDeleted(['166', 0], 1) yield * this.markDeleted(['166', 0], 1)
yield* this.markDeleted(['166', 2], 1) yield * this.markDeleted(['166', 2], 1)
yield* this.markGarbageCollected(['166', 2], 1) yield * this.markGarbageCollected(['166', 2], 1)
yield* this.markDeleted(['166', 1], 1) yield * this.markDeleted(['166', 1], 1)
yield* this.markDeleted(['166', 3], 1) yield * this.markDeleted(['166', 3], 1)
yield* this.markGarbageCollected(['166', 3], 1) yield * this.markGarbageCollected(['166', 3], 1)
yield* this.markDeleted(['166', 0], 1) yield * this.markDeleted(['166', 0], 1)
expect(yield* this.getDeleteSet()).toEqual({'166': [[0, 2, false], [2, 2, true]]}) expect(yield * this.getDeleteSet()).toEqual({'166': [[0, 2, false], [2, 2, true]]})
done() done()
}) })
})) }))
it('Debug #2', async(function * (done) { it('Debug #2', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.markDeleted(['293', 0], 1) yield * this.markDeleted(['293', 0], 1)
yield* this.markDeleted(['291', 2], 1) yield * this.markDeleted(['291', 2], 1)
yield* this.markDeleted(['291', 2], 1) yield * this.markDeleted(['291', 2], 1)
yield* this.markGarbageCollected(['293', 0], 1) yield * this.markGarbageCollected(['293', 0], 1)
yield* this.markDeleted(['293', 1], 1) yield * this.markDeleted(['293', 1], 1)
yield* this.markGarbageCollected(['291', 2], 1) yield * this.markGarbageCollected(['291', 2], 1)
expect(yield* this.getDeleteSet()).toEqual({'291': [[2, 1, true]], '293': [[0, 1, true], [1, 1, false]]}) expect(yield * this.getDeleteSet()).toEqual({'291': [[2, 1, true]], '293': [[0, 1, true], [1, 1, false]]})
done() done()
}) })
})) }))
it('Debug #3', async(function * (done) { it('Debug #3', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.markDeleted(['581', 0], 1) yield * this.markDeleted(['581', 0], 1)
yield* this.markDeleted(['581', 1], 1) yield * this.markDeleted(['581', 1], 1)
yield* this.markDeleted(['580', 0], 1) yield * this.markDeleted(['580', 0], 1)
yield* this.markDeleted(['580', 0], 1) yield * this.markDeleted(['580', 0], 1)
yield* this.markGarbageCollected(['581', 0], 1) yield * this.markGarbageCollected(['581', 0], 1)
yield* this.markDeleted(['581', 2], 1) yield * this.markDeleted(['581', 2], 1)
yield* this.markDeleted(['580', 1], 1) yield * this.markDeleted(['580', 1], 1)
yield* this.markDeleted(['580', 2], 1) yield * this.markDeleted(['580', 2], 1)
yield* this.markDeleted(['580', 1], 1) yield * this.markDeleted(['580', 1], 1)
yield* this.markDeleted(['580', 2], 1) yield * this.markDeleted(['580', 2], 1)
yield* this.markGarbageCollected(['581', 2], 1) yield * this.markGarbageCollected(['581', 2], 1)
yield* this.markGarbageCollected(['581', 1], 1) yield * this.markGarbageCollected(['581', 1], 1)
yield* this.markGarbageCollected(['580', 1], 1) yield * this.markGarbageCollected(['580', 1], 1)
expect(yield* this.getDeleteSet()).toEqual({'580': [[0, 1, false], [1, 1, true], [2, 1, false]], '581': [[0, 3, true]]}) expect(yield * this.getDeleteSet()).toEqual({'580': [[0, 1, false], [1, 1, true], [2, 1, false]], '581': [[0, 3, true]]})
done() done()
}) })
})) }))
it('Debug #4', async(function * (done) { it('Debug #4', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.markDeleted(['544', 0], 1) yield * this.markDeleted(['544', 0], 1)
yield* this.markDeleted(['543', 2], 1) yield * this.markDeleted(['543', 2], 1)
yield* this.markDeleted(['544', 0], 1) yield * this.markDeleted(['544', 0], 1)
yield* this.markDeleted(['543', 2], 1) yield * this.markDeleted(['543', 2], 1)
yield* this.markGarbageCollected(['544', 0], 1) yield * this.markGarbageCollected(['544', 0], 1)
yield* this.markDeleted(['545', 1], 1) yield * this.markDeleted(['545', 1], 1)
yield* this.markDeleted(['543', 4], 1) yield * this.markDeleted(['543', 4], 1)
yield* this.markDeleted(['543', 3], 1) yield * this.markDeleted(['543', 3], 1)
yield* this.markDeleted(['544', 1], 1) yield * this.markDeleted(['544', 1], 1)
yield* this.markDeleted(['544', 2], 1) yield * this.markDeleted(['544', 2], 1)
yield* this.markDeleted(['544', 1], 1) yield * this.markDeleted(['544', 1], 1)
yield* this.markDeleted(['544', 2], 1) yield * this.markDeleted(['544', 2], 1)
yield* this.markGarbageCollected(['543', 2], 1) yield * this.markGarbageCollected(['543', 2], 1)
yield* this.markGarbageCollected(['543', 4], 1) yield * this.markGarbageCollected(['543', 4], 1)
yield* this.markGarbageCollected(['544', 2], 1) yield * this.markGarbageCollected(['544', 2], 1)
yield* this.markGarbageCollected(['543', 3], 1) yield * this.markGarbageCollected(['543', 3], 1)
expect(yield* this.getDeleteSet()).toEqual({'543': [[2, 3, true]], '544': [[0, 1, true], [1, 1, false], [2, 1, true]], '545': [[1, 1, false]]}) expect(yield * this.getDeleteSet()).toEqual({'543': [[2, 3, true]], '544': [[0, 1, true], [1, 1, false], [2, 1, true]], '545': [[1, 1, false]]})
done() done()
}) })
})) }))
it('Debug #5', async(function * (done) { it('Debug #5', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.applyDeleteSet({'16': [[1, 2, false]], '17': [[0, 1, true], [1, 3, false]]}) yield * this.applyDeleteSet({'16': [[1, 2, false]], '17': [[0, 1, true], [1, 3, false]]})
expect(yield* this.getDeleteSet()).toEqual({'16': [[1, 2, false]], '17': [[0, 1, true], [1, 3, false]]}) expect(yield * this.getDeleteSet()).toEqual({'16': [[1, 2, false]], '17': [[0, 1, true], [1, 3, false]]})
yield* this.applyDeleteSet({'16': [[1, 2, false]], '17': [[0, 4, true]]}) yield * this.applyDeleteSet({'16': [[1, 2, false]], '17': [[0, 4, true]]})
expect(yield* this.getDeleteSet()).toEqual({'16': [[1, 2, false]], '17': [[0, 4, true]]}) expect(yield * this.getDeleteSet()).toEqual({'16': [[1, 2, false]], '17': [[0, 4, true]]})
done() done()
}) })
})) }))
it('Debug #6', async(function * (done) { it('Debug #6', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.applyDeleteSet({'40': [[0, 3, false]]}) yield * this.applyDeleteSet({'40': [[0, 3, false]]})
expect(yield* this.getDeleteSet()).toEqual({'40': [[0, 3, false]]}) expect(yield * this.getDeleteSet()).toEqual({'40': [[0, 3, false]]})
yield* this.applyDeleteSet({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]}) yield * this.applyDeleteSet({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]})
expect(yield* this.getDeleteSet()).toEqual({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]}) expect(yield * this.getDeleteSet()).toEqual({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]})
done() done()
}) })
})) }))
it('Debug #7', async(function * (done) { it('Debug #7', async(function * (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.markDeleted(['9', 2], 1) yield * this.markDeleted(['9', 2], 1)
yield* this.markDeleted(['11', 2], 1) yield * this.markDeleted(['11', 2], 1)
yield* this.markDeleted(['11', 4], 1) yield * this.markDeleted(['11', 4], 1)
yield* this.markDeleted(['11', 1], 1) yield * this.markDeleted(['11', 1], 1)
yield* this.markDeleted(['9', 4], 1) yield * this.markDeleted(['9', 4], 1)
yield* this.markDeleted(['10', 0], 1) yield * this.markDeleted(['10', 0], 1)
yield* this.markGarbageCollected(['11', 2], 1) yield * this.markGarbageCollected(['11', 2], 1)
yield* this.markDeleted(['11', 2], 1) yield * this.markDeleted(['11', 2], 1)
yield* this.markGarbageCollected(['11', 3], 1) yield * this.markGarbageCollected(['11', 3], 1)
yield* this.markDeleted(['11', 3], 1) yield * this.markDeleted(['11', 3], 1)
yield* this.markDeleted(['11', 3], 1) yield * this.markDeleted(['11', 3], 1)
yield* this.markDeleted(['9', 4], 1) yield * this.markDeleted(['9', 4], 1)
yield* this.markDeleted(['10', 0], 1) yield * this.markDeleted(['10', 0], 1)
yield* this.markGarbageCollected(['11', 1], 1) yield * this.markGarbageCollected(['11', 1], 1)
yield* this.markDeleted(['11', 1], 1) yield * this.markDeleted(['11', 1], 1)
expect(yield* this.getDeleteSet()).toEqual({'9': [[2, 1, false], [4, 1, false]], '10': [[0, 1, false]], '11': [[1, 3, true], [4, 1, false]]}) expect(yield * this.getDeleteSet()).toEqual({'9': [[2, 1, false], [4, 1, false]], '10': [[0, 1, false]], '11': [[1, 3, true], [4, 1, false]]})
done() done()
}) })
})) }))
@ -167,54 +167,54 @@ for (let database of databases) {
}) })
afterEach(function (done) { afterEach(function (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.store.destroy() yield * this.store.destroy()
done() done()
}) })
}) })
it('debug #1', function (done) { it('debug #1', function (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.os.put({id: [2]}) yield * this.os.put({id: [2]})
yield* this.os.put({id: [0]}) yield * this.os.put({id: [0]})
yield* this.os.delete([2]) yield * this.os.delete([2])
yield* this.os.put({id: [1]}) yield * this.os.put({id: [1]})
expect(yield* this.os.find([0])).toBeTruthy() expect(yield * this.os.find([0])).toBeTruthy()
expect(yield* this.os.find([1])).toBeTruthy() expect(yield * this.os.find([1])).toBeTruthy()
expect(yield* this.os.find([2])).toBeFalsy() expect(yield * this.os.find([2])).toBeFalsy()
done() done()
}) })
}) })
it('can add&retrieve 5 elements', function (done) { it('can add&retrieve 5 elements', function (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.os.put({val: 'four', id: [4]}) yield * this.os.put({val: 'four', id: [4]})
yield* this.os.put({val: 'one', id: [1]}) yield * this.os.put({val: 'one', id: [1]})
yield* this.os.put({val: 'three', id: [3]}) yield * this.os.put({val: 'three', id: [3]})
yield* this.os.put({val: 'two', id: [2]}) yield * this.os.put({val: 'two', id: [2]})
yield* this.os.put({val: 'five', id: [5]}) yield * this.os.put({val: 'five', id: [5]})
expect((yield* this.os.find([1])).val).toEqual('one') expect((yield * this.os.find([1])).val).toEqual('one')
expect((yield* this.os.find([2])).val).toEqual('two') expect((yield * this.os.find([2])).val).toEqual('two')
expect((yield* this.os.find([3])).val).toEqual('three') expect((yield * this.os.find([3])).val).toEqual('three')
expect((yield* this.os.find([4])).val).toEqual('four') expect((yield * this.os.find([4])).val).toEqual('four')
expect((yield* this.os.find([5])).val).toEqual('five') expect((yield * this.os.find([5])).val).toEqual('five')
done() done()
}) })
}) })
it('5 elements do not exist anymore after deleting them', function (done) { it('5 elements do not exist anymore after deleting them', function (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.os.put({val: 'four', id: [4]}) yield * this.os.put({val: 'four', id: [4]})
yield* this.os.put({val: 'one', id: [1]}) yield * this.os.put({val: 'one', id: [1]})
yield* this.os.put({val: 'three', id: [3]}) yield * this.os.put({val: 'three', id: [3]})
yield* this.os.put({val: 'two', id: [2]}) yield * this.os.put({val: 'two', id: [2]})
yield* this.os.put({val: 'five', id: [5]}) yield * this.os.put({val: 'five', id: [5]})
yield* this.os.delete([4]) yield * this.os.delete([4])
expect(yield* this.os.find([4])).not.toBeTruthy() expect(yield * this.os.find([4])).not.toBeTruthy()
yield* this.os.delete([3]) yield * this.os.delete([3])
expect(yield* this.os.find([3])).not.toBeTruthy() expect(yield * this.os.find([3])).not.toBeTruthy()
yield* this.os.delete([2]) yield * this.os.delete([2])
expect(yield* this.os.find([2])).not.toBeTruthy() expect(yield * this.os.find([2])).not.toBeTruthy()
yield* this.os.delete([1]) yield * this.os.delete([1])
expect(yield* this.os.find([1])).not.toBeTruthy() expect(yield * this.os.find([1])).not.toBeTruthy()
yield* this.os.delete([5]) yield * this.os.delete([5])
expect(yield* this.os.find([5])).not.toBeTruthy() expect(yield * this.os.find([5])).not.toBeTruthy()
done() done()
}) })
}) })
@ -232,9 +232,9 @@ for (let database of databases) {
var r = Math.random() var r = Math.random()
if (r < 0.8) { if (r < 0.8) {
var obj = [Math.floor(Math.random() * numberOfOSTests * 10000)] var obj = [Math.floor(Math.random() * numberOfOSTests * 10000)]
if (!(yield* this.os.find(obj))) { if (!(yield * this.os.find(obj))) {
elements.push(obj) elements.push(obj)
yield* this.os.put({id: obj}) yield * this.os.put({id: obj})
} }
} else if (elements.length > 0) { } else if (elements.length > 0) {
var elemid = Math.floor(Math.random() * elements.length) var elemid = Math.floor(Math.random() * elements.length)
@ -242,7 +242,7 @@ for (let database of databases) {
elements = elements.filter(function (e) { elements = elements.filter(function (e) {
return !Y.utils.compareIds(e, elem) return !Y.utils.compareIds(e, elem)
}) })
yield* this.os.delete(elem) yield * this.os.delete(elem)
} }
} }
done() done()
@ -250,14 +250,14 @@ for (let database of databases) {
}) })
afterAll(function (done) { afterAll(function (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.store.destroy() yield * this.store.destroy()
done() done()
}) })
}) })
it('can find every object', function (done) { it('can find every object', function (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
for (var id of elements) { for (var id of elements) {
expect((yield* this.os.find(id)).id).toEqual(id) expect((yield * this.os.find(id)).id).toEqual(id)
} }
done() done()
}) })
@ -266,7 +266,7 @@ for (let database of databases) {
it('can find every object with lower bound search', function (done) { it('can find every object with lower bound search', function (done) {
store.requestTransaction(function * () { store.requestTransaction(function * () {
for (var id of elements) { for (var id of elements) {
var e = yield* this.os.findWithLowerBound(id) var e = yield * this.os.findWithLowerBound(id)
expect(e.id).toEqual(id) expect(e.id).toEqual(id)
} }
done() done()
@ -281,7 +281,7 @@ for (let database of databases) {
var actualResults = 0 var actualResults = 0
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.os.iterate(this, lowerBound, null, function * (val) { yield * this.os.iterate(this, lowerBound, null, function * (val) {
expect(val).toBeDefined() expect(val).toBeDefined()
actualResults++ actualResults++
}) })
@ -297,7 +297,7 @@ for (let database of databases) {
}).length }).length
var actualResults = 0 var actualResults = 0
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.os.iterate(this, lowerBound, null, function * (val) { yield * this.os.iterate(this, lowerBound, null, function * (val) {
expect(val).toBeDefined() expect(val).toBeDefined()
actualResults++ actualResults++
}) })
@ -314,7 +314,7 @@ for (let database of databases) {
var actualResults = 0 var actualResults = 0
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.os.iterate(this, null, upperBound, function * (val) { yield * this.os.iterate(this, null, upperBound, function * (val) {
expect(val).toBeDefined() expect(val).toBeDefined()
actualResults++ actualResults++
}) })
@ -340,7 +340,7 @@ for (let database of databases) {
}).length }).length
var actualResults = 0 var actualResults = 0
store.requestTransaction(function * () { store.requestTransaction(function * () {
yield* this.os.iterate(this, lowerBound, upperBound, function * (val) { yield * this.os.iterate(this, lowerBound, upperBound, function * (val) {
expect(val).toBeDefined() expect(val).toBeDefined()
actualResults++ actualResults++
}) })

View File

@ -46,7 +46,7 @@ g.setRandomSeed = function setRandomSeed (seed) {
g.generateRandomSeed() g.generateRandomSeed()
g.YConcurrency_TestingMode = true g.YConcurrencyTestingMode = true
jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000 jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000
@ -168,7 +168,7 @@ function fixAwaitingInType (type) {
type.os.requestTransaction(function * () { type.os.requestTransaction(function * () {
if (type.eventHandler.awaiting > 0 && type.eventHandler._debuggingAwaiting === true) { if (type.eventHandler.awaiting > 0 && type.eventHandler._debuggingAwaiting === true) {
type.eventHandler._debuggingAwaiting = false type.eventHandler._debuggingAwaiting = false
yield* type.eventHandler.awaitOps(this, function * () { /* mock function */ }) yield * type.eventHandler.awaitOps(this, function * () { /* mock function */ })
} }
wait(50).then(type.os.whenTransactionsFinished()).then(wait(50)).then(resolve) wait(50).then(type.os.whenTransactionsFinished()).then(wait(50)).then(resolve)
}) })
@ -178,13 +178,13 @@ function fixAwaitingInType (type) {
g.fixAwaitingInType = fixAwaitingInType g.fixAwaitingInType = fixAwaitingInType
g.applyRandomTransactionsNoGCNoDisconnect = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) { g.applyRandomTransactionsNoGCNoDisconnect = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
yield* applyTransactions(1, numberOfTransactions, objects, users, transactions, true) yield * applyTransactions(1, numberOfTransactions, objects, users, transactions, true)
yield Y.utils.globalRoom.flushAll() yield Y.utils.globalRoom.flushAll()
yield Promise.all(objects.map(fixAwaitingInType)) yield Promise.all(objects.map(fixAwaitingInType))
}) })
g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) { g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
yield* applyTransactions(1, numberOfTransactions, objects, users, transactions) yield * applyTransactions(1, numberOfTransactions, objects, users, transactions)
yield Promise.all(objects.map(fixAwaitingInType)) yield Promise.all(objects.map(fixAwaitingInType))
yield Y.utils.globalRoom.flushAll() yield Y.utils.globalRoom.flushAll()
yield Promise.all(objects.map(fixAwaitingInType)) yield Promise.all(objects.map(fixAwaitingInType))
@ -200,7 +200,7 @@ g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransaction
}) })
g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) { g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
yield* applyTransactions(1, numberOfTransactions, objects, users.slice(1), transactions) yield * applyTransactions(1, numberOfTransactions, objects, users.slice(1), transactions)
yield Y.utils.globalRoom.flushAll() yield Y.utils.globalRoom.flushAll()
yield Promise.all(objects.map(fixAwaitingInType)) yield Promise.all(objects.map(fixAwaitingInType))
for (var u in users) { for (var u in users) {
@ -242,18 +242,18 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
// t1 and t2 basically do the same. They define t[1,2], ds[1,2], and allDels[1,2] // t1 and t2 basically do the same. They define t[1,2], ds[1,2], and allDels[1,2]
function * t1 () { function * t1 () {
s1 = yield* this.getStateSet() s1 = yield * this.getStateSet()
ds1 = yield* this.getDeleteSet() ds1 = yield * this.getDeleteSet()
allDels1 = [] allDels1 = []
yield* this.ds.iterate(this, null, null, function * (d) { yield * this.ds.iterate(this, null, null, function * (d) {
allDels1.push(d) allDels1.push(d)
}) })
} }
function * t2 () { function * t2 () {
s2 = yield* this.getStateSet() s2 = yield * this.getStateSet()
ds2 = yield* this.getDeleteSet() ds2 = yield * this.getDeleteSet()
allDels2 = [] allDels2 = []
yield* this.ds.iterate(this, null, null, function * (d) { yield * this.ds.iterate(this, null, null, function * (d) {
allDels2.push(d) allDels2.push(d)
}) })
} }
@ -269,25 +269,25 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
for (var uid = 0; uid < users.length; uid++) { for (var uid = 0; uid < users.length; uid++) {
var u = users[uid] var u = users[uid]
u.db.requestTransaction(function * () { u.db.requestTransaction(function * () {
var sv = yield* this.getStateVector() var sv = yield * this.getStateVector()
for (var s of sv) { for (var s of sv) {
yield* this.updateState(s.user) yield * this.updateState(s.user)
} }
// compare deleted ops against deleteStore // compare deleted ops against deleteStore
yield* this.os.iterate(this, null, null, function * (o) { yield * this.os.iterate(this, null, null, function * (o) {
if (o.deleted === true) { if (o.deleted === true) {
expect(yield* this.isDeleted(o.id)).toBeTruthy() expect(yield * this.isDeleted(o.id)).toBeTruthy()
} }
}) })
// compare deleteStore against deleted ops // compare deleteStore against deleted ops
var ds = [] var ds = []
yield* this.ds.iterate(this, null, null, function * (d) { yield * this.ds.iterate(this, null, null, function * (d) {
ds.push(d) ds.push(d)
}) })
for (var j in ds) { for (var j in ds) {
var d = ds[j] var d = ds[j]
for (var i = 0; i < d.len; i++) { for (var i = 0; i < d.len; i++) {
var o = yield* this.getInsertion([d.id[0], d.id[1] + i]) var o = yield * this.getInsertion([d.id[0], d.id[1] + i])
// gc'd or deleted // gc'd or deleted
if (d.gc) { if (d.gc) {
expect(o).toBeFalsy() expect(o).toBeFalsy()
@ -300,8 +300,8 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
// compare allDels tree // compare allDels tree
if (s1 == null) { if (s1 == null) {
u.db.requestTransaction(function * () { u.db.requestTransaction(function * () {
yield* t1.call(this) yield * t1.call(this)
yield* this.os.iterate(this, null, null, function * (o) { yield * this.os.iterate(this, null, null, function * (o) {
o = Y.utils.copyObject(o) o = Y.utils.copyObject(o)
delete o.origin delete o.origin
delete o.originOf delete o.originOf
@ -310,9 +310,9 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
}) })
} else { } else {
u.db.requestTransaction(function * () { u.db.requestTransaction(function * () {
yield* t2.call(this) yield * t2.call(this)
var db2 = [] var db2 = []
yield* this.os.iterate(this, null, null, function * (o) { yield * this.os.iterate(this, null, null, function * (o) {
o = Y.utils.copyObject(o) o = Y.utils.copyObject(o)
delete o.origin delete o.origin
delete o.originOf delete o.originOf

View File

@ -19,7 +19,7 @@
* requiredOps * requiredOps
- Operations that are required to execute this operation. - Operations that are required to execute this operation.
*/ */
module.exports = function (Y/* :any */) { export default function extendStruct (Y) {
var Struct = { var Struct = {
/* This is the only operation that is actually not a structure, because /* This is the only operation that is actually not a structure, because
it is not stored in the OS. This is why it _does not_ have an id it is not stored in the OS. This is why it _does not_ have an id
@ -40,7 +40,7 @@ module.exports = function (Y/* :any */) {
return [] // [op.target] return [] // [op.target]
}, },
execute: function * (op) { execute: function * (op) {
return yield* this.deleteOperation(op.target, op.length || 1) return yield * this.deleteOperation(op.target, op.length || 1)
} }
}, },
Insert: { Insert: {
@ -101,13 +101,13 @@ module.exports = function (Y/* :any */) {
return 0 return 0
} else { } else {
var d = 0 var d = 0
var o = yield* this.getInsertion(op.left) var o = yield * this.getInsertion(op.left)
while (!Y.utils.matchesId(o, op.origin)) { while (!Y.utils.matchesId(o, op.origin)) {
d++ d++
if (o.left == null) { if (o.left == null) {
break break
} else { } else {
o = yield* this.getInsertion(o.left) o = yield * this.getInsertion(o.left)
} }
} }
return d return d
@ -138,17 +138,17 @@ module.exports = function (Y/* :any */) {
if (op.origin != null) { // TODO: !== instead of != if (op.origin != null) { // TODO: !== instead of !=
// we save in origin that op originates in it // we save in origin that op originates in it
// we need that later when we eventually garbage collect origin (see transaction) // we need that later when we eventually garbage collect origin (see transaction)
var origin = yield* this.getInsertionCleanEnd(op.origin) var origin = yield * this.getInsertionCleanEnd(op.origin)
if (origin.originOf == null) { if (origin.originOf == null) {
origin.originOf = [] origin.originOf = []
} }
origin.originOf.push(op.id) origin.originOf.push(op.id)
yield* this.setOperation(origin) yield * this.setOperation(origin)
if (origin.right != null) { if (origin.right != null) {
tryToRemergeLater.push(origin.right) tryToRemergeLater.push(origin.right)
} }
} }
var distanceToOrigin = i = yield* Struct.Insert.getDistanceToOrigin.call(this, op) // most cases: 0 (starts from 0) var distanceToOrigin = i = yield * Struct.Insert.getDistanceToOrigin.call(this, op) // most cases: 0 (starts from 0)
// now we begin to insert op in the list of insertions.. // now we begin to insert op in the list of insertions..
var o var o
@ -157,29 +157,29 @@ module.exports = function (Y/* :any */) {
// find o. o is the first conflicting operation // find o. o is the first conflicting operation
if (op.left != null) { if (op.left != null) {
o = yield* this.getInsertionCleanEnd(op.left) o = yield * this.getInsertionCleanEnd(op.left)
if (!Y.utils.compareIds(op.left, op.origin) && o.right != null) { if (!Y.utils.compareIds(op.left, op.origin) && o.right != null) {
// only if not added previously // only if not added previously
tryToRemergeLater.push(o.right) tryToRemergeLater.push(o.right)
} }
o = (o.right == null) ? null : yield* this.getOperation(o.right) o = (o.right == null) ? null : yield * this.getOperation(o.right)
} else { // left == null } else { // left == null
parent = yield* this.getOperation(op.parent) parent = yield * this.getOperation(op.parent)
let startId = op.parentSub ? parent.map[op.parentSub] : parent.start let startId = op.parentSub ? parent.map[op.parentSub] : parent.start
start = startId == null ? null : yield* this.getOperation(startId) start = startId == null ? null : yield * this.getOperation(startId)
o = start o = start
} }
// make sure to split op.right if necessary (also add to tryCombineWithLeft) // make sure to split op.right if necessary (also add to tryCombineWithLeft)
if (op.right != null) { if (op.right != null) {
tryToRemergeLater.push(op.right) tryToRemergeLater.push(op.right)
yield* this.getInsertionCleanStart(op.right) yield * this.getInsertionCleanStart(op.right)
} }
// handle conflicts // handle conflicts
while (true) { while (true) {
if (o != null && !Y.utils.compareIds(o.id, op.right)) { if (o != null && !Y.utils.compareIds(o.id, op.right)) {
var oOriginDistance = yield* Struct.Insert.getDistanceToOrigin.call(this, o) var oOriginDistance = yield * Struct.Insert.getDistanceToOrigin.call(this, o)
if (oOriginDistance === i) { if (oOriginDistance === i) {
// case 1 // case 1
if (o.id[0] < op.id[0]) { if (o.id[0] < op.id[0]) {
@ -197,7 +197,7 @@ module.exports = function (Y/* :any */) {
} }
i++ i++
if (o.right != null) { if (o.right != null) {
o = yield* this.getInsertion(o.right) o = yield * this.getInsertion(o.right)
} else { } else {
o = null o = null
} }
@ -210,17 +210,17 @@ module.exports = function (Y/* :any */) {
var left = null var left = null
var right = null var right = null
if (parent == null) { if (parent == null) {
parent = yield* this.getOperation(op.parent) parent = yield * this.getOperation(op.parent)
} }
// reconnect left and set right of op // reconnect left and set right of op
if (op.left != null) { if (op.left != null) {
left = yield* this.getInsertion(op.left) left = yield * this.getInsertion(op.left)
// link left // link left
op.right = left.right op.right = left.right
left.right = op.id left.right = op.id
yield* this.setOperation(left) yield * this.setOperation(left)
} else { } else {
// set op.right from parent, if necessary // set op.right from parent, if necessary
op.right = op.parentSub ? parent.map[op.parentSub] || null : parent.start op.right = op.parentSub ? parent.map[op.parentSub] || null : parent.start
@ -228,33 +228,33 @@ module.exports = function (Y/* :any */) {
// reconnect right // reconnect right
if (op.right != null) { if (op.right != null) {
// TODO: wanna connect right too? // TODO: wanna connect right too?
right = yield* this.getOperation(op.right) right = yield * this.getOperation(op.right)
right.left = Y.utils.getLastId(op) right.left = Y.utils.getLastId(op)
// if right exists, and it is supposed to be gc'd. Remove it from the gc // if right exists, and it is supposed to be gc'd. Remove it from the gc
if (right.gc != null) { if (right.gc != null) {
if (right.content != null && right.content.length > 1) { if (right.content != null && right.content.length > 1) {
right = yield* this.getInsertionCleanEnd(right.id) right = yield * this.getInsertionCleanEnd(right.id)
} }
this.store.removeFromGarbageCollector(right) this.store.removeFromGarbageCollector(right)
} }
yield* this.setOperation(right) yield * this.setOperation(right)
} }
// update parents .map/start/end properties // update parents .map/start/end properties
if (op.parentSub != null) { if (op.parentSub != null) {
if (left == null) { if (left == null) {
parent.map[op.parentSub] = op.id parent.map[op.parentSub] = op.id
yield* this.setOperation(parent) yield * this.setOperation(parent)
} }
// is a child of a map struct. // is a child of a map struct.
// Then also make sure that only the most left element is not deleted // Then also make sure that only the most left element is not deleted
// We do not call the type in this case (this is what the third parameter is for) // We do not call the type in this case (this is what the third parameter is for)
if (op.right != null) { if (op.right != null) {
yield* this.deleteOperation(op.right, 1, true) yield * this.deleteOperation(op.right, 1, true)
} }
if (op.left != null) { if (op.left != null) {
yield* this.deleteOperation(op.id, 1, true) yield * this.deleteOperation(op.id, 1, true)
} }
} else { } else {
if (right == null || left == null) { if (right == null || left == null) {
@ -264,14 +264,14 @@ module.exports = function (Y/* :any */) {
if (left == null) { if (left == null) {
parent.start = op.id parent.start = op.id
} }
yield* this.setOperation(parent) yield * this.setOperation(parent)
} }
} }
// try to merge original op.left and op.origin // try to merge original op.left and op.origin
for (i = 0; i < tryToRemergeLater.length; i++) { for (i = 0; i < tryToRemergeLater.length; i++) {
var m = yield* this.getOperation(tryToRemergeLater[i]) var m = yield * this.getOperation(tryToRemergeLater[i])
yield* this.tryCombineWithLeft(m) yield * this.tryCombineWithLeft(m)
} }
} }
}, },
@ -329,7 +329,7 @@ module.exports = function (Y/* :any */) {
return null return null
} }
var res = null var res = null
var o = yield* this.getOperation(op.start) var o = yield * this.getOperation(op.start)
while (true) { while (true) {
if (!o.deleted) { if (!o.deleted) {
@ -337,7 +337,7 @@ module.exports = function (Y/* :any */) {
pos-- pos--
} }
if (pos >= 0 && o.right != null) { if (pos >= 0 && o.right != null) {
o = yield* this.getOperation(o.right) o = yield * this.getOperation(o.right)
} else { } else {
break break
} }
@ -348,7 +348,7 @@ module.exports = function (Y/* :any */) {
o = o.start o = o.start
var res = [] var res = []
while (o != null) { // TODO: change to != (at least some convention) while (o != null) { // TODO: change to != (at least some convention)
var operation = yield* this.getOperation(o) var operation = yield * this.getOperation(o)
if (!operation.deleted) { if (!operation.deleted) {
res.push(f(operation)) res.push(f(operation))
} }
@ -398,13 +398,13 @@ module.exports = function (Y/* :any */) {
get: function * (op, name) { get: function * (op, name) {
var oid = op.map[name] var oid = op.map[name]
if (oid != null) { if (oid != null) {
var res = yield* this.getOperation(oid) var res = yield * this.getOperation(oid)
if (res == null || res.deleted) { if (res == null || res.deleted) {
return void 0 return void 0
} else if (res.opContent == null) { } else if (res.opContent == null) {
return res.content[0] return res.content[0]
} else { } else {
return yield* this.getType(res.opContent) return yield * this.getType(res.opContent)
} }
} }
} }

View File

@ -74,7 +74,7 @@
- this is called only by `getOperations(startSS)`. It makes an operation - this is called only by `getOperations(startSS)`. It makes an operation
applyable on a given SS. applyable on a given SS.
*/ */
module.exports = function (Y/* :any */) { export default function extendTransaction (Y) {
class TransactionInterface { class TransactionInterface {
/* :: /* ::
store: Y.AbstractDatabase; store: Y.AbstractDatabase;
@ -91,7 +91,7 @@ module.exports = function (Y/* :any */) {
var send = [] var send = []
for (var i = 0; i < ops.length; i++) { for (var i = 0; i < ops.length; i++) {
var op = ops[i] var op = ops[i]
yield* this.store.tryExecute.call(this, op) yield * this.store.tryExecute.call(this, op)
if (op.id == null || typeof op.id[1] !== 'string') { if (op.id == null || typeof op.id[1] !== 'string') {
send.push(Y.Struct[op.struct].encode(op)) send.push(Y.Struct[op.struct].encode(op))
} }
@ -104,15 +104,15 @@ module.exports = function (Y/* :any */) {
* deleteList (start) { * deleteList (start) {
while (start != null) { while (start != null) {
start = yield* this.getOperation(start) start = yield * this.getOperation(start)
if (!start.gc) { if (!start.gc) {
start.gc = true start.gc = true
start.deleted = true start.deleted = true
yield* this.setOperation(start) yield * this.setOperation(start)
var delLength = start.content != null ? start.content.length : 1 var delLength = start.content != null ? start.content.length : 1
yield* this.markDeleted(start.id, delLength) yield * this.markDeleted(start.id, delLength)
if (start.opContent != null) { if (start.opContent != null) {
yield* this.deleteOperation(start.opContent) yield * this.deleteOperation(start.opContent)
} }
this.store.queueGarbageCollector(start.id) this.store.queueGarbageCollector(start.id)
} }
@ -127,10 +127,10 @@ module.exports = function (Y/* :any */) {
if (length == null) { if (length == null) {
length = 1 length = 1
} }
yield* this.markDeleted(targetId, length) yield * this.markDeleted(targetId, length)
while (length > 0) { while (length > 0) {
var callType = false var callType = false
var target = yield* this.os.findWithUpperBound([targetId[0], targetId[1] + length - 1]) var target = yield * this.os.findWithUpperBound([targetId[0], targetId[1] + length - 1])
var targetLength = target != null && target.content != null ? target.content.length : 1 var targetLength = target != null && target.content != null ? target.content.length : 1
if (target == null || target.id[0] !== targetId[0] || target.id[1] + targetLength <= targetId[1]) { if (target == null || target.id[0] !== targetId[0] || target.id[1] + targetLength <= targetId[1]) {
// does not exist or is not in the range of the deletion // does not exist or is not in the range of the deletion
@ -141,12 +141,12 @@ module.exports = function (Y/* :any */) {
if (!target.deleted) { if (!target.deleted) {
if (target.id[1] < targetId[1]) { if (target.id[1] < targetId[1]) {
// starts to the left of the deletion range // starts to the left of the deletion range
target = yield* this.getInsertionCleanStart(targetId) target = yield * this.getInsertionCleanStart(targetId)
targetLength = target.content.length // must have content property! targetLength = target.content.length // must have content property!
} }
if (target.id[1] + targetLength > targetId[1] + length) { if (target.id[1] + targetLength > targetId[1] + length) {
// ends to the right of the deletion range // ends to the right of the deletion range
target = yield* this.getInsertionCleanEnd([targetId[0], targetId[1] + length - 1]) target = yield * this.getInsertionCleanEnd([targetId[0], targetId[1] + length - 1])
targetLength = target.content.length targetLength = target.content.length
} }
} }
@ -161,35 +161,35 @@ module.exports = function (Y/* :any */) {
// delete containing lists // delete containing lists
if (target.start != null) { if (target.start != null) {
// TODO: don't do it like this .. -.- // TODO: don't do it like this .. -.-
yield* this.deleteList(target.start) yield * this.deleteList(target.start)
// yield* this.deleteList(target.id) -- do not gc itself because this may still get referenced // yield* this.deleteList(target.id) -- do not gc itself because this may still get referenced
} }
if (target.map != null) { if (target.map != null) {
for (var name in target.map) { for (var name in target.map) {
yield* this.deleteList(target.map[name]) yield * this.deleteList(target.map[name])
} }
// TODO: here to.. (see above) // TODO: here to.. (see above)
// yield* this.deleteList(target.id) -- see above // yield* this.deleteList(target.id) -- see above
} }
if (target.opContent != null) { if (target.opContent != null) {
yield* this.deleteOperation(target.opContent) yield * this.deleteOperation(target.opContent)
// target.opContent = null // target.opContent = null
} }
if (target.requires != null) { if (target.requires != null) {
for (var i = 0; i < target.requires.length; i++) { for (var i = 0; i < target.requires.length; i++) {
yield* this.deleteOperation(target.requires[i]) yield * this.deleteOperation(target.requires[i])
} }
} }
} }
var left var left
if (target.left != null) { if (target.left != null) {
left = yield* this.getInsertion(target.left) left = yield * this.getInsertion(target.left)
} else { } else {
left = null left = null
} }
// set here because it was deleted and/or gc'd // set here because it was deleted and/or gc'd
yield* this.setOperation(target) yield * this.setOperation(target)
/* /*
Check if it is possible to add right to the gc. Check if it is possible to add right to the gc.
@ -198,12 +198,12 @@ module.exports = function (Y/* :any */) {
*/ */
var right var right
if (target.right != null) { if (target.right != null) {
right = yield* this.getOperation(target.right) right = yield * this.getOperation(target.right)
} else { } else {
right = null right = null
} }
if (callType && !preventCallType) { if (callType && !preventCallType) {
yield* this.store.operationAdded(this, { yield * this.store.operationAdded(this, {
struct: 'Delete', struct: 'Delete',
target: target.id, target: target.id,
length: targetLength, length: targetLength,
@ -211,9 +211,9 @@ module.exports = function (Y/* :any */) {
}) })
} }
// need to gc in the end! // need to gc in the end!
yield* this.store.addToGarbageCollector.call(this, target, left) yield * this.store.addToGarbageCollector.call(this, target, left)
if (right != null) { if (right != null) {
yield* this.store.addToGarbageCollector.call(this, right, target) yield * this.store.addToGarbageCollector.call(this, right, target)
} }
} }
} }
@ -224,22 +224,22 @@ module.exports = function (Y/* :any */) {
* markGarbageCollected (id, len) { * markGarbageCollected (id, len) {
// this.mem.push(["gc", id]); // this.mem.push(["gc", id]);
this.store.addToDebug('yield* this.markGarbageCollected(', id, ', ', len, ')') this.store.addToDebug('yield* this.markGarbageCollected(', id, ', ', len, ')')
var n = yield* this.markDeleted(id, len) var n = yield * this.markDeleted(id, len)
if (n.id[1] < id[1] && !n.gc) { if (n.id[1] < id[1] && !n.gc) {
// un-extend left // un-extend left
var newlen = n.len - (id[1] - n.id[1]) var newlen = n.len - (id[1] - n.id[1])
n.len -= newlen n.len -= newlen
yield* this.ds.put(n) yield * this.ds.put(n)
n = {id: id, len: newlen, gc: false} n = {id: id, len: newlen, gc: false}
yield* this.ds.put(n) yield * this.ds.put(n)
} }
// get prev&next before adding a new operation // get prev&next before adding a new operation
var prev = yield* this.ds.findPrev(id) var prev = yield * this.ds.findPrev(id)
var next = yield* this.ds.findNext(id) var next = yield * this.ds.findNext(id)
if (id[1] + len < n.id[1] + n.len && !n.gc) { if (id[1] + len < n.id[1] + n.len && !n.gc) {
// un-extend right // un-extend right
yield* this.ds.put({id: [id[0], id[1] + len], len: n.len - len, gc: false}) yield * this.ds.put({id: [id[0], id[1] + len], len: n.len - len, gc: false})
n.len = len n.len = len
} }
// set gc'd // set gc'd
@ -251,7 +251,7 @@ module.exports = function (Y/* :any */) {
Y.utils.compareIds([prev.id[0], prev.id[1] + prev.len], n.id) Y.utils.compareIds([prev.id[0], prev.id[1] + prev.len], n.id)
) { ) {
prev.len += n.len prev.len += n.len
yield* this.ds.delete(n.id) yield * this.ds.delete(n.id)
n = prev n = prev
// ds.put n here? // ds.put n here?
} }
@ -262,10 +262,10 @@ module.exports = function (Y/* :any */) {
Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id) Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id)
) { ) {
n.len += next.len n.len += next.len
yield* this.ds.delete(next.id) yield * this.ds.delete(next.id)
} }
yield* this.ds.put(n) yield * this.ds.put(n)
yield* this.updateState(n.id[0]) yield * this.updateState(n.id[0])
} }
/* /*
Mark an operation as deleted. Mark an operation as deleted.
@ -277,7 +277,7 @@ module.exports = function (Y/* :any */) {
length = 1 length = 1
} }
// this.mem.push(["del", id]); // this.mem.push(["del", id]);
var n = yield* this.ds.findWithUpperBound(id) var n = yield * this.ds.findWithUpperBound(id)
if (n != null && n.id[0] === id[0]) { if (n != null && n.id[0] === id[0]) {
if (n.id[1] <= id[1] && id[1] <= n.id[1] + n.len) { if (n.id[1] <= id[1] && id[1] <= n.id[1] + n.len) {
// id is in n's range // id is in n's range
@ -291,7 +291,7 @@ module.exports = function (Y/* :any */) {
if (diff < length) { if (diff < length) {
// a partial deletion // a partial deletion
n = {id: [id[0], id[1] + diff], len: length - diff, gc: false} n = {id: [id[0], id[1] + diff], len: length - diff, gc: false}
yield* this.ds.put(n) yield * this.ds.put(n)
} else { } else {
// already gc'd // already gc'd
throw new Error('Cannot happen! (it dit though.. :()') throw new Error('Cannot happen! (it dit though.. :()')
@ -305,15 +305,15 @@ module.exports = function (Y/* :any */) {
} else { } else {
// cannot extend left (there is no left!) // cannot extend left (there is no left!)
n = {id: id, len: length, gc: false} n = {id: id, len: length, gc: false}
yield* this.ds.put(n) // TODO: you double-put !! yield * this.ds.put(n) // TODO: you double-put !!
} }
} else { } else {
// cannot extend left // cannot extend left
n = {id: id, len: length, gc: false} n = {id: id, len: length, gc: false}
yield* this.ds.put(n) yield * this.ds.put(n)
} }
// can extend right? // can extend right?
var next = yield* this.ds.findNext(n.id) var next = yield * this.ds.findNext(n.id)
if ( if (
next != null && next != null &&
n.id[0] === next.id[0] && n.id[0] === next.id[0] &&
@ -329,8 +329,8 @@ module.exports = function (Y/* :any */) {
// delete the missing range after next // delete the missing range after next
diff = diff - next.len // missing range after next diff = diff - next.len // missing range after next
if (diff > 0) { if (diff > 0) {
yield* this.ds.put(n) // unneccessary? TODO! yield * this.ds.put(n) // unneccessary? TODO!
yield* this.markDeleted([next.id[0], next.id[1] + next.len], diff) yield * this.markDeleted([next.id[0], next.id[1] + next.len], diff)
} }
} }
break break
@ -339,8 +339,8 @@ module.exports = function (Y/* :any */) {
if (diff > next.len) { if (diff > next.len) {
// n is even longer than next // n is even longer than next
// get next.next, and try to extend it // get next.next, and try to extend it
var _next = yield* this.ds.findNext(next.id) var _next = yield * this.ds.findNext(next.id)
yield* this.ds.delete(next.id) yield * this.ds.delete(next.id)
if (_next == null || n.id[0] !== _next.id[0]) { if (_next == null || n.id[0] !== _next.id[0]) {
break break
} else { } else {
@ -351,13 +351,13 @@ module.exports = function (Y/* :any */) {
} else { } else {
// n just partially overlaps with next. extend n, delete next, and break this loop // n just partially overlaps with next. extend n, delete next, and break this loop
n.len += next.len - diff n.len += next.len - diff
yield* this.ds.delete(next.id) yield * this.ds.delete(next.id)
break break
} }
} }
} }
} }
yield* this.ds.put(n) yield * this.ds.put(n)
return n return n
} }
/* /*
@ -372,28 +372,28 @@ module.exports = function (Y/* :any */) {
if (!this.store.gc) { if (!this.store.gc) {
return return
} }
yield* this.os.iterate(this, null, null, function * (op) { yield * this.os.iterate(this, null, null, function * (op) {
if (op.gc) { if (op.gc) {
delete op.gc delete op.gc
yield* this.setOperation(op) yield * this.setOperation(op)
} }
if (op.parent != null) { if (op.parent != null) {
var parentDeleted = yield* this.isDeleted(op.parent) var parentDeleted = yield * this.isDeleted(op.parent)
if (parentDeleted) { if (parentDeleted) {
op.gc = true op.gc = true
if (!op.deleted) { if (!op.deleted) {
yield* this.markDeleted(op.id, op.content != null ? op.content.length : 1) yield * this.markDeleted(op.id, op.content != null ? op.content.length : 1)
op.deleted = true op.deleted = true
if (op.opContent != null) { if (op.opContent != null) {
yield* this.deleteOperation(op.opContent) yield * this.deleteOperation(op.opContent)
} }
if (op.requires != null) { if (op.requires != null) {
for (var i = 0; i < op.requires.length; i++) { for (var i = 0; i < op.requires.length; i++) {
yield* this.deleteOperation(op.requires[i]) yield * this.deleteOperation(op.requires[i])
} }
} }
} }
yield* this.setOperation(op) yield * this.setOperation(op)
this.store.gc1.push(op.id) // this is ok becaues its shortly before sync (otherwise use queueGarbageCollector!) this.store.gc1.push(op.id) // this is ok becaues its shortly before sync (otherwise use queueGarbageCollector!)
return return
} }
@ -401,9 +401,9 @@ module.exports = function (Y/* :any */) {
if (op.deleted) { if (op.deleted) {
var left = null var left = null
if (op.left != null) { if (op.left != null) {
left = yield* this.getInsertion(op.left) left = yield * this.getInsertion(op.left)
} }
yield* this.store.addToGarbageCollector.call(this, op, left) yield * this.store.addToGarbageCollector.call(this, op, left)
} }
}) })
} }
@ -418,8 +418,8 @@ module.exports = function (Y/* :any */) {
*/ */
* garbageCollectOperation (id) { * garbageCollectOperation (id) {
this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')') this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')')
var o = yield* this.getOperation(id) var o = yield * this.getOperation(id)
yield* this.markGarbageCollected(id, (o != null && o.content != null) ? o.content.length : 1) // always mark gc'd yield * this.markGarbageCollected(id, (o != null && o.content != null) ? o.content.length : 1) // always mark gc'd
// if op exists, then clean that mess up.. // if op exists, then clean that mess up..
if (o != null) { if (o != null) {
var deps = [] var deps = []
@ -430,32 +430,32 @@ module.exports = function (Y/* :any */) {
deps = deps.concat(o.requires) deps = deps.concat(o.requires)
} }
for (var i = 0; i < deps.length; i++) { for (var i = 0; i < deps.length; i++) {
var dep = yield* this.getOperation(deps[i]) var dep = yield * this.getOperation(deps[i])
if (dep != null) { if (dep != null) {
if (!dep.deleted) { if (!dep.deleted) {
yield* this.deleteOperation(dep.id) yield * this.deleteOperation(dep.id)
dep = yield* this.getOperation(dep.id) dep = yield * this.getOperation(dep.id)
} }
dep.gc = true dep.gc = true
yield* this.setOperation(dep) yield * this.setOperation(dep)
this.store.queueGarbageCollector(dep.id) this.store.queueGarbageCollector(dep.id)
} else { } else {
yield* this.markGarbageCollected(deps[i], 1) yield * this.markGarbageCollected(deps[i], 1)
} }
} }
// remove gc'd op from the left op, if it exists // remove gc'd op from the left op, if it exists
if (o.left != null) { if (o.left != null) {
var left = yield* this.getInsertion(o.left) var left = yield * this.getInsertion(o.left)
left.right = o.right left.right = o.right
yield* this.setOperation(left) yield * this.setOperation(left)
} }
// remove gc'd op from the right op, if it exists // remove gc'd op from the right op, if it exists
// also reset origins of right ops // also reset origins of right ops
if (o.right != null) { if (o.right != null) {
var right = yield* this.getOperation(o.right) var right = yield * this.getOperation(o.right)
right.left = o.left right.left = o.left
yield* this.setOperation(right) yield * this.setOperation(right)
if (o.originOf != null && o.originOf.length > 0) { if (o.originOf != null && o.originOf.length > 0) {
// find new origin of right ops // find new origin of right ops
@ -463,7 +463,7 @@ module.exports = function (Y/* :any */) {
var neworigin = o.left var neworigin = o.left
var neworigin_ = null var neworigin_ = null
while (neworigin != null) { while (neworigin != null) {
neworigin_ = yield* this.getInsertion(neworigin) neworigin_ = yield * this.getInsertion(neworigin)
if (neworigin_.deleted) { if (neworigin_.deleted) {
break break
} }
@ -506,10 +506,10 @@ module.exports = function (Y/* :any */) {
// ** Now the new implementation starts ** // ** Now the new implementation starts **
// reset neworigin of all originOf[*] // reset neworigin of all originOf[*]
for (var _i in o.originOf) { for (var _i in o.originOf) {
var originsIn = yield* this.getOperation(o.originOf[_i]) var originsIn = yield * this.getOperation(o.originOf[_i])
if (originsIn != null) { if (originsIn != null) {
originsIn.origin = neworigin originsIn.origin = neworigin
yield* this.setOperation(originsIn) yield * this.setOperation(originsIn)
} }
} }
if (neworigin != null) { if (neworigin != null) {
@ -518,7 +518,7 @@ module.exports = function (Y/* :any */) {
} else { } else {
neworigin_.originOf = o.originOf.concat(neworigin_.originOf) neworigin_.originOf = o.originOf.concat(neworigin_.originOf)
} }
yield* this.setOperation(neworigin_) yield * this.setOperation(neworigin_)
} }
// we don't need to set right here, because // we don't need to set right here, because
// right should be in o.originOf => it is set it the previous for loop // right should be in o.originOf => it is set it the previous for loop
@ -527,15 +527,15 @@ module.exports = function (Y/* :any */) {
// o may originate in another operation. // o may originate in another operation.
// Since o is deleted, we have to reset o.origin's `originOf` property // Since o is deleted, we have to reset o.origin's `originOf` property
if (o.origin != null) { if (o.origin != null) {
var origin = yield* this.getInsertion(o.origin) var origin = yield * this.getInsertion(o.origin)
origin.originOf = origin.originOf.filter(function (_id) { origin.originOf = origin.originOf.filter(function (_id) {
return !Y.utils.compareIds(id, _id) return !Y.utils.compareIds(id, _id)
}) })
yield* this.setOperation(origin) yield * this.setOperation(origin)
} }
var parent var parent
if (o.parent != null) { if (o.parent != null) {
parent = yield* this.getOperation(o.parent) parent = yield * this.getOperation(o.parent)
} }
// remove gc'd op from parent, if it exists // remove gc'd op from parent, if it exists
if (parent != null) { if (parent != null) {
@ -562,32 +562,32 @@ module.exports = function (Y/* :any */) {
} }
} }
if (setParent) { if (setParent) {
yield* this.setOperation(parent) yield * this.setOperation(parent)
} }
} }
// finally remove it from the os // finally remove it from the os
yield* this.removeOperation(o.id) yield * this.removeOperation(o.id)
} }
} }
* checkDeleteStoreForState (state) { * checkDeleteStoreForState (state) {
var n = yield* this.ds.findWithUpperBound([state.user, state.clock]) var n = yield * this.ds.findWithUpperBound([state.user, state.clock])
if (n != null && n.id[0] === state.user && n.gc) { if (n != null && n.id[0] === state.user && n.gc) {
state.clock = Math.max(state.clock, n.id[1] + n.len) state.clock = Math.max(state.clock, n.id[1] + n.len)
} }
} }
* updateState (user) { * updateState (user) {
var state = yield* this.getState(user) var state = yield * this.getState(user)
yield* this.checkDeleteStoreForState(state) yield * this.checkDeleteStoreForState(state)
var o = yield* this.getInsertion([user, state.clock]) var o = yield * this.getInsertion([user, state.clock])
var oLength = (o != null && o.content != null) ? o.content.length : 1 var oLength = (o != null && o.content != null) ? o.content.length : 1
while (o != null && user === o.id[0] && o.id[1] <= state.clock && o.id[1] + oLength > state.clock) { while (o != null && user === o.id[0] && o.id[1] <= state.clock && o.id[1] + oLength > state.clock) {
// either its a new operation (1. case), or it is an operation that was deleted, but is not yet in the OS // either its a new operation (1. case), or it is an operation that was deleted, but is not yet in the OS
state.clock += oLength state.clock += oLength
yield* this.checkDeleteStoreForState(state) yield * this.checkDeleteStoreForState(state)
o = yield* this.os.findNext(o.id) o = yield * this.os.findNext(o.id)
oLength = (o != null && o.content != null) ? o.content.length : 1 oLength = (o != null && o.content != null) ? o.content.length : 1
} }
yield* this.setState(state) yield * this.setState(state)
} }
/* /*
apply a delete set in order to get apply a delete set in order to get
@ -600,7 +600,7 @@ module.exports = function (Y/* :any */) {
var dv = ds[user] var dv = ds[user]
var pos = 0 var pos = 0
var d = dv[pos] var d = dv[pos]
yield* this.ds.iterate(this, [user, 0], [user, Number.MAX_VALUE], function * (n) { yield * this.ds.iterate(this, [user, 0], [user, Number.MAX_VALUE], function * (n) {
// cases: // cases:
// 1. d deletes something to the right of n // 1. d deletes something to the right of n
// => go to next n (break) // => go to next n (break)
@ -649,14 +649,14 @@ module.exports = function (Y/* :any */) {
for (var i = 0; i < deletions.length; i++) { for (var i = 0; i < deletions.length; i++) {
var del = deletions[i] var del = deletions[i]
// always try to delete.. // always try to delete..
yield* this.deleteOperation([del[0], del[1]], del[2]) yield * this.deleteOperation([del[0], del[1]], del[2])
if (del[3]) { if (del[3]) {
// gc.. // gc..
yield* this.markGarbageCollected([del[0], del[1]], del[2]) // always mark gc'd yield * this.markGarbageCollected([del[0], del[1]], del[2]) // always mark gc'd
// remove operation.. // remove operation..
var counter = del[1] + del[2] var counter = del[1] + del[2]
while (counter >= del[1]) { while (counter >= del[1]) {
var o = yield* this.os.findWithUpperBound([del[0], counter - 1]) var o = yield * this.os.findWithUpperBound([del[0], counter - 1])
if (o == null) { if (o == null) {
break break
} }
@ -667,14 +667,14 @@ module.exports = function (Y/* :any */) {
} }
if (o.id[1] + oLen > del[1] + del[2]) { if (o.id[1] + oLen > del[1] + del[2]) {
// overlaps right // overlaps right
o = yield* this.getInsertionCleanEnd([del[0], del[1] + del[2] - 1]) o = yield * this.getInsertionCleanEnd([del[0], del[1] + del[2] - 1])
} }
if (o.id[1] < del[1]) { if (o.id[1] < del[1]) {
// overlaps left // overlaps left
o = yield* this.getInsertionCleanStart([del[0], del[1]]) o = yield * this.getInsertionCleanStart([del[0], del[1]])
} }
counter = o.id[1] counter = o.id[1]
yield* this.garbageCollectOperation(o.id) yield * this.garbageCollectOperation(o.id)
} }
} }
if (this.store.forwardAppliedOperations) { if (this.store.forwardAppliedOperations) {
@ -685,7 +685,7 @@ module.exports = function (Y/* :any */) {
} }
} }
* isGarbageCollected (id) { * isGarbageCollected (id) {
var n = yield* this.ds.findWithUpperBound(id) var n = yield * this.ds.findWithUpperBound(id)
return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len && n.gc return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len && n.gc
} }
/* /*
@ -693,7 +693,7 @@ module.exports = function (Y/* :any */) {
*/ */
* getDeleteSet () { * getDeleteSet () {
var ds = {} var ds = {}
yield* this.ds.iterate(this, null, null, function * (n) { yield * this.ds.iterate(this, null, null, function * (n) {
var user = n.id[0] var user = n.id[0]
var counter = n.id[1] var counter = n.id[1]
var len = n.len var len = n.len
@ -708,15 +708,15 @@ module.exports = function (Y/* :any */) {
return ds return ds
} }
* isDeleted (id) { * isDeleted (id) {
var n = yield* this.ds.findWithUpperBound(id) var n = yield * this.ds.findWithUpperBound(id)
return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len
} }
* setOperation (op) { * setOperation (op) {
yield* this.os.put(op) yield * this.os.put(op)
return op return op
} }
* addOperation (op) { * addOperation (op) {
yield* this.os.put(op) yield * this.os.put(op)
if (this.store.y.connector.isSynced && this.store.forwardAppliedOperations && typeof op.id[1] !== 'string') { if (this.store.y.connector.isSynced && this.store.forwardAppliedOperations && typeof op.id[1] !== 'string') {
// is connected, and this is not going to be send in addOperation // is connected, and this is not going to be send in addOperation
this.store.y.connector.broadcastOps([op]) this.store.y.connector.broadcastOps([op])
@ -731,7 +731,7 @@ module.exports = function (Y/* :any */) {
op.left[0] === op.id[0] && op.left[0] === op.id[0] &&
Y.utils.compareIds(op.left, op.origin) Y.utils.compareIds(op.left, op.origin)
) { ) {
var left = yield* this.getInsertion(op.left) var left = yield * this.getInsertion(op.left)
if (left.content != null && if (left.content != null &&
left.id[1] + left.content.length === op.id[1] && left.id[1] + left.content.length === op.id[1] &&
left.originOf.length === 1 && left.originOf.length === 1 &&
@ -746,13 +746,13 @@ module.exports = function (Y/* :any */) {
} }
left.content = left.content.concat(op.content) left.content = left.content.concat(op.content)
left.right = op.right left.right = op.right
yield* this.os.delete(op.id) yield * this.os.delete(op.id)
yield* this.setOperation(left) yield * this.setOperation(left)
} }
} }
} }
* getInsertion (id) { * getInsertion (id) {
var ins = yield* this.os.findWithUpperBound(id) var ins = yield * this.os.findWithUpperBound(id)
if (ins == null) { if (ins == null) {
return null return null
} else { } else {
@ -765,13 +765,13 @@ module.exports = function (Y/* :any */) {
} }
} }
* getInsertionCleanStartEnd (id) { * getInsertionCleanStartEnd (id) {
yield* this.getInsertionCleanStart(id) yield * this.getInsertionCleanStart(id)
return yield* this.getInsertionCleanEnd(id) return yield * this.getInsertionCleanEnd(id)
} }
// Return an insertion such that id is the first element of content // Return an insertion such that id is the first element of content
// This function manipulates an operation, if necessary // This function manipulates an operation, if necessary
* getInsertionCleanStart (id) { * getInsertionCleanStart (id) {
var ins = yield* this.getInsertion(id) var ins = yield * this.getInsertion(id)
if (ins != null) { if (ins != null) {
if (ins.id[1] === id[1]) { if (ins.id[1] === id[1]) {
return ins return ins
@ -785,8 +785,8 @@ module.exports = function (Y/* :any */) {
left.right = ins.id left.right = ins.id
ins.left = leftLid ins.left = leftLid
// debugger // check // debugger // check
yield* this.setOperation(left) yield * this.setOperation(left)
yield* this.setOperation(ins) yield * this.setOperation(ins)
if (left.gc) { if (left.gc) {
this.store.queueGarbageCollector(ins.id) this.store.queueGarbageCollector(ins.id)
} }
@ -799,7 +799,7 @@ module.exports = function (Y/* :any */) {
// Return an insertion such that id is the last element of content // Return an insertion such that id is the last element of content
// This function manipulates an operation, if necessary // This function manipulates an operation, if necessary
* getInsertionCleanEnd (id) { * getInsertionCleanEnd (id) {
var ins = yield* this.getInsertion(id) var ins = yield * this.getInsertion(id)
if (ins != null) { if (ins != null) {
if (ins.content == null || (ins.id[1] + ins.content.length - 1 === id[1])) { if (ins.content == null || (ins.id[1] + ins.content.length - 1 === id[1])) {
return ins return ins
@ -813,8 +813,8 @@ module.exports = function (Y/* :any */) {
ins.right = right.id ins.right = right.id
right.left = insLid right.left = insLid
// debugger // check // debugger // check
yield* this.setOperation(right) yield * this.setOperation(right)
yield* this.setOperation(ins) yield * this.setOperation(ins)
if (ins.gc) { if (ins.gc) {
this.store.queueGarbageCollector(right.id) this.store.queueGarbageCollector(right.id)
} }
@ -825,7 +825,7 @@ module.exports = function (Y/* :any */) {
} }
} }
* getOperation (id/* :any */)/* :Transaction<any> */ { * getOperation (id/* :any */)/* :Transaction<any> */ {
var o = yield* this.os.find(id) var o = yield * this.os.find(id)
if (id[0] !== '_' || o != null) { if (id[0] !== '_' || o != null) {
return o return o
} else { // type is string } else { // type is string
@ -835,7 +835,7 @@ module.exports = function (Y/* :any */) {
var struct = comp[0] var struct = comp[0]
var op = Y.Struct[struct].create(id) var op = Y.Struct[struct].create(id)
op.type = comp[1] op.type = comp[1]
yield* this.setOperation(op) yield * this.setOperation(op)
return op return op
} else { } else {
// won't be called. but just in case.. // won't be called. but just in case..
@ -846,17 +846,17 @@ module.exports = function (Y/* :any */) {
} }
} }
* removeOperation (id) { * removeOperation (id) {
yield* this.os.delete(id) yield * this.os.delete(id)
} }
* setState (state) { * setState (state) {
var val = { var val = {
id: [state.user], id: [state.user],
clock: state.clock clock: state.clock
} }
yield* this.ss.put(val) yield * this.ss.put(val)
} }
* getState (user) { * getState (user) {
var n = yield* this.ss.find([user]) var n = yield * this.ss.find([user])
var clock = n == null ? null : n.clock var clock = n == null ? null : n.clock
if (clock == null) { if (clock == null) {
clock = 0 clock = 0
@ -868,7 +868,7 @@ module.exports = function (Y/* :any */) {
} }
* getStateVector () { * getStateVector () {
var stateVector = [] var stateVector = []
yield* this.ss.iterate(this, null, null, function * (n) { yield * this.ss.iterate(this, null, null, function * (n) {
stateVector.push({ stateVector.push({
user: n.id[0], user: n.id[0],
clock: n.clock clock: n.clock
@ -878,7 +878,7 @@ module.exports = function (Y/* :any */) {
} }
* getStateSet () { * getStateSet () {
var ss = {} var ss = {}
yield* this.ss.iterate(this, null, null, function * (n) { yield * this.ss.iterate(this, null, null, function * (n) {
ss[n.id[0]] = n.clock ss[n.id[0]] = n.clock
}) })
return ss return ss
@ -936,7 +936,7 @@ module.exports = function (Y/* :any */) {
} }
var send = [] var send = []
var endSV = yield* this.getStateVector() var endSV = yield * this.getStateVector()
for (var endState of endSV) { for (var endState of endSV) {
var user = endState.user var user = endState.user
if (user === '_') { if (user === '_') {
@ -946,14 +946,14 @@ module.exports = function (Y/* :any */) {
if (startPos > 0) { if (startPos > 0) {
// There is a change that [user, startPos] is in a composed Insertion (with a smaller counter) // There is a change that [user, startPos] is in a composed Insertion (with a smaller counter)
// find out if that is the case // find out if that is the case
var firstMissing = yield* this.getInsertion([user, startPos]) var firstMissing = yield * this.getInsertion([user, startPos])
if (firstMissing != null) { if (firstMissing != null) {
// update startPos // update startPos
startPos = firstMissing.id[1] startPos = firstMissing.id[1]
startSS[user] = startPos startSS[user] = startPos
} }
} }
yield* this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) { yield * this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) {
op = Y.Struct[op.struct].encode(op) op = Y.Struct[op.struct].encode(op)
if (op.struct !== 'Insert') { if (op.struct !== 'Insert') {
send.push(op) send.push(op)
@ -966,7 +966,7 @@ module.exports = function (Y/* :any */) {
// 2. or to the first operation that has an origin that is not to the // 2. or to the first operation that has an origin that is not to the
// right of op. // right of op.
// For this we maintain a list of ops which origins are not found yet. // For this we maintain a list of ops which origins are not found yet.
var missing_origins = [op] var missingOrigins = [op]
var newright = op.right var newright = op.right
while (true) { while (true) {
if (o.left == null) { if (o.left == null) {
@ -974,15 +974,15 @@ module.exports = function (Y/* :any */) {
send.push(op) send.push(op)
if (!Y.utils.compareIds(o.id, op.id)) { if (!Y.utils.compareIds(o.id, op.id)) {
o = Y.Struct[op.struct].encode(o) o = Y.Struct[op.struct].encode(o)
o.right = missing_origins[missing_origins.length - 1].id o.right = missingOrigins[missingOrigins.length - 1].id
send.push(o) send.push(o)
} }
break break
} }
o = yield* this.getInsertion(o.left) o = yield * this.getInsertion(o.left)
// we set another o, check if we can reduce $missing_origins // we set another o, check if we can reduce $missingOrigins
while (missing_origins.length > 0 && Y.utils.matchesId(o, missing_origins[missing_origins.length - 1].origin)) { while (missingOrigins.length > 0 && Y.utils.matchesId(o, missingOrigins[missingOrigins.length - 1].origin)) {
missing_origins.pop() missingOrigins.pop()
} }
if (o.id[1] < (startSS[o.id[0]] || 0)) { if (o.id[1] < (startSS[o.id[0]] || 0)) {
// case 2. o is known // case 2. o is known
@ -995,17 +995,17 @@ module.exports = function (Y/* :any */) {
send.push(op) send.push(op)
op = Y.Struct[op.struct].encode(o) op = Y.Struct[op.struct].encode(o)
op.right = newright op.right = newright
if (missing_origins.length > 0) { if (missingOrigins.length > 0) {
console.log('This should not happen .. :( please report this') console.log('This should not happen .. :( please report this')
} }
missing_origins = [op] missingOrigins = [op]
} else { } else {
// case 4. send o, continue to find op.origin // case 4. send o, continue to find op.origin
var s = Y.Struct[op.struct].encode(o) var s = Y.Struct[op.struct].encode(o)
s.right = missing_origins[missing_origins.length - 1].id s.right = missingOrigins[missingOrigins.length - 1].id
s.left = s.origin s.left = s.origin
send.push(s) send.push(s)
missing_origins.push(o) missingOrigins.push(o)
} }
} }
} }
@ -1020,7 +1020,7 @@ module.exports = function (Y/* :any */) {
*/ */
* getOperationsUntransformed () { * getOperationsUntransformed () {
var ops = [] var ops = []
yield* this.os.iterate(this, null, null, function * (op) { yield * this.os.iterate(this, null, null, function * (op) {
if (op.id[0] !== '_') { if (op.id[0] !== '_') {
ops.push(op) ops.push(op)
} }
@ -1039,25 +1039,25 @@ module.exports = function (Y/* :any */) {
// update parents .map/start/end properties // update parents .map/start/end properties
if (op.parentSub != null && op.left == null) { if (op.parentSub != null && op.left == null) {
// op is child of Map // op is child of Map
let parent = yield* this.getOperation(op.parent) let parent = yield * this.getOperation(op.parent)
parent.map[op.parentSub] = op.id parent.map[op.parentSub] = op.id
yield* this.setOperation(parent) yield * this.setOperation(parent)
} else if (op.right == null || op.left == null) { } else if (op.right == null || op.left == null) {
let parent = yield* this.getOperation(op.parent) let parent = yield * this.getOperation(op.parent)
if (op.right == null) { if (op.right == null) {
parent.end = Y.utils.getLastId(op) parent.end = Y.utils.getLastId(op)
} }
if (op.left == null) { if (op.left == null) {
parent.start = op.id parent.start = op.id
} }
yield* this.setOperation(parent) yield * this.setOperation(parent)
} }
} }
} }
yield* this.os.put(op) yield * this.os.put(op)
} }
for (var user in stateSet) { for (var user in stateSet) {
yield* this.ss.put({ yield * this.ss.put({
id: [user], id: [user],
clock: stateSet[user] clock: stateSet[user]
}) })
@ -1089,9 +1089,9 @@ module.exports = function (Y/* :any */) {
} }
*/ */
* flush () { * flush () {
yield* this.os.flush() yield * this.os.flush()
yield* this.ss.flush() yield * this.ss.flush()
yield* this.ds.flush() yield * this.ds.flush()
} }
} }
Y.Transaction = TransactionInterface Y.Transaction = TransactionInterface

View File

@ -1,6 +1,3 @@
/* @flow */
'use strict'
/* /*
EventHandler is an helper class for constructing custom types. EventHandler is an helper class for constructing custom types.
@ -23,7 +20,8 @@
database request to finish). EventHandler helps you to make your type database request to finish). EventHandler helps you to make your type
synchronous. synchronous.
*/ */
module.exports = function (Y /* : any*/) {
export default function Utils (Y) {
Y.utils = {} Y.utils = {}
Y.utils.bubbleEvent = function (type, event) { Y.utils.bubbleEvent = function (type, event) {
@ -299,7 +297,7 @@ module.exports = function (Y /* : any*/) {
} }
var before = this.waiting.length var before = this.waiting.length
// somehow create new operations // somehow create new operations
yield* f.apply(transaction, args) yield * f.apply(transaction, args)
// remove all appended ops / awaited ops // remove all appended ops / awaited ops
this.waiting.splice(before) this.waiting.splice(before)
if (this.awaiting > 0) this.awaiting-- if (this.awaiting > 0) this.awaiting--
@ -309,7 +307,7 @@ module.exports = function (Y /* : any*/) {
for (let i = 0; i < this.waiting.length; i++) { for (let i = 0; i < this.waiting.length; i++) {
var o = this.waiting[i] var o = this.waiting[i]
if (o.struct === 'Insert') { if (o.struct === 'Insert') {
var _o = yield* transaction.getInsertion(o.id) var _o = yield * transaction.getInsertion(o.id)
if (_o.parentSub != null && _o.left != null) { if (_o.parentSub != null && _o.left != null) {
// if o is an insertion of a map struc (parentSub is defined), then it shouldn't be necessary to compute left // if o is an insertion of a map struc (parentSub is defined), then it shouldn't be necessary to compute left
this.waiting.splice(i, 1) this.waiting.splice(i, 1)
@ -321,10 +319,10 @@ module.exports = function (Y /* : any*/) {
o.left = null o.left = null
} else { } else {
// find next undeleted op // find next undeleted op
var left = yield* transaction.getInsertion(_o.left) var left = yield * transaction.getInsertion(_o.left)
while (left.deleted != null) { while (left.deleted != null) {
if (left.left != null) { if (left.left != null) {
left = yield* transaction.getInsertion(left.left) left = yield * transaction.getInsertion(left.left)
} else { } else {
left = null left = null
break break
@ -662,7 +660,7 @@ module.exports = function (Y /* : any*/) {
if (i < 0 && noSuperCall === undefined) { if (i < 0 && noSuperCall === undefined) {
// did not reach break in last loop // did not reach break in last loop
// read id and put it to the end of readBuffer // read id and put it to the end of readBuffer
o = yield* super.find(id) o = yield * super.find(id)
} }
if (o != null) { if (o != null) {
for (i = 0; i < this.readBuffer.length - 1; i++) { for (i = 0; i < this.readBuffer.length - 1; i++) {
@ -692,7 +690,7 @@ module.exports = function (Y /* : any*/) {
// write writeBuffer[0] // write writeBuffer[0]
var write = this.writeBuffer[0] var write = this.writeBuffer[0]
if (write.id[0] !== null) { if (write.id[0] !== null) {
yield* super.put(write) yield * super.put(write)
} }
// put o to the end of writeBuffer // put o to the end of writeBuffer
for (i = 0; i < this.writeBuffer.length - 1; i++) { for (i = 0; i < this.writeBuffer.length - 1; i++) {
@ -722,44 +720,44 @@ module.exports = function (Y /* : any*/) {
} }
} }
} }
yield* this.flush() yield * this.flush()
yield* super.delete(id) yield * super.delete(id)
} }
* findWithLowerBound (id) { * findWithLowerBound (id) {
var o = yield* this.find(id, true) var o = yield * this.find(id, true)
if (o != null) { if (o != null) {
return o return o
} else { } else {
yield* this.flush() yield * this.flush()
return yield* super.findWithLowerBound.apply(this, arguments) return yield * super.findWithLowerBound.apply(this, arguments)
} }
} }
* findWithUpperBound (id) { * findWithUpperBound (id) {
var o = yield* this.find(id, true) var o = yield * this.find(id, true)
if (o != null) { if (o != null) {
return o return o
} else { } else {
yield* this.flush() yield * this.flush()
return yield* super.findWithUpperBound.apply(this, arguments) return yield * super.findWithUpperBound.apply(this, arguments)
} }
} }
* findNext () { * findNext () {
yield* this.flush() yield * this.flush()
return yield* super.findNext.apply(this, arguments) return yield * super.findNext.apply(this, arguments)
} }
* findPrev () { * findPrev () {
yield* this.flush() yield * this.flush()
return yield* super.findPrev.apply(this, arguments) return yield * super.findPrev.apply(this, arguments)
} }
* iterate () { * iterate () {
yield* this.flush() yield * this.flush()
yield* super.iterate.apply(this, arguments) yield * super.iterate.apply(this, arguments)
} }
* flush () { * flush () {
for (var i = 0; i < this.writeBuffer.length; i++) { for (var i = 0; i < this.writeBuffer.length; i++) {
var write = this.writeBuffer[i] var write = this.writeBuffer[i]
if (write.id[0] !== null) { if (write.id[0] !== null) {
yield* super.put(write) yield * super.put(write)
this.writeBuffer[i] = { this.writeBuffer[i] = {
id: [null, null] id: [null, null]
} }

View File

@ -1,18 +1,20 @@
/* @flow */ import debug from 'debug'
'use strict' import extendConnector from './Connector.js'
import extendDatabase from './Database.js'
import extendTransaction from './Transaction.js'
import extendStruct from './Struct.js'
import extendUtils from './Utils.js'
require('./Connector.js')(Y) extendConnector(Y)
require('./Database.js')(Y) extendDatabase(Y)
require('./Transaction.js')(Y) extendTransaction(Y)
require('./Struct.js')(Y) extendStruct(Y)
require('./Utils.js')(Y) extendUtils(Y)
require('./Connectors/Test.js')(Y)
Y.debug = require('debug') Y.debug = debug
var requiringModules = {} var requiringModules = {}
module.exports = Y
Y.requiringModules = requiringModules Y.requiringModules = requiringModules
Y.extend = function (name, value) { Y.extend = function (name, value) {
@ -110,7 +112,7 @@ type YOptions = {
} }
*/ */
function Y (opts/* :YOptions */) /* :Promise<YConfig> */ { export default function Y (opts/* :YOptions */) /* :Promise<YConfig> */ {
if (opts.hasOwnProperty('sourceDir')) { if (opts.hasOwnProperty('sourceDir')) {
Y.sourceDir = opts.sourceDir Y.sourceDir = opts.sourceDir
} }
@ -120,11 +122,11 @@ function Y (opts/* :YOptions */) /* :Promise<YConfig> */ {
modules.push(opts.share[name]) modules.push(opts.share[name])
} }
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if (opts == null) reject('An options object is expected! ') if (opts == null) reject(new Error('An options object is expected!'))
else if (opts.connector == null) reject('You must specify a connector! (missing connector property)') else if (opts.connector == null) reject(new Error('You must specify a connector! (missing connector property)'))
else if (opts.connector.name == null) reject('You must specify connector name! (missing connector.name property)') else if (opts.connector.name == null) reject(new Error('You must specify connector name! (missing connector.name property)'))
else if (opts.db == null) reject('You must specify a database! (missing db property)') else if (opts.db == null) reject(new Error('You must specify a database! (missing db property)'))
else if (opts.connector.name == null) reject('You must specify db name! (missing db.name property)') else if (opts.connector.name == null) reject(new Error('You must specify db name! (missing db.name property)'))
else { else {
opts = Y.utils.copyObject(opts) opts = Y.utils.copyObject(opts)
opts.connector = Y.utils.copyObject(opts.connector) opts.connector = Y.utils.copyObject(opts.connector)
@ -182,7 +184,7 @@ class YConfig {
args = typedef.parseArguments(args[0])[1] args = typedef.parseArguments(args[0])[1]
} }
} }
share[propertyname] = yield* this.store.initType.call(this, id, args) share[propertyname] = yield * this.store.initType.call(this, id, args)
} }
this.store.whenTransactionsFinished() this.store.whenTransactionsFinished()
.then(callback) .then(callback)
@ -229,7 +231,7 @@ class YConfig {
this.db.destroyTypes() this.db.destroyTypes()
// make sure to wait for all transactions before destroying the db // make sure to wait for all transactions before destroying the db
this.db.requestTransaction(function * () { this.db.requestTransaction(function * () {
yield* self.db.destroy() yield * self.db.destroy()
}) })
return this.db.whenTransactionsFinished() return this.db.whenTransactionsFinished()
}) })