2016-03-18 21:00:51 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var Immutable = require('immutable');
|
2016-05-15 18:55:48 +00:00
|
|
|
var _Dataspace = require('./dataspace.js');
|
|
|
|
var Dataspace = _Dataspace.Dataspace;
|
2016-06-05 17:14:54 +00:00
|
|
|
var __ = _Dataspace.__;
|
2016-03-18 21:00:51 +00:00
|
|
|
var Mux = require('./mux.js');
|
|
|
|
var Patch = require('./patch.js');
|
2016-05-08 15:33:39 +00:00
|
|
|
var Trie = require('./trie.js');
|
2016-03-18 21:00:51 +00:00
|
|
|
var Util = require('./util.js');
|
2016-08-07 19:33:09 +00:00
|
|
|
var Dataflow = require('./dataflow.js');
|
2016-03-18 21:00:51 +00:00
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2016-08-07 19:33:09 +00:00
|
|
|
function spawnActor(bootFn, optName) {
|
|
|
|
Dataspace.spawn(new Actor(bootFn, optName));
|
2016-03-18 21:00:51 +00:00
|
|
|
}
|
|
|
|
|
2016-08-07 19:33:09 +00:00
|
|
|
function Actor(bootFn, optName) {
|
|
|
|
this.fields = {};
|
2016-03-18 21:00:51 +00:00
|
|
|
this.facets = Immutable.Set();
|
|
|
|
this.mux = new Mux.Mux();
|
2016-05-18 00:04:37 +00:00
|
|
|
this.previousKnowledge = Trie.emptyTrie;
|
2016-05-15 18:55:48 +00:00
|
|
|
this.knowledge = Trie.emptyTrie;
|
2016-05-15 20:16:36 +00:00
|
|
|
this.pendingActions = [];
|
2016-08-07 19:33:09 +00:00
|
|
|
this.dataflowGraph = new Dataflow.Graph();
|
2016-03-18 21:00:51 +00:00
|
|
|
|
2016-05-18 20:51:51 +00:00
|
|
|
if (typeof optName !== 'undefined') {
|
|
|
|
this.name = optName;
|
|
|
|
}
|
|
|
|
|
2016-03-18 21:00:51 +00:00
|
|
|
this.boot = function() {
|
2016-05-10 04:40:53 +00:00
|
|
|
var self = this;
|
2016-08-08 01:01:49 +00:00
|
|
|
withCurrentFacet(null, function () {
|
2016-08-07 19:33:09 +00:00
|
|
|
bootFn.call(self.fields);
|
2016-05-10 04:40:53 +00:00
|
|
|
});
|
2016-08-07 19:33:09 +00:00
|
|
|
this.quiesce();
|
2016-03-18 21:00:51 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-08-07 19:33:09 +00:00
|
|
|
(function () {
|
|
|
|
var priorities = ['PRIORITY_QUERY_HIGH',
|
|
|
|
'PRIORITY_QUERY',
|
|
|
|
'PRIORITY_QUERY_HANDLER',
|
|
|
|
'PRIORITY_NORMAL'];
|
|
|
|
for (var i = 0; i < priorities.length; i++) {
|
|
|
|
Actor[priorities[i]] = i;
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
|
|
|
|
Actor.prototype.nextPendingAction = function (probe) {
|
|
|
|
for (var i = 0; i < this.pendingActions.length; i++) {
|
|
|
|
var q = this.pendingActions[i];
|
|
|
|
if (q.length > 0) {
|
|
|
|
return probe ? true : q.shift();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2016-03-18 21:00:51 +00:00
|
|
|
Actor.prototype.handleEvent = function(e) {
|
2016-08-07 19:33:09 +00:00
|
|
|
var actor = this;
|
2016-05-15 18:55:48 +00:00
|
|
|
if (e.type === 'stateChange') {
|
2016-05-18 00:04:37 +00:00
|
|
|
this.previousKnowledge = this.knowledge;
|
2016-05-15 18:55:48 +00:00
|
|
|
this.knowledge = e.patch.updateInterests(this.knowledge);
|
|
|
|
}
|
2016-08-07 19:33:09 +00:00
|
|
|
if (this.nextPendingAction(true)) {
|
2016-05-15 20:16:36 +00:00
|
|
|
throw new Error('Syndicate: pendingActions must not be nonempty at start of handleEvent');
|
|
|
|
}
|
2016-03-18 21:00:51 +00:00
|
|
|
this.facets.forEach(function (f) {
|
2016-08-08 01:01:49 +00:00
|
|
|
withCurrentFacet(f, function () { f.handleEvent(e, false); });
|
2016-03-18 21:00:51 +00:00
|
|
|
});
|
2016-08-07 19:33:09 +00:00
|
|
|
this.quiesce();
|
|
|
|
};
|
|
|
|
|
|
|
|
Actor.prototype.quiesce = function() {
|
|
|
|
var actor = this;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
var entry = this.nextPendingAction(false);
|
|
|
|
if (!entry) break;
|
|
|
|
|
2016-08-08 01:01:49 +00:00
|
|
|
withCurrentFacet(entry.facet, entry.action);
|
2016-08-07 19:33:09 +00:00
|
|
|
|
|
|
|
this.dataflowGraph.repairDamage(function (subjectId) {
|
|
|
|
var facet = subjectId[0];
|
|
|
|
var endpoint = subjectId[1];
|
2016-08-08 01:05:35 +00:00
|
|
|
if (!facet.terminated) {
|
|
|
|
withCurrentFacet(facet, function () {
|
|
|
|
var patch = Patch.retract(__).andThen(endpoint.subscriptionFn.call(facet.fields));
|
|
|
|
var r = facet.actor.mux.updateStream(endpoint.eid, patch);
|
|
|
|
Dataspace.stateChange(r.deltaAggregate);
|
|
|
|
});
|
|
|
|
}
|
2016-08-07 19:33:09 +00:00
|
|
|
});
|
2016-05-15 20:16:36 +00:00
|
|
|
}
|
2016-08-07 19:33:09 +00:00
|
|
|
|
2016-03-19 18:47:39 +00:00
|
|
|
this.checkForTermination();
|
2016-03-18 21:00:51 +00:00
|
|
|
};
|
|
|
|
|
2016-08-07 19:33:09 +00:00
|
|
|
Actor.prototype.pushAction = function (a, priorityOpt) {
|
|
|
|
var priority = typeof priorityOpt === 'undefined' ? Actor.PRIORITY_NORMAL : priorityOpt;
|
|
|
|
while (this.pendingActions.length < priority + 1) {
|
|
|
|
this.pendingActions.push([]);
|
|
|
|
}
|
|
|
|
this.pendingActions[priority].push({facet: Facet.current, action: a});
|
2016-05-15 20:16:36 +00:00
|
|
|
};
|
|
|
|
|
2016-03-18 21:00:51 +00:00
|
|
|
Actor.prototype.addFacet = function(facet) {
|
|
|
|
this.facets = this.facets.add(facet);
|
|
|
|
};
|
|
|
|
|
2016-03-18 21:08:49 +00:00
|
|
|
Actor.prototype.removeFacet = function(facet) {
|
|
|
|
this.facets = this.facets.remove(facet);
|
2016-03-19 18:47:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Actor.prototype.checkForTermination = function() {
|
2016-03-18 21:08:49 +00:00
|
|
|
if (this.facets.isEmpty()) {
|
2016-04-07 07:42:54 +00:00
|
|
|
Dataspace.exit();
|
2016-03-18 21:08:49 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-18 21:00:51 +00:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
function Facet(actor) {
|
|
|
|
this.actor = actor;
|
|
|
|
this.endpoints = Immutable.Map();
|
2016-03-18 21:13:52 +00:00
|
|
|
this.initBlocks = Immutable.List();
|
|
|
|
this.doneBlocks = Immutable.List();
|
2016-05-10 04:40:53 +00:00
|
|
|
this.children = Immutable.Set();
|
|
|
|
this.parent = Facet.current;
|
2016-08-07 19:33:09 +00:00
|
|
|
this.fields = Dataflow.Graph.newScope((this.parent && this.parent.fields) || actor.fields);
|
2016-05-18 00:04:37 +00:00
|
|
|
this.terminated = false;
|
2016-08-08 01:02:17 +00:00
|
|
|
this.fid = Facet.nextFid++;
|
2016-05-10 04:40:53 +00:00
|
|
|
}
|
|
|
|
|
2016-08-08 01:02:17 +00:00
|
|
|
Facet.nextFid = 0;
|
2016-05-10 04:40:53 +00:00
|
|
|
Facet.current = null;
|
|
|
|
|
2016-08-25 12:12:32 +00:00
|
|
|
Facet.build = function(f) {
|
|
|
|
var facet = new Facet(Dataspace.activeBehavior());
|
2016-12-05 16:53:06 +00:00
|
|
|
withCurrentFacet(facet, function () {
|
|
|
|
f.call(facet.fields);
|
|
|
|
});
|
2016-08-25 12:12:32 +00:00
|
|
|
facet.completeBuild();
|
|
|
|
};
|
|
|
|
|
2016-08-08 01:01:49 +00:00
|
|
|
function withCurrentFacet(facet, f) {
|
2016-08-07 19:33:09 +00:00
|
|
|
var previousFacet = Facet.current;
|
2016-05-10 04:40:53 +00:00
|
|
|
Facet.current = facet;
|
|
|
|
var result;
|
|
|
|
try {
|
|
|
|
result = f();
|
|
|
|
} catch (e) {
|
2016-08-07 19:33:09 +00:00
|
|
|
Facet.current = previousFacet;
|
2016-05-10 04:40:53 +00:00
|
|
|
throw e;
|
|
|
|
}
|
2016-08-07 19:33:09 +00:00
|
|
|
Facet.current = previousFacet;
|
2016-05-10 04:40:53 +00:00
|
|
|
return result;
|
2016-03-18 21:00:51 +00:00
|
|
|
}
|
|
|
|
|
2016-08-07 23:44:57 +00:00
|
|
|
Facet.prototype.handleEvent = function(e, isSynthetic) {
|
2016-03-18 21:00:51 +00:00
|
|
|
var facet = this;
|
2016-05-10 04:40:53 +00:00
|
|
|
facet.endpoints.forEach(function(endpoint) {
|
2016-08-07 23:44:57 +00:00
|
|
|
endpoint.handlerFn.call(facet.fields, e, isSynthetic);
|
2016-03-18 21:00:51 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Facet.prototype.addAssertion = function(assertionFn) {
|
|
|
|
return this.addEndpoint(new Endpoint(assertionFn, function(e) {}));
|
|
|
|
};
|
|
|
|
|
2016-08-07 19:33:09 +00:00
|
|
|
Facet.prototype.addOnEventHandler = function(handler, priorityOpt) {
|
2016-05-15 20:16:36 +00:00
|
|
|
var facet = this;
|
|
|
|
return this.addEndpoint(new Endpoint(function () { return Patch.emptyPatch; }, function (e) {
|
2016-08-07 19:33:09 +00:00
|
|
|
facet.actor.pushAction(function () { handler(e); }, priorityOpt);
|
2016-05-15 20:16:36 +00:00
|
|
|
}));
|
2016-05-14 23:06:00 +00:00
|
|
|
};
|
|
|
|
|
2016-08-07 19:33:09 +00:00
|
|
|
Facet.prototype.addDataflow = function(subjectFunction) {
|
|
|
|
var facet = this;
|
|
|
|
return this.addEndpoint(new Endpoint(function () {
|
|
|
|
var subjectId = facet.actor.dataflowGraph.currentSubjectId;
|
|
|
|
facet.actor.pushAction(function () {
|
|
|
|
facet.actor.dataflowGraph.withSubject(subjectId, function () {
|
|
|
|
subjectFunction.call(facet.fields);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return Patch.emptyPatch;
|
|
|
|
}, function (e) {}));
|
|
|
|
};
|
|
|
|
|
|
|
|
Facet.prototype.onEvent = function(priority,
|
|
|
|
isTerminal,
|
|
|
|
eventType,
|
|
|
|
subscriptionFn,
|
|
|
|
projectionFn,
|
|
|
|
handlerFn)
|
|
|
|
{
|
2016-03-18 21:00:51 +00:00
|
|
|
var facet = this;
|
2016-03-19 17:48:49 +00:00
|
|
|
switch (eventType) {
|
2016-03-18 21:00:51 +00:00
|
|
|
|
2016-03-19 17:48:49 +00:00
|
|
|
case 'message':
|
|
|
|
return this.addEndpoint(new Endpoint(subscriptionFn, function(e) {
|
|
|
|
if (e.type === 'message') {
|
2016-08-07 19:33:09 +00:00
|
|
|
var proj = projectionFn.call(facet.fields);
|
2016-03-19 17:48:49 +00:00
|
|
|
var spec = Patch.prependAtMeta(proj.assertion, proj.metalevel);
|
2016-05-08 15:33:39 +00:00
|
|
|
var match = Trie.matchPattern(e.message, spec);
|
2016-03-18 21:00:51 +00:00
|
|
|
// console.log(match);
|
|
|
|
if (match) {
|
|
|
|
if (isTerminal) { facet.terminate(); }
|
2016-08-07 19:33:09 +00:00
|
|
|
facet.actor.pushAction(function () { Util.kwApply(handlerFn, facet.fields, match); },
|
|
|
|
priority);
|
2016-03-18 21:00:51 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-19 17:48:49 +00:00
|
|
|
}));
|
|
|
|
|
|
|
|
case 'asserted': /* fall through */
|
|
|
|
case 'retracted':
|
2016-08-07 23:44:57 +00:00
|
|
|
return this.addEndpoint(new Endpoint(subscriptionFn, function(e, isSynthetic) {
|
2016-03-19 17:48:49 +00:00
|
|
|
if (e.type === 'stateChange') {
|
2016-08-07 19:33:09 +00:00
|
|
|
var proj = projectionFn.call(facet.fields);
|
2016-03-19 17:48:49 +00:00
|
|
|
var spec = Patch.prependAtMeta(proj.assertion, proj.metalevel);
|
2016-05-08 15:33:39 +00:00
|
|
|
var objects = Trie.projectObjects(eventType === 'asserted'
|
|
|
|
? e.patch.added
|
|
|
|
: e.patch.removed,
|
|
|
|
spec);
|
2016-03-21 01:01:17 +00:00
|
|
|
if (objects && objects.size > 0) {
|
2016-05-18 00:50:11 +00:00
|
|
|
var shouldTerminate = isTerminal;
|
|
|
|
objects.forEach(function (o) {
|
|
|
|
var instantiated = Trie.instantiateProjection(spec, o);
|
2016-08-07 23:44:57 +00:00
|
|
|
if (facet.interestWas(eventType, instantiated, isSynthetic)) {
|
2016-05-18 00:50:11 +00:00
|
|
|
if (shouldTerminate) {
|
|
|
|
shouldTerminate = false;
|
|
|
|
facet.terminate();
|
2016-05-18 00:04:37 +00:00
|
|
|
}
|
2016-05-18 00:50:11 +00:00
|
|
|
facet.actor.pushAction(function () {
|
2016-08-07 19:33:09 +00:00
|
|
|
Util.kwApply(handlerFn, facet.fields, o);
|
|
|
|
}, priority);
|
2016-05-18 00:50:11 +00:00
|
|
|
}
|
2016-05-15 20:16:36 +00:00
|
|
|
});
|
2016-03-18 21:00:51 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-19 17:48:49 +00:00
|
|
|
}));
|
|
|
|
|
|
|
|
case 'risingEdge':
|
2016-08-07 19:33:09 +00:00
|
|
|
var endpoint = new Endpoint(function() {
|
|
|
|
var newValue = subscriptionFn.call(facet.fields);
|
|
|
|
if (newValue && !this.currentValue) {
|
|
|
|
if (isTerminal) { facet.terminate(); }
|
|
|
|
facet.actor.pushAction(function () {
|
|
|
|
handlerFn.call(facet.fields);
|
|
|
|
}, priority);
|
|
|
|
}
|
|
|
|
this.currentValue = newValue;
|
|
|
|
return Patch.emptyPatch;
|
|
|
|
}, function(e) {});
|
2016-03-19 17:48:49 +00:00
|
|
|
endpoint.currentValue = false;
|
|
|
|
return this.addEndpoint(endpoint);
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new Error("Unsupported Facet eventType: " + eventType);
|
|
|
|
}
|
2016-03-18 21:00:51 +00:00
|
|
|
};
|
|
|
|
|
2016-08-07 23:44:57 +00:00
|
|
|
Facet.prototype.interestWas = function(assertedOrRetracted, pat, isSyntheticEvent) {
|
2016-05-18 00:04:37 +00:00
|
|
|
function orStar(a, b) { return (a || b); }
|
2016-08-07 23:44:57 +00:00
|
|
|
var previousKnowledge = isSyntheticEvent ? Trie.emptyTrie : this.actor.previousKnowledge;
|
|
|
|
var oldExists = Trie.matchValue(previousKnowledge, pat, false, orStar);
|
2016-05-18 00:04:37 +00:00
|
|
|
var newExists = Trie.matchValue(this.actor.knowledge, pat, false, orStar);
|
|
|
|
switch (assertedOrRetracted) {
|
|
|
|
case 'asserted':
|
|
|
|
return !oldExists && newExists;
|
|
|
|
case 'retracted':
|
|
|
|
return oldExists && !newExists;
|
|
|
|
default:
|
|
|
|
throw new Error("Unexpected assertedOrRetracted in Facet.interestWas: " + assertedOrRetracted);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-18 21:00:51 +00:00
|
|
|
Facet.prototype.addEndpoint = function(endpoint) {
|
2016-08-07 19:33:09 +00:00
|
|
|
var facet = this;
|
|
|
|
var patch = facet.actor.dataflowGraph.withSubject([facet, endpoint], function () {
|
|
|
|
return endpoint.subscriptionFn.call(facet.fields);
|
|
|
|
});
|
|
|
|
var r = facet.actor.mux.addStream(patch);
|
|
|
|
endpoint.eid = r.pid;
|
|
|
|
facet.endpoints = facet.endpoints.set(endpoint.eid, endpoint);
|
2016-04-07 07:42:54 +00:00
|
|
|
Dataspace.stateChange(r.deltaAggregate);
|
2016-08-07 19:33:09 +00:00
|
|
|
return facet; // for chaining
|
2016-03-18 21:00:51 +00:00
|
|
|
};
|
|
|
|
|
2016-03-18 21:13:52 +00:00
|
|
|
Facet.prototype.addInitBlock = function(thunk) {
|
|
|
|
this.initBlocks = this.initBlocks.push(thunk);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
Facet.prototype.addDoneBlock = function(thunk) {
|
|
|
|
this.doneBlocks = this.doneBlocks.push(thunk);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2016-03-18 21:00:51 +00:00
|
|
|
Facet.prototype.completeBuild = function() {
|
2016-03-18 21:13:52 +00:00
|
|
|
var facet = this;
|
2016-03-18 21:00:51 +00:00
|
|
|
this.actor.addFacet(this);
|
2016-05-10 04:40:53 +00:00
|
|
|
if (this.parent) {
|
|
|
|
this.parent.children = this.parent.children.add(this);
|
|
|
|
}
|
2016-08-08 01:01:49 +00:00
|
|
|
withCurrentFacet(facet, function () {
|
2016-08-07 19:33:09 +00:00
|
|
|
facet.initBlocks.forEach(function(b) { b.call(facet.fields); });
|
2016-05-10 21:02:31 +00:00
|
|
|
});
|
2016-05-15 18:55:48 +00:00
|
|
|
var initialEvent = _Dataspace.stateChange(new Patch.Patch(facet.actor.knowledge, Trie.emptyTrie));
|
2016-08-08 01:01:49 +00:00
|
|
|
withCurrentFacet(facet, function () { facet.handleEvent(initialEvent, true); });
|
2016-03-18 21:00:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Facet.prototype.terminate = function() {
|
|
|
|
var facet = this;
|
2016-05-18 00:04:37 +00:00
|
|
|
|
|
|
|
if (facet.terminated) return;
|
|
|
|
facet.terminated = true;
|
|
|
|
|
2016-03-18 21:00:51 +00:00
|
|
|
var aggregate = Patch.emptyPatch;
|
|
|
|
this.endpoints.forEach(function(endpoint, eid) {
|
|
|
|
var r = facet.actor.mux.removeStream(eid);
|
|
|
|
aggregate = aggregate.andThen(r.deltaAggregate);
|
|
|
|
});
|
2016-04-07 07:42:54 +00:00
|
|
|
Dataspace.stateChange(aggregate);
|
2016-05-18 00:04:37 +00:00
|
|
|
|
2016-03-18 21:00:51 +00:00
|
|
|
this.endpoints = Immutable.Map();
|
2016-05-10 04:40:53 +00:00
|
|
|
if (this.parent) {
|
|
|
|
this.parent.children = this.parent.children.remove(this);
|
|
|
|
}
|
2016-05-18 00:04:37 +00:00
|
|
|
|
2016-03-18 21:08:49 +00:00
|
|
|
this.actor.removeFacet(this);
|
2016-05-18 00:04:37 +00:00
|
|
|
|
2016-05-10 04:40:53 +00:00
|
|
|
this.children.forEach(function (child) {
|
|
|
|
child.terminate();
|
|
|
|
});
|
2016-08-07 19:33:09 +00:00
|
|
|
|
2016-08-08 01:01:49 +00:00
|
|
|
withCurrentFacet(facet, function () {
|
2016-08-07 19:33:09 +00:00
|
|
|
facet.doneBlocks.forEach(function(b) { b.call(facet.fields); });
|
|
|
|
});
|
2016-03-18 21:00:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
function Endpoint(subscriptionFn, handlerFn) {
|
|
|
|
this.subscriptionFn = subscriptionFn;
|
|
|
|
this.handlerFn = handlerFn;
|
2016-08-07 19:33:09 +00:00
|
|
|
this.eid = 'uninitialized_eid'; // initialized later
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
function referenceField(obj, prop) {
|
|
|
|
if (!(prop in obj)) {
|
2016-08-08 01:01:49 +00:00
|
|
|
Dataspace.activeBehavior().dataflowGraph.recordObservation(Immutable.List.of(obj, prop));
|
2016-08-07 19:33:09 +00:00
|
|
|
}
|
|
|
|
return obj[prop];
|
|
|
|
}
|
|
|
|
|
|
|
|
function declareField(obj, prop, init) {
|
|
|
|
if (prop in obj) {
|
|
|
|
obj[prop] = init;
|
|
|
|
} else {
|
2016-08-08 01:01:49 +00:00
|
|
|
Dataspace.activeBehavior().dataflowGraph.defineObservableProperty(obj, prop, init, {
|
2016-08-07 19:33:09 +00:00
|
|
|
objectId: Immutable.List.of(obj, prop)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function deleteField(obj, prop) {
|
2016-08-08 01:01:49 +00:00
|
|
|
Dataspace.activeBehavior().dataflowGraph.recordDamage(Immutable.List.of(obj, prop));
|
2016-08-07 19:33:09 +00:00
|
|
|
return delete obj[prop];
|
2016-03-18 21:00:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
module.exports.spawnActor = spawnActor;
|
2016-08-25 12:12:32 +00:00
|
|
|
module.exports.Facet = Facet;
|
2016-08-07 19:33:09 +00:00
|
|
|
module.exports.referenceField = referenceField;
|
|
|
|
module.exports.declareField = declareField;
|
|
|
|
module.exports.deleteField = deleteField;
|