mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-22 20:41:18 +00:00
Initial concept of JS for Chrome debugging
The idea is that we will have an NPM module that should always be required to use the Realm JS API. It will handle the underlying implementation details depending on which environment it is being run in (i.e. JavaScriptCore, Chrome, Node). The focus here was on stubbing out the machinery required to get React Native Chrome debugging working with the Realm API by leveraging synchronous requests. The app itself will need to run a web server that responds to these requests.
This commit is contained in:
parent
4e6c03eafb
commit
bf1ecfbb07
@ -5,6 +5,7 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var Realm = require('realm');
|
||||
|
||||
var {
|
||||
AppRegistry,
|
||||
|
@ -6,6 +6,7 @@
|
||||
"start": "node_modules/react-native/packager/packager.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-native": "0.11.4"
|
||||
"react-native": "0.11.4",
|
||||
"realm": "file:../../lib"
|
||||
}
|
||||
}
|
||||
|
14
lib/.eslintrc
Normal file
14
lib/.eslintrc
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"commonjs": true,
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
},
|
||||
"rules": {
|
||||
"comma-dangle": 0,
|
||||
"no-unused-vars": 1,
|
||||
"strict": [2, "global"]
|
||||
}
|
||||
}
|
7
lib/index.js
Normal file
7
lib/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
if (typeof Realm != 'undefined') {
|
||||
module.exports = Realm; // eslint-disable-line no-undef
|
||||
} else {
|
||||
module.exports = require('./shim');
|
||||
}
|
88
lib/lists.js
Normal file
88
lib/lists.js
Normal file
@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
const rpc = require('./rpc');
|
||||
|
||||
const idKey = Symbol();
|
||||
const realmKey = Symbol();
|
||||
const prototype = {};
|
||||
|
||||
exports.create = create;
|
||||
|
||||
[
|
||||
'pop',
|
||||
'shift',
|
||||
'push',
|
||||
'unshift',
|
||||
'splice',
|
||||
].forEach(function(name, i) {
|
||||
const growthMethod = (i >= 2);
|
||||
|
||||
Object.defineProperty(prototype, name, {
|
||||
value: function() {
|
||||
let listId = this[idKey];
|
||||
let realmId = this[realmKey];
|
||||
|
||||
if (!listId || !realmId) {
|
||||
throw new TypeError(name + ' method was not called on a List!');
|
||||
}
|
||||
|
||||
let result = rpc.callListMethod(realmId, listId, name, Array.from(arguments));
|
||||
|
||||
// Since this method might have grown the list, ensure index properties are defined.
|
||||
if (growthMethod) {
|
||||
defineIndexProperties(this.length);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Object.defineProperty(prototype, 'length', {
|
||||
get() {
|
||||
return rpc.getListSize(this[realmKey], this[idKey]);
|
||||
}
|
||||
});
|
||||
|
||||
function create(realmId, info) {
|
||||
let list = Object.create(prototype);
|
||||
let size = info.size;
|
||||
|
||||
list[realmKey] = realmId;
|
||||
list[idKey] = info.id;
|
||||
|
||||
defineIndexProperties(size);
|
||||
}
|
||||
|
||||
let maxSize = 0;
|
||||
function defineIndexProperties(size) {
|
||||
if (size < maxSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
let props = {};
|
||||
for (let i = maxSize; i <= size; i++) {
|
||||
props[i] = {
|
||||
get: getterForIndex(i),
|
||||
set: setterForIndex(i),
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Use ES6 Proxy once it's supported on Chrome!
|
||||
Object.defineProperties(prototype, props);
|
||||
|
||||
maxSize = size + 1;
|
||||
}
|
||||
|
||||
function getterForIndex(index) {
|
||||
return function() {
|
||||
let realmId = this[realmKey];
|
||||
return rpc.getListItem(realmId, this[idKey], index);
|
||||
};
|
||||
}
|
||||
|
||||
function setterForIndex(index) {
|
||||
return function(value) {
|
||||
rpc.setListItem(this[realmKey], this[idKey], index, value);
|
||||
};
|
||||
}
|
52
lib/objects.js
Normal file
52
lib/objects.js
Normal file
@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
const rpc = require('./rpc');
|
||||
|
||||
const idKey = Symbol();
|
||||
const realmKey = Symbol();
|
||||
const schemaKey = Symbol();
|
||||
const registeredConstructors = {};
|
||||
|
||||
exports.create = create;
|
||||
exports.registerConstructors = registerConstructors;
|
||||
|
||||
function create(realmId, info) {
|
||||
let schema = info.schema;
|
||||
let constructor = (registeredConstructors[realmId] || {})[schema.name];
|
||||
let object = constructor ? Object.create(constructor.prototype) : {};
|
||||
let props = {};
|
||||
|
||||
object[realmKey] = realmId;
|
||||
object[idKey] = info.id;
|
||||
object[schemaKey] = schema;
|
||||
|
||||
for (let prop of schema.properties) {
|
||||
let name = prop.name;
|
||||
|
||||
props[name] = {
|
||||
get: getterForProperty(name),
|
||||
set: setterForProperty(name),
|
||||
};
|
||||
}
|
||||
|
||||
Object.defineProperties(object, props);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
function registerConstructors(realmId, constructors) {
|
||||
registeredConstructors[realmId] = constructors;
|
||||
}
|
||||
|
||||
function getterForProperty(name) {
|
||||
return function() {
|
||||
let realmId = this[realmKey];
|
||||
return rpc.getObjectProperty(realmId, this[idKey], name);
|
||||
};
|
||||
}
|
||||
|
||||
function setterForProperty(name) {
|
||||
return function(value) {
|
||||
rpc.setObjectProperty(this[realmKey], this[idKey], name, value);
|
||||
};
|
||||
}
|
5
lib/package.json
Normal file
5
lib/package.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "realm",
|
||||
"version": "0.0.1",
|
||||
"private": true
|
||||
}
|
99
lib/rpc.js
Normal file
99
lib/rpc.js
Normal file
@ -0,0 +1,99 @@
|
||||
'use strict';
|
||||
|
||||
const DEVICE_HOST = 'localhost:8082';
|
||||
|
||||
const typeConverters = {};
|
||||
|
||||
exports.registerTypeConverter = registerTypeConverter;
|
||||
|
||||
exports.createRealm = createRealm;
|
||||
exports.createObject = createObject;
|
||||
|
||||
exports.getObjectProperty = getObjectProperty;
|
||||
exports.setObjectProperty = setObjectProperty;
|
||||
|
||||
exports.getListItem = getListItem;
|
||||
exports.setListItem = setListItem;
|
||||
exports.getListSize = getListSize;
|
||||
exports.callListMethod = callListMethod;
|
||||
|
||||
exports.beginTransaction = beginTransaction;
|
||||
exports.cancelTransaction = cancelTransaction;
|
||||
exports.commitTransaction = commitTransaction;
|
||||
|
||||
function registerTypeConverter(type, handler) {
|
||||
typeConverters[type] = handler;
|
||||
}
|
||||
|
||||
function createRealm(config) {
|
||||
return sendRequest('create_realm', config);
|
||||
}
|
||||
|
||||
function createObject(realmId, type, values) {
|
||||
return sendRequest('create_object', {realmId, type, values});
|
||||
}
|
||||
|
||||
function getObjectProperty(realmId, objectId, name) {
|
||||
let result = sendRequest('get_property', {realmId, objectId, name});
|
||||
return convert(result);
|
||||
}
|
||||
|
||||
function setObjectProperty(realmId, objectId, name, value) {
|
||||
sendRequest('set_property', {realmId, objectId, name, value});
|
||||
}
|
||||
|
||||
function getListItem(realmId, listId, index) {
|
||||
let result = sendRequest('get_list_item', {realmId, listId, index});
|
||||
return convert(result);
|
||||
}
|
||||
|
||||
function setListItem(realmId, listId, index, value) {
|
||||
sendRequest('set_list_item', {realmId, listId, index, value});
|
||||
}
|
||||
|
||||
function getListSize(realmId, listId) {
|
||||
return sendRequest('get_list_size', {realmId, listId});
|
||||
}
|
||||
|
||||
function callListMethod(realmId, listId, name, args) {
|
||||
let result = sendRequest('call_list_method', {realmId, listId, name, arguments: args});
|
||||
return convert(result);
|
||||
}
|
||||
|
||||
function beginTransaction(realmId) {
|
||||
sendRequest('begin_transaction', {realmId});
|
||||
}
|
||||
|
||||
function cancelTransaction(realmId) {
|
||||
sendRequest('cancel_transaction', {realmId});
|
||||
}
|
||||
|
||||
function commitTransaction(realmId) {
|
||||
sendRequest('commit_transaction', {realmId});
|
||||
}
|
||||
|
||||
function convert(realmId, info) {
|
||||
let handler = typeConverters[info.type];
|
||||
return handler ? handler(realmId, info) : info.value;
|
||||
}
|
||||
|
||||
function sendRequest(command, data) {
|
||||
let body = JSON.stringify(data);
|
||||
let request = new XMLHttpRequest();
|
||||
let url = 'http://' + DEVICE_HOST + '/' + command;
|
||||
|
||||
request.open('POST', url, false);
|
||||
request.send(body);
|
||||
|
||||
if (request.status != 200) {
|
||||
throw new Error(request.responseText);
|
||||
}
|
||||
|
||||
let response = JSON.parse(request.responseText);
|
||||
|
||||
if (!response || response.error) {
|
||||
throw new Error((response && response.error) || 'Invalid response for "' + command + '"');
|
||||
}
|
||||
|
||||
return response.result;
|
||||
}
|
77
lib/shim.js
Normal file
77
lib/shim.js
Normal file
@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
const lists = require('./lists');
|
||||
const objects = require('./objects');
|
||||
const rpc = require('./rpc');
|
||||
const types = require('./types');
|
||||
|
||||
const realmKey = Symbol();
|
||||
|
||||
// TODO: DATA
|
||||
rpc.registerTypeConverter(types.DATE, (_, info) => new Date(info.value));
|
||||
rpc.registerTypeConverter(types.LIST, lists.create);
|
||||
rpc.registerTypeConverter(types.OBJECT, objects.create);
|
||||
|
||||
class Realm {
|
||||
constructor(config) {
|
||||
let schema = typeof config == 'object' && config.schema;
|
||||
let constructors = {};
|
||||
|
||||
for (let i = 0, len = schema ? schema.length : 0; i < len; i++) {
|
||||
let item = schema[i];
|
||||
let proto = item.prototype;
|
||||
|
||||
if (proto.schema) {
|
||||
schema.splice(i, 1, proto.schema);
|
||||
constructors[proto.schema.name] = item;
|
||||
}
|
||||
}
|
||||
|
||||
let realmId = this[realmKey] = rpc.createRealm(config);
|
||||
|
||||
objects.registerConstructors(realmId, constructors);
|
||||
}
|
||||
|
||||
addNotification(callback) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
create(type, values) {
|
||||
let realmId = this[realmKey];
|
||||
let info = rpc.createObject(realmId, type, values);
|
||||
|
||||
return objects.create(realmId, info);
|
||||
}
|
||||
|
||||
delete(object) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
deleteAll() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
objects(type) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
write(callback) {
|
||||
if (typeof callback != 'function')
|
||||
throw new TypeError('Realm.write() must be passed a function!');
|
||||
|
||||
rpc.beginTransaction(this[realmKey]);
|
||||
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
rpc.cancelTransaction(this[realmKey]);
|
||||
throw e;
|
||||
}
|
||||
|
||||
rpc.commitTransaction(this[realmKey]);
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(Realm, 'Types', {value: types});
|
||||
|
||||
module.exports = Realm;
|
19
lib/types.js
Normal file
19
lib/types.js
Normal file
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const types = {};
|
||||
|
||||
[
|
||||
'BOOL',
|
||||
'INT',
|
||||
'FLOAT',
|
||||
'DOUBLE',
|
||||
'STRING',
|
||||
'DATE',
|
||||
'DATA',
|
||||
'OBJECT',
|
||||
'LIST',
|
||||
].forEach(function(type) {
|
||||
types[type] = 'PropTypes' + type;
|
||||
});
|
||||
|
||||
module.exports = Object.freeze(types);
|
Loading…
x
Reference in New Issue
Block a user