sockethub 4.0.0 → 5.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +3 -3
  2. package/bin/sockethub +23 -19
  3. package/coverage/tmp/coverage-15699-1646422276150-0.json +1 -0
  4. package/dist/bootstrap/init.js +14 -4
  5. package/dist/bootstrap/init.js.map +1 -1
  6. package/dist/bootstrap/platforms.js +81 -69
  7. package/dist/common.js +10 -12
  8. package/dist/common.js.map +1 -1
  9. package/dist/config.js +4 -22
  10. package/dist/config.js.map +1 -1
  11. package/dist/crypto.js +7 -8
  12. package/dist/crypto.js.map +1 -1
  13. package/dist/janitor.js +14 -9
  14. package/dist/janitor.js.map +1 -1
  15. package/dist/middleware/create-activity-object.js +19 -0
  16. package/dist/middleware/create-activity-object.js.map +1 -0
  17. package/dist/middleware/expand-activity-stream.js +33 -0
  18. package/dist/middleware/expand-activity-stream.js.map +1 -0
  19. package/dist/middleware/expand-activity-stream.test.data.js +360 -0
  20. package/dist/middleware/expand-activity-stream.test.data.js.map +1 -0
  21. package/dist/middleware/store-credentials.js +19 -0
  22. package/dist/middleware/store-credentials.js.map +1 -0
  23. package/dist/middleware/validate.js +77 -0
  24. package/dist/middleware/validate.js.map +1 -0
  25. package/dist/middleware/validate.test.data.js +321 -0
  26. package/dist/middleware/validate.test.data.js.map +1 -0
  27. package/dist/middleware.js +47 -49
  28. package/dist/middleware.js.map +1 -1
  29. package/dist/platform-instance.js +84 -66
  30. package/dist/platform-instance.js.map +1 -1
  31. package/dist/platform.js +50 -25
  32. package/dist/platform.js.map +1 -1
  33. package/dist/process-manager.js +7 -4
  34. package/dist/process-manager.js.map +1 -1
  35. package/dist/routes.js +9 -7
  36. package/dist/routes.js.map +1 -1
  37. package/dist/serve.js +3 -3
  38. package/dist/serve.js.map +1 -1
  39. package/dist/sockethub-client.js +2604 -0
  40. package/dist/sockethub-client.js.map +1 -0
  41. package/dist/sockethub-client.min.js +2 -0
  42. package/dist/sockethub-client.min.js.LICENSE.txt +24 -0
  43. package/dist/sockethub.js +75 -58
  44. package/dist/sockethub.js.map +1 -1
  45. package/dist/store.js +17 -0
  46. package/dist/store.js.map +1 -0
  47. package/package.json +48 -44
  48. package/src/bootstrap/init.ts +16 -2
  49. package/src/bootstrap/platforms.js +14 -18
  50. package/src/common.test.ts +44 -33
  51. package/src/common.ts +9 -17
  52. package/src/config.test.ts +16 -38
  53. package/src/config.ts +1 -23
  54. package/src/crypto.test.ts +15 -17
  55. package/src/crypto.ts +4 -5
  56. package/src/janitor.ts +19 -12
  57. package/src/middleware/create-activity-object.test.ts +10 -0
  58. package/src/middleware/create-activity-object.ts +13 -0
  59. package/src/middleware/expand-activity-stream.test.data.ts +365 -0
  60. package/src/middleware/expand-activity-stream.test.ts +78 -0
  61. package/src/middleware/expand-activity-stream.ts +27 -0
  62. package/src/middleware/store-credentials.test.ts +72 -0
  63. package/src/middleware/store-credentials.ts +16 -0
  64. package/src/{validate.d.ts → middleware/validate.d.ts} +0 -0
  65. package/src/middleware/validate.test.data.ts +320 -0
  66. package/src/middleware/validate.test.ts +47 -0
  67. package/src/middleware/validate.ts +49 -0
  68. package/src/middleware.test.ts +148 -0
  69. package/src/middleware.ts +46 -51
  70. package/src/platform-instance.test.ts +224 -196
  71. package/src/platform-instance.ts +74 -58
  72. package/src/platform.ts +44 -24
  73. package/src/process-manager.ts +7 -4
  74. package/src/routes.test.ts +32 -17
  75. package/src/routes.ts +8 -6
  76. package/src/serve.ts +1 -1
  77. package/src/sockethub-client.test.ts +235 -0
  78. package/src/sockethub-client.ts +164 -0
  79. package/src/sockethub.ts +96 -93
  80. package/src/store.test.ts +26 -0
  81. package/src/store.ts +17 -0
  82. package/tsconfig.json +8 -8
  83. package/views/examples/dummy.ejs +7 -7
  84. package/views/examples/feeds.ejs +10 -10
  85. package/views/examples/irc.ejs +65 -59
  86. package/views/examples/shared.js +31 -29
  87. package/views/examples/xmpp.ejs +49 -58
  88. package/webpack.minified.config.js +14 -0
  89. package/webpack.normal.config.js +14 -0
  90. package/coverage/clover.xml +0 -190
  91. package/coverage/coverage-final.json +0 -6
  92. package/coverage/lcov-report/base.css +0 -224
  93. package/coverage/lcov-report/block-navigation.js +0 -79
  94. package/coverage/lcov-report/common.ts.html +0 -143
  95. package/coverage/lcov-report/config.ts.html +0 -359
  96. package/coverage/lcov-report/crypto.ts.html +0 -203
  97. package/coverage/lcov-report/favicon.png +0 -0
  98. package/coverage/lcov-report/index.html +0 -171
  99. package/coverage/lcov-report/platform-instance.ts.html +0 -740
  100. package/coverage/lcov-report/prettify.css +0 -1
  101. package/coverage/lcov-report/prettify.js +0 -2
  102. package/coverage/lcov-report/routes.ts.html +0 -353
  103. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  104. package/coverage/lcov-report/sorter.js +0 -170
  105. package/coverage/lcov-report/src/common.ts.html +0 -143
  106. package/coverage/lcov-report/src/config.ts.html +0 -359
  107. package/coverage/lcov-report/src/crypto.ts.html +0 -182
  108. package/coverage/lcov-report/src/index.html +0 -156
  109. package/coverage/lcov-report/src/platform-instance.ts.html +0 -740
  110. package/coverage/lcov-report/src/routes/base.ts.html +0 -359
  111. package/coverage/lcov-report/src/routes/examples.ts.html +0 -311
  112. package/coverage/lcov-report/src/routes/index.html +0 -111
  113. package/coverage/lcov-report/src/services/http.ts.html +0 -239
  114. package/coverage/lcov-report/src/services/index.html +0 -111
  115. package/coverage/lcov-report/src/services/redis.ts.html +0 -140
  116. package/coverage/lcov-report/src/shared-resources.ts.html +0 -104
  117. package/coverage/lcov.info +0 -336
  118. package/coverage/tmp/coverage-70996-1620314182345-0.json +0 -1
  119. package/dist/bootstrap/platforms.js.map +0 -1
  120. package/dist/js/client.js +0 -177
  121. package/dist/js/client.js.map +0 -1
  122. package/dist/resource-manager.js +0 -66
  123. package/dist/resource-manager.js.map +0 -1
  124. package/dist/routes/base.js +0 -92
  125. package/dist/routes/base.js.map +0 -1
  126. package/dist/routes/examples.js +0 -93
  127. package/dist/routes/examples.js.map +0 -1
  128. package/dist/services/http.js +0 -68
  129. package/dist/services/http.js.map +0 -1
  130. package/dist/services/redis.js +0 -1
  131. package/dist/services/redis.js.map +0 -1
  132. package/dist/shared-resources.js +0 -11
  133. package/dist/shared-resources.js.map +0 -1
  134. package/dist/validate.js +0 -157
  135. package/dist/validate.js.map +0 -1
  136. package/jest.config.js +0 -18
  137. package/src/js/client.js +0 -190
  138. package/src/validate.ts +0 -147
  139. package/test/middleware-suite.js +0 -101
  140. package/test/validate-suite.js +0 -338
@@ -1,43 +1,54 @@
1
- import { getPlatformId, getSessionStore } from './common';
1
+ import proxyquire from 'proxyquire';
2
+ import { expect } from 'chai';
3
+ import * as sinon from 'sinon';
4
+
5
+ import { decryptJobData, getPlatformId } from './common';
2
6
  import crypto from './crypto';
3
- import Store from 'secure-store-redis';
4
-
5
- jest.mock('./config');
6
- jest.mock('secure-store-redis');
7
- jest.mock('./crypto', () => ({
8
- __esModule: true,
9
- default: {
10
- encrypt: jest.fn().mockImplementation(() => {
11
- console.log("encrypt called");
12
- }),
13
- decrypt: jest.fn().mockImplementation(() => {
14
- console.log("decrypt called");
15
- }),
16
- hash: jest.fn().mockImplementation((string) => {
17
- return string;
18
- }),
19
- }
20
- }));
7
+
8
+ proxyquire.noPreserveCache();
9
+ proxyquire.noCallThru();
21
10
 
22
11
  describe("getPlatformId", () => {
12
+ let cryptoHashStub;
13
+
14
+ beforeEach(() => {
15
+ cryptoHashStub = sinon.stub(crypto, 'hash');
16
+ cryptoHashStub.returnsArg(0);
17
+ proxyquire('./common', { crypto: { hash: cryptoHashStub }});
18
+ });
19
+
20
+ afterEach(() => {
21
+ cryptoHashStub.restore();
22
+ });
23
+
23
24
  it('generates platform hash', () => {
24
- expect(getPlatformId('foo')).toBe('foo');
25
- expect(crypto.hash).toBeCalledWith('foo');
25
+ expect(getPlatformId('foo')).to.be.equal('foo');
26
+ sinon.assert.calledOnce(cryptoHashStub);
27
+ sinon.assert.calledWith(cryptoHashStub, 'foo');
26
28
  });
27
29
  it('generates platform + actor hash', () => {
28
- expect(getPlatformId('foo', 'bar')).toBe('foobar');
29
- expect(crypto.hash).toBeCalledWith('foobar');
30
+ expect(getPlatformId('foo', 'bar')).to.be.equal('foobar');
31
+ sinon.assert.calledOnce(cryptoHashStub);
32
+ sinon.assert.calledWith(cryptoHashStub, 'foobar');
30
33
  });
31
34
  });
32
35
 
33
- describe('getSessionStore', () => {
34
- it('returns a valid Store object', () => {
35
- getSessionStore('a parent id', 'a parent secret',
36
- 'a session id', 'a session secret');
37
- expect(Store).toBeCalledWith({
38
- namespace: 'sockethub:a parent id:session:a session id:store',
39
- secret: 'a parent secreta session secret',
40
- redis: undefined
41
- });
36
+ describe("decryptJobData", () => {
37
+ let cryptoDecryptStub;
38
+
39
+ beforeEach(() => {
40
+ cryptoDecryptStub = sinon.stub(crypto, 'decrypt');
41
+ cryptoDecryptStub.returnsArg(0);
42
+ proxyquire('./common', { crypto: { hash: cryptoDecryptStub }});
43
+ });
44
+
45
+ afterEach(() => {
46
+ cryptoDecryptStub.restore();
42
47
  });
43
- });
48
+
49
+ it("decrypts and returns expected object", () => {
50
+ const jobData = {data:{title:"foo", msg:'encryptedjobdata', sessionId:'foobar'}};
51
+ const secret = 'secretstring';
52
+ expect(decryptJobData(jobData, secret)).to.be.eql(jobData.data);
53
+ });
54
+ });
package/src/common.ts CHANGED
@@ -1,22 +1,14 @@
1
- import Store from 'secure-store-redis';
2
-
3
- import config from "./config";
4
1
  import crypto from "./crypto";
5
-
6
- export interface Store {
7
- save(id: string, obj: any, cb: Function);
8
- get(id: string, cb: Function);
9
- }
10
-
11
- export function getSessionStore(parentId: string, parentSecret: string,
12
- sessionId: string, sessionSecret: string): Store {
13
- return new Store({
14
- namespace: 'sockethub:' + parentId + ':session:' + sessionId + ':store',
15
- secret: parentSecret + sessionSecret,
16
- redis: config.get('redis')
17
- });
18
- }
2
+ import { JobDataDecrypted, JobEncrypted } from "./sockethub";
19
3
 
20
4
  export function getPlatformId(platform: string, actor?: string): string {
21
5
  return actor ? crypto.hash(platform + actor) : crypto.hash(platform);
6
+ }
7
+
8
+ export function decryptJobData(job: JobEncrypted, secret: string): JobDataDecrypted {
9
+ return {
10
+ title: job.data.title,
11
+ msg: crypto.decrypt(job.data.msg, secret),
12
+ sessionId: job.data.sessionId
13
+ };
22
14
  }
@@ -1,50 +1,28 @@
1
- import { redisConfig } from "./config";
2
-
3
- import Redis from 'ioredis';
4
-
5
- jest.mock('ioredis');
1
+ import { expect } from 'chai';
6
2
 
3
+ import { Config } from './config';
7
4
 
8
5
  describe('config', () => {
9
- beforeEach(() => {
10
- jest.resetModules();
6
+
7
+ it('loads default values', () => {
8
+ const config = new Config();
9
+ expect(config).to.have.property('get');
10
+ expect(config.get('service:host')).to.eql('localhost');
11
11
  });
12
12
 
13
- it('prioritizes redis url', () => {
14
- process.env = { REDIS_URL: 'a redis url' };
15
- const nconf = require('nconf');
16
- const config = require('./config');
17
- expect(config.default).toHaveProperty('get');
18
- expect(config.default.get('redis')).toStrictEqual(
19
- {"host": "127.0.0.1", "port": 6379, "url": "a redis url"});
13
+ it('overrides from env', () => {
14
+ const hostname = 'a host string';
15
+ process.env = { HOST: hostname };
16
+ const config = new Config();
17
+ expect(config).to.have.property('get');
18
+ expect(config.get('service:host')).to.eql(hostname);
20
19
  });
21
20
 
22
21
  it('defaults to redis config', () => {
23
22
  process.env = { REDIS_URL: '' };
24
- const nconf = require('nconf');
25
- const config = require('./config');
26
- expect(config.default).toHaveProperty('get');
27
- expect(config.default.get('redis')).toStrictEqual(
23
+ const config = new Config();
24
+ expect(config).to.have.property('get');
25
+ expect(config.get('redis')).to.eql(
28
26
  {"host": "127.0.0.1", "port": 6379});
29
27
  });
30
-
31
- it('has a redisConfig object', () => {
32
- expect(redisConfig).toHaveProperty('createClient');
33
- expect(Redis).toHaveBeenCalledTimes(2);
34
- })
35
-
36
- it('returns existing client', () => {
37
- const client1 = redisConfig.createClient('client');
38
- expect(Redis).toHaveBeenCalledTimes(2);
39
- });
40
-
41
- it('returns existing subscriber', () => {
42
- const client1 = redisConfig.createClient('subscriber');
43
- expect(Redis).toHaveBeenCalledTimes(2);
44
- });
45
-
46
- it('returns new client otherwise', () => {
47
- const client1 = redisConfig.createClient('foo');
48
- expect(Redis).toHaveBeenCalledTimes(3);
49
- });
50
28
  });
package/src/config.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import nconf from 'nconf';
2
2
  import { debug } from 'debug';
3
- import Redis from 'ioredis';
4
3
 
5
4
  const log = debug('sockethub:bootstrap:config');
6
5
 
7
- class Config {
6
+ export class Config {
8
7
  constructor() {
9
8
  log('initializing config');
10
9
  // assign config loading priorities (command-line, environment, cfg, defaults)
@@ -70,25 +69,4 @@ class Config {
70
69
  }
71
70
 
72
71
  const config = new Config();
73
-
74
- /**
75
- * config object that can be passed in to ioredis to make use of existing connections
76
- * https://github.com/OptimalBits/bull/blob/master/PATTERNS.md#reusing-redis-connections
77
- */
78
- const client = new Redis(config.get('redis'));
79
- const subscriber = new Redis(config.get('redis'));
80
-
81
- export const redisConfig = {
82
- createClient: function (type) {
83
- switch (type) {
84
- case 'client':
85
- return client;
86
- case 'subscriber':
87
- return subscriber;
88
- default:
89
- return new Redis(config.get('redis'));
90
- }
91
- }
92
- };
93
-
94
72
  export default config;
@@ -1,43 +1,41 @@
1
- import crypto from './crypto';
1
+ import { expect } from 'chai';
2
+ import proxyquire from 'proxyquire';
3
+
4
+ const crypto = proxyquire('./crypto', {
5
+ 'crypto': {
6
+ randomBytes: () => Buffer.alloc(16)
7
+ }
8
+ }).default;
2
9
 
3
10
  const secret = 'a test secret.. that is 16 x 2..';
4
11
  const data = {'foo': 'bar'};
5
12
  const encryptedData = "00000000000000000000000000000000:0543ec94d863fbf4b7a19b48e69d9317";
6
13
 
7
- jest.mock('crypto', () => {
8
- const originalModule = jest.requireActual('crypto');
9
- return {
10
- __esModule: true,
11
- ...originalModule,
12
- randomBytes: jest.fn(() => Buffer.alloc(16)),
13
- };
14
- });
15
-
16
14
  describe('crypto', () => {
17
15
  it('encrypts', () => {
18
- expect(crypto.encrypt(data, secret)).toBe(encryptedData);
16
+ expect(crypto.encrypt(data, secret)).to.be.equal(encryptedData);
19
17
  });
20
18
  it('decrypts', () => {
21
- expect(crypto.decrypt(encryptedData, secret)).toStrictEqual(data);
19
+ expect(crypto.decrypt(encryptedData, secret)).to.eql(data);
22
20
  });
23
21
  it('hashes', () => {
24
- expect(crypto.hash('foobar')).toBe('8843d7f');
22
+ expect(crypto.hash('foobar')).to.be.equal('8843d7f');
25
23
  });
26
24
  it('randTokens 8', () => {
27
25
  const token = crypto.randToken(8);
28
- expect(token.length).toBe(8);
26
+ expect(token.length).to.be.equal(8);
29
27
  });
30
28
  it('randTokens 16', () => {
31
29
  const token = crypto.randToken(16);
32
- expect(token.length).toBe(16);
30
+ expect(token.length).to.be.equal(16);
33
31
  });
34
32
  it('randTokens 32', () => {
35
33
  const token = crypto.randToken(32);
36
- expect(token.length).toBe(32);
34
+ expect(token.length).to.be.equal(32);
37
35
  });
38
36
  it('randTokens 33+ will fail', () => {
39
37
  expect(() => {
40
38
  crypto.randToken(33);
41
- }).toThrowError();
39
+ }).to.throw();
42
40
  });
43
41
  });
package/src/crypto.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  import { randomBytes, createCipheriv, createDecipheriv, createHash } from 'crypto';
2
+ import { ActivityStream } from "./sockethub";
2
3
 
3
4
  const ALGORITHM = 'aes-256-cbc',
4
5
  IV_LENGTH = 16; // For AES, this is always 16
5
6
 
6
- let crypto;
7
-
8
7
  class Crypto {
9
8
  constructor() {}
10
- encrypt(json: object, secret: string): string {
9
+ encrypt(json: ActivityStream, secret: string): string {
11
10
  const iv = randomBytes(IV_LENGTH);
12
11
  const cipher = createCipheriv(ALGORITHM, Buffer.from(secret), iv);
13
12
  let encrypted = cipher.update(JSON.stringify(json));
@@ -15,7 +14,7 @@ class Crypto {
15
14
  encrypted = Buffer.concat([encrypted, cipher.final()]);
16
15
  return iv.toString('hex') + ':' + encrypted.toString('hex');
17
16
  }
18
- decrypt(text: string, secret: string): object {
17
+ decrypt(text: string, secret: string): ActivityStream {
19
18
  let parts = text.split(':');
20
19
  const iv = Buffer.from(parts.shift(), 'hex');
21
20
  const encryptedText = Buffer.from(parts.join(':'), 'hex');
@@ -38,5 +37,5 @@ class Crypto {
38
37
  }
39
38
  }
40
39
 
41
- crypto = new Crypto();
40
+ const crypto = new Crypto();
42
41
  export default crypto;
package/src/janitor.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import debug from 'debug';
2
2
 
3
- import { platformInstances } from './platform-instance';
4
- import serve from "./serve";
3
+ import PlatformInstance, { platformInstances } from './platform-instance';
4
+ import serve, { SocketInstance } from "./serve";
5
5
 
6
6
  const rmLog = debug('sockethub:janitor');
7
7
 
@@ -23,15 +23,15 @@ function janitorCycle() {
23
23
  rmLog('initializing resource manager');
24
24
  setInterval(async () => {
25
25
  cycleCount++;
26
- const sockets = await serve.io.fetchSockets();
26
+ const sockets: Array<SocketInstance> = await serve.io.fetchSockets();
27
27
 
28
28
  if (! (cycleCount % 4)) {
29
29
  reportCount++;
30
- rmLog(`sessions: ${sockets.length} instances: ${platformInstances.size}`);
30
+ rmLog(`socket sessions: ${sockets.length} platform instances: ${platformInstances.size}`);
31
31
  }
32
32
 
33
33
  for (let platformInstance of platformInstances.values()) {
34
- removeStaleSessions(platformInstance, sockets);
34
+ removeStaleSocketSessions(platformInstance, sockets);
35
35
  // Static platforms are for global use, not tied to a unique to session / eg. credentials)
36
36
  if ((! platformInstance.global) && (platformInstance.sessions.size === 0)) {
37
37
  removeStalePlatformInstance(platformInstance);
@@ -42,7 +42,7 @@ function janitorCycle() {
42
42
  }, TICK);
43
43
  }
44
44
 
45
- function socketExists(sessionId, sockets) {
45
+ function socketExists(sessionId: string, sockets: Array<SocketInstance>) {
46
46
  for (let socket of sockets) {
47
47
  if (socket.id === sessionId) {
48
48
  return true;
@@ -51,20 +51,27 @@ function socketExists(sessionId, sockets) {
51
51
  return false;
52
52
  }
53
53
 
54
- function removeStaleSessions(platformInstance, sockets) {
55
- for (let sessionId of platformInstance.sessions.values()) {
54
+ function removeSessionCallbacks(platformInstance: PlatformInstance, sessionId: string) {
55
+ for (const key in platformInstance.sessionCallbacks) {
56
+ platformInstance.sessionCallbacks[key].delete(sessionId);
57
+ }
58
+ }
59
+
60
+ function removeStaleSocketSessions(platformInstance: PlatformInstance,
61
+ sockets: Array<SocketInstance>) {
62
+ for (const sessionId of platformInstance.sessions.values()) {
56
63
  if (! socketExists(sessionId, sockets)) {
57
- rmLog('removing stale session reference ' + sessionId + ' in platform instance '
64
+ rmLog('removing stale socket session reference ' + sessionId + ' in platform instance '
58
65
  + platformInstance.id);
59
66
  platformInstance.sessions.delete(sessionId);
67
+ removeSessionCallbacks(platformInstance, sessionId);
60
68
  }
61
69
  }
62
70
  }
63
71
 
64
- function removeStalePlatformInstance(platformInstance) {
72
+ function removeStalePlatformInstance(platformInstance: PlatformInstance) {
65
73
  if (platformInstance.flaggedForTermination) {
66
- rmLog(`terminating platform instance ${platformInstance.id} ` +
67
- `(flagged for termination: no registered sessions found)`);
74
+ rmLog(`terminating platform instance ${platformInstance.id}`);
68
75
  platformInstance.destroy(); // terminate
69
76
  } else {
70
77
  rmLog(`flagging for termination platform instance ${platformInstance.id} ` +
@@ -0,0 +1,10 @@
1
+ import createActivityObject from "./create-activity-object";
2
+
3
+ describe('Middleware: createActivityObject', () => {
4
+ it('Calls activity.Object.create with incoming data', (done) => {
5
+ // @ts-ignore
6
+ createActivityObject({foo: 'bar'}, (msg) => {
7
+ done();
8
+ });
9
+ });
10
+ });
@@ -0,0 +1,13 @@
1
+ import ActivityStreams from 'activity-streams';
2
+ import config from "../config";
3
+ const activity = ActivityStreams(config.get('activity-streams:opts'));
4
+
5
+ /**
6
+ * A simple middleware wrapper for the activity-streams Object.create method.
7
+ * @param obj
8
+ * @param done
9
+ */
10
+ export default function createActivityObject(obj: any, done: Function) {
11
+ activity.Object.create(obj);
12
+ done(obj);
13
+ }