ironflock 0.0.6

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.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # ironflock
2
+
3
+ ## About
4
+
5
+ With this library you can publishing data to IronFlock fleet storage.
6
+ When this library is used on a certain device the library automatically uses the private messaging realm (Unified Name Space)
7
+ of the device's fleet and the data is collected in the respective fleet database.
8
+
9
+ So if you use the library in your app, the data collection will always be private to the app user's fleet.
10
+
11
+ ## Usage
12
+
13
+ **CommonJS**
14
+
15
+ ```
16
+ const IronFlock = require("ironflock")
17
+ const ironflock = new IronFlock()
18
+
19
+ ironflock.publish("re.example.rpc", [{ example_data: "test" }]) // any type of data is publishable
20
+ ```
21
+
22
+ **ES6**
23
+
24
+ For ES6 modules `"type": "module"` is required in your package.json
25
+
26
+ ```
27
+ import IronFlock from "ironflock"
28
+ const ironflock = new IronFlock()
29
+
30
+ ironflock.publish("re.example.rpc", [{ example_data: "test" }]) // any type of data is publishable
31
+ ```
32
+
33
+ ## Options
34
+
35
+ The `IronFlock` constructor can be configured with the following options:
36
+
37
+ ```
38
+ {
39
+ quiet?: boolean;
40
+ serialNumber?: string;
41
+ }
42
+ ```
43
+
44
+ **quiet**: Toggles autobahn.js debug information
45
+
46
+ **serialNumber**: Used to set the serial_number of the device if the `DEVICE_SERIAL_NUMBER` environment variable does not exist. It can also be used if the user wishes to authenticate as another device.
@@ -0,0 +1,38 @@
1
+ /// <reference types="when" />
2
+ /// <reference types="node" />
3
+ import autobahn from "autobahn";
4
+ import { EventEmitter } from "stream";
5
+ export type Details = {
6
+ caller_authid: string;
7
+ caller_authrole: string;
8
+ };
9
+ export type AuthPayload = {
10
+ success: boolean;
11
+ role: string;
12
+ realm: string;
13
+ authid: string;
14
+ extra: {
15
+ username: string;
16
+ };
17
+ };
18
+ export declare class CrossbarConnection extends EventEmitter {
19
+ session?: autobahn.Session;
20
+ connection?: autobahn.Connection;
21
+ serial_number: string;
22
+ socketURI: string;
23
+ realm: string;
24
+ private reconnectTimeout?;
25
+ constructor(serial_number: string);
26
+ private init;
27
+ getSession(): autobahn.Session | undefined;
28
+ isConnected(): boolean | undefined;
29
+ /**
30
+ *
31
+ * @param {string} procedure
32
+ * @param {any | undefined} args
33
+ * @param {any | undefined} kwargs
34
+ * @param {autobahn.ICallOptions} options
35
+ */
36
+ call<T>(procedure: string, args?: any[], kwargs?: object, options?: any): When.Promise<T> | undefined;
37
+ publish(topic: string, args?: any[], kwargs?: object, options?: any): When.Promise<autobahn.IPublication> | undefined;
38
+ }
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
+ return new (P || (P = Promise))(function (resolve, reject) {
20
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
24
+ });
25
+ };
26
+ var __generator = (this && this.__generator) || function (thisArg, body) {
27
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
28
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
+ function verb(n) { return function (v) { return step([n, v]); }; }
30
+ function step(op) {
31
+ if (f) throw new TypeError("Generator is already executing.");
32
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
33
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
34
+ if (y = 0, t) op = [op[0] & 2, t.value];
35
+ switch (op[0]) {
36
+ case 0: case 1: t = op; break;
37
+ case 4: _.label++; return { value: op[1], done: false };
38
+ case 5: _.label++; y = op[1]; op = [0]; continue;
39
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
+ default:
41
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
+ if (t[2]) _.ops.pop();
46
+ _.trys.pop(); continue;
47
+ }
48
+ op = body.call(thisArg, _);
49
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
+ }
52
+ };
53
+ var __importDefault = (this && this.__importDefault) || function (mod) {
54
+ return (mod && mod.__esModule) ? mod : { "default": mod };
55
+ };
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ exports.CrossbarConnection = void 0;
58
+ var autobahn_1 = __importDefault(require("autobahn"));
59
+ var stream_1 = require("stream");
60
+ // import { MsgpackSerializer } from "./serializer";
61
+ var RESWARM_URL = process.env.RESWARM_URL;
62
+ var DATAPODS_WS_URI = "ws://devices.datapods.io:8088/ws";
63
+ var STUDIO_WS_URI = "ws://devices.record-evolution.com:8088/ws";
64
+ var LOCALHOST_WS_URI = "ws://reswarm-crossbar:8088";
65
+ var socketURIMap = {
66
+ "https://studio.datapods.io": DATAPODS_WS_URI,
67
+ "https://studio.record-evolution.com": STUDIO_WS_URI,
68
+ "http://localhost:8085": LOCALHOST_WS_URI
69
+ };
70
+ var CrossbarConnection = /** @class */ (function (_super) {
71
+ __extends(CrossbarConnection, _super);
72
+ function CrossbarConnection(serial_number) {
73
+ var _this = _super.call(this) || this;
74
+ _this.serial_number = serial_number;
75
+ _this.realm = "userapps";
76
+ _this.socketURI = RESWARM_URL ? socketURIMap[RESWARM_URL] : DATAPODS_WS_URI;
77
+ _this.init();
78
+ return _this;
79
+ }
80
+ CrossbarConnection.prototype.init = function () {
81
+ var _this = this;
82
+ this.connection = new autobahn_1.default.Connection({
83
+ realm: this.realm,
84
+ url: this.socketURI,
85
+ authmethods: ["wampcra"],
86
+ authid: this.serial_number,
87
+ max_retries: -1,
88
+ max_retry_delay: 5,
89
+ initial_retry_delay: 1,
90
+ // @ts-ignore
91
+ // serializers: [new MsgpackSerializer()],
92
+ // @ts-ignore
93
+ autoping_interval: 2,
94
+ autoping_timeout: 4,
95
+ onchallenge: function (session, method, extra) {
96
+ return autobahn_1.default.auth_cra.sign(_this.serial_number, extra.challenge);
97
+ },
98
+ });
99
+ this.connection.onopen = function (session, details) { return __awaiter(_this, void 0, void 0, function () {
100
+ return __generator(this, function (_a) {
101
+ this.session = session;
102
+ this.session.caller_disclose_me = true;
103
+ this.emit('connected', this.serial_number);
104
+ return [2 /*return*/];
105
+ });
106
+ }); };
107
+ this.connection.onclose = function (reason, details) {
108
+ _this.emit('disconnected');
109
+ console.log("connection closed: ", reason, details);
110
+ console.log("retrying in 1 second..");
111
+ if (_this.reconnectTimeout)
112
+ clearTimeout(_this.reconnectTimeout);
113
+ _this.reconnectTimeout = setTimeout(function () {
114
+ _this.emit('reconnecting');
115
+ _this.init();
116
+ }, 1000);
117
+ return true;
118
+ };
119
+ this.connection.open();
120
+ };
121
+ CrossbarConnection.prototype.getSession = function () {
122
+ return this.session;
123
+ };
124
+ CrossbarConnection.prototype.isConnected = function () {
125
+ return this.session && this.session.isOpen;
126
+ };
127
+ /**
128
+ *
129
+ * @param {string} procedure
130
+ * @param {any | undefined} args
131
+ * @param {any | undefined} kwargs
132
+ * @param {autobahn.ICallOptions} options
133
+ */
134
+ CrossbarConnection.prototype.call = function (procedure, args, kwargs, options) {
135
+ var _a;
136
+ if (!this.isConnected()) {
137
+ throw new Error("cannot call without a connected session");
138
+ }
139
+ return (_a = this.session) === null || _a === void 0 ? void 0 : _a.call(procedure, args, kwargs, options);
140
+ };
141
+ CrossbarConnection.prototype.publish = function (topic, args, kwargs, options) {
142
+ var _a;
143
+ if (!this.isConnected()) {
144
+ throw new Error("cannot call without a connected session");
145
+ }
146
+ return (_a = this.session) === null || _a === void 0 ? void 0 : _a.publish(topic, args, kwargs, options);
147
+ };
148
+ return CrossbarConnection;
149
+ }(stream_1.EventEmitter));
150
+ exports.CrossbarConnection = CrossbarConnection;
@@ -0,0 +1,8 @@
1
+ /// <reference types="node" />
2
+ export declare class MsgpackSerializer {
3
+ SERIALIZER_ID: string;
4
+ BINARY: boolean;
5
+ constructor();
6
+ serialize(obj: any): Buffer;
7
+ unserialize(payload: Buffer): any;
8
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.MsgpackSerializer = void 0;
27
+ var msgpack = __importStar(require("msgpack-lite"));
28
+ var MsgpackSerializer = /** @class */ (function () {
29
+ function MsgpackSerializer() {
30
+ this.SERIALIZER_ID = 'msgpack';
31
+ this.BINARY = true;
32
+ }
33
+ MsgpackSerializer.prototype.serialize = function (obj) {
34
+ try {
35
+ var payload = msgpack.encode(obj);
36
+ return payload;
37
+ }
38
+ catch (e) {
39
+ console.warn('MessagePack encoding error', e);
40
+ throw e;
41
+ }
42
+ };
43
+ MsgpackSerializer.prototype.unserialize = function (payload) {
44
+ try {
45
+ var obj = msgpack.decode(payload);
46
+ return obj;
47
+ }
48
+ catch (e) {
49
+ console.warn('MessagePack decoding error', e);
50
+ throw e;
51
+ }
52
+ };
53
+ return MsgpackSerializer;
54
+ }());
55
+ exports.MsgpackSerializer = MsgpackSerializer;
@@ -0,0 +1,2 @@
1
+ import { IronFlock } from "./ironflock";
2
+ export = IronFlock;
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ var ironflock_1 = require("./ironflock");
3
+ module.exports = ironflock_1.IronFlock;
@@ -0,0 +1,18 @@
1
+ type TOptions = {
2
+ quiet?: boolean;
3
+ serialNumber?: string;
4
+ };
5
+ export declare class IronFlock {
6
+ private crossbarSession;
7
+ private onConnectTasks;
8
+ private options?;
9
+ constructor(options?: TOptions);
10
+ private onConnected;
11
+ private executeAllQueuedTasks;
12
+ private queue;
13
+ private _publish;
14
+ publish(topic: string, data: any): Promise<any>;
15
+ onConnect(cb: Function): void;
16
+ onDisconnect(cb: Function): void;
17
+ }
18
+ export {};
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.IronFlock = void 0;
51
+ var crossbar_1 = require("./crossbar");
52
+ var _a = process.env, DEVICE_SERIAL_NUMBER = _a.DEVICE_SERIAL_NUMBER, DEVICE_NAME = _a.DEVICE_NAME;
53
+ var IronFlock = /** @class */ (function () {
54
+ function IronFlock(options) {
55
+ this.onConnectTasks = [];
56
+ this.options = { quiet: true };
57
+ if (!DEVICE_SERIAL_NUMBER && !(options === null || options === void 0 ? void 0 : options.serialNumber)) {
58
+ throw new Error("the DEVICE_SERIAL_NUMBER environment variable is not defined");
59
+ }
60
+ var device_serial_number = DEVICE_SERIAL_NUMBER ? DEVICE_SERIAL_NUMBER : options === null || options === void 0 ? void 0 : options.serialNumber;
61
+ if (options) {
62
+ this.options = __assign(__assign({}, this.options), options);
63
+ }
64
+ this.onConnected = this.onConnected.bind(this);
65
+ this.crossbarSession = new crossbar_1.CrossbarConnection(device_serial_number);
66
+ this.crossbarSession.on("connected", this.onConnected);
67
+ }
68
+ IronFlock.prototype.onConnected = function () {
69
+ var _a;
70
+ return __awaiter(this, void 0, void 0, function () {
71
+ return __generator(this, function (_b) {
72
+ if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.quiet)) {
73
+ console.log("Successfully established a socket connection as ".concat(this.crossbarSession.serial_number, " with ").concat(this.crossbarSession.socketURI, "!"));
74
+ }
75
+ return [2 /*return*/, this.executeAllQueuedTasks()];
76
+ });
77
+ });
78
+ };
79
+ IronFlock.prototype.executeAllQueuedTasks = function () {
80
+ return __awaiter(this, void 0, void 0, function () {
81
+ return __generator(this, function (_a) {
82
+ switch (_a.label) {
83
+ case 0: return [4 /*yield*/, Promise.all(this.onConnectTasks.map(function (t) { return t(); }))];
84
+ case 1:
85
+ _a.sent();
86
+ this.onConnectTasks = [];
87
+ return [2 /*return*/];
88
+ }
89
+ });
90
+ });
91
+ };
92
+ IronFlock.prototype.queue = function (task) {
93
+ return __awaiter(this, void 0, void 0, function () {
94
+ var taskResult;
95
+ var _this = this;
96
+ return __generator(this, function (_a) {
97
+ switch (_a.label) {
98
+ case 0:
99
+ if (!this.crossbarSession.isConnected()) return [3 /*break*/, 2];
100
+ return [4 /*yield*/, task()];
101
+ case 1:
102
+ taskResult = _a.sent();
103
+ return [2 /*return*/, taskResult !== null && taskResult !== void 0 ? taskResult : true];
104
+ case 2: return [2 /*return*/, new Promise(function (resolve, reject) {
105
+ _this.onConnectTasks.push(function () { return __awaiter(_this, void 0, void 0, function () {
106
+ var taskResult, error_1;
107
+ return __generator(this, function (_a) {
108
+ switch (_a.label) {
109
+ case 0:
110
+ _a.trys.push([0, 2, , 3]);
111
+ return [4 /*yield*/, task()];
112
+ case 1:
113
+ taskResult = _a.sent();
114
+ resolve(taskResult !== null && taskResult !== void 0 ? taskResult : true);
115
+ return [3 /*break*/, 3];
116
+ case 2:
117
+ error_1 = _a.sent();
118
+ reject(error_1);
119
+ return [3 /*break*/, 3];
120
+ case 3: return [2 /*return*/];
121
+ }
122
+ });
123
+ }); });
124
+ })];
125
+ }
126
+ });
127
+ });
128
+ };
129
+ IronFlock.prototype._publish = function (topic, data) {
130
+ // checks for null and undefined
131
+ if (data == null) {
132
+ throw new Error("null or undefined cannot be published as payload");
133
+ }
134
+ var args = Array.isArray(data) ? data : [data];
135
+ var kwargs = {
136
+ DEVICE_SERIAL_NUMBER: this.crossbarSession.serial_number,
137
+ DEVICE_NAME: DEVICE_NAME,
138
+ };
139
+ return this.crossbarSession.publish(topic, args, kwargs);
140
+ };
141
+ IronFlock.prototype.publish = function (topic, data) {
142
+ var _this = this;
143
+ return this.queue(function () { return _this._publish.apply(_this, [topic, data]); });
144
+ };
145
+ IronFlock.prototype.onConnect = function (cb) {
146
+ this.crossbarSession.on("connected", function () { return cb(); });
147
+ };
148
+ IronFlock.prototype.onDisconnect = function (cb) {
149
+ this.crossbarSession.on("disconnected", function () { return cb(); });
150
+ };
151
+ return IronFlock;
152
+ }());
153
+ exports.IronFlock = IronFlock;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var ironflock_1 = require("../ironflock");
40
+ (function () {
41
+ return __awaiter(this, void 0, void 0, function () {
42
+ var options, ironflock, i;
43
+ return __generator(this, function (_a) {
44
+ switch (_a.label) {
45
+ case 0:
46
+ options = { serialNumber: '603fca74-c45b-4c5e-bd22-24988fd077e6' };
47
+ ironflock = new ironflock_1.IronFlock(options);
48
+ for (i = 0; i < 20; i++) {
49
+ ironflock.publish("re.datapods.meetup", {
50
+ temperature: i,
51
+ });
52
+ }
53
+ console.log("Published 20 temperature entries");
54
+ console.log("Waiting 2 seconds...");
55
+ return [4 /*yield*/, new Promise(function (res) { return setTimeout(res, 2000); })];
56
+ case 1:
57
+ _a.sent();
58
+ console.log("Done waiting 2 seconds!");
59
+ for (i = i; i < 40; i++) {
60
+ ironflock.publish("re.datapods.meetup", {
61
+ temperature: i,
62
+ });
63
+ }
64
+ console.log("Published another 20 temperature entries");
65
+ return [2 /*return*/];
66
+ }
67
+ });
68
+ });
69
+ })();
@@ -0,0 +1 @@
1
+ export declare function isPlainObject(o: any): boolean;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isPlainObject = void 0;
4
+ function isObject(o) {
5
+ return Object.prototype.toString.call(o) === "[object Object]";
6
+ }
7
+ function isPlainObject(o) {
8
+ var ctor, prot;
9
+ if (isObject(o) === false)
10
+ return false;
11
+ // If has modified constructor
12
+ ctor = o.constructor;
13
+ if (ctor === undefined)
14
+ return true;
15
+ // If has modified prototype
16
+ prot = ctor.prototype;
17
+ if (isObject(prot) === false)
18
+ return false;
19
+ // If constructor does not have an Object-specific method
20
+ if (prot.hasOwnProperty("isPrototypeOf") === false) {
21
+ return false;
22
+ }
23
+ // Most likely a plain Object
24
+ return true;
25
+ }
26
+ exports.isPlainObject = isPlainObject;
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "ironflock",
3
+ "version": "0.0.6",
4
+ "description": "Collect data in the IronFlock Storage architecture",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "author": "Record Evolution GmbH",
8
+ "license": "MIT",
9
+ "dependencies": {
10
+ "autobahn": "https://gitpkg.now.sh/jszczypk/autobahn-js/packages/autobahn?patch-1",
11
+ "cbor": "^9.0.1",
12
+ "msgpack-lite": "^0.1.26",
13
+ "msgpack5": "^6.0.2",
14
+ "node-gyp": "^10.0.1",
15
+ "ws": "^7.5.5"
16
+ },
17
+ "scripts": {
18
+ "prepublish": "rm -rf dist && tsc",
19
+ "release": "npm version patch --tag-version-prefix='' && git push && git push --tag"
20
+ },
21
+ "devDependencies": {
22
+ "@types/autobahn": "^20.9.4",
23
+ "@types/msgpack-lite": "^0.1.11",
24
+ "@types/node": "^16.11.10"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/RecordEvolution/reswarm-js.git"
29
+ },
30
+ "keywords": [
31
+ "ironflock",
32
+ "messaging"
33
+ ],
34
+ "bugs": {
35
+ "url": "https://github.com/RecordEvolution/reswarm-js/issues"
36
+ },
37
+ "homepage": "https://github.com/RecordEvolution/reswarm-js#readme"
38
+ }
@@ -0,0 +1,122 @@
1
+ import autobahn from "autobahn";
2
+ import { EventEmitter } from "stream";
3
+ // import { MsgpackSerializer } from "./serializer";
4
+ const { RESWARM_URL } = process.env
5
+
6
+ export type Details = { caller_authid: string; caller_authrole: string };
7
+
8
+ export type AuthPayload = {
9
+ success: boolean;
10
+ role: string;
11
+ realm: string;
12
+ authid: string;
13
+ extra: {
14
+ username: string;
15
+ };
16
+ };
17
+
18
+ const DATAPODS_WS_URI = "ws://devices.datapods.io:8088/ws";
19
+ const STUDIO_WS_URI = "ws://devices.record-evolution.com:8088/ws";
20
+ const LOCALHOST_WS_URI = "ws://reswarm-crossbar:8088";
21
+
22
+ const socketURIMap: Record<string, string> = {
23
+ "https://studio.datapods.io": DATAPODS_WS_URI,
24
+ "https://studio.record-evolution.com": STUDIO_WS_URI,
25
+ "http://localhost:8085": LOCALHOST_WS_URI
26
+ };
27
+
28
+ export class CrossbarConnection extends EventEmitter {
29
+ session?: autobahn.Session;
30
+ connection?: autobahn.Connection;
31
+ serial_number: string;
32
+ socketURI: string;
33
+ realm: string;
34
+
35
+ private reconnectTimeout?: any;
36
+
37
+ constructor(serial_number: string) {
38
+ super()
39
+ this.serial_number = serial_number;
40
+ this.realm = "userapps";
41
+ this.socketURI = RESWARM_URL ? socketURIMap[RESWARM_URL] : DATAPODS_WS_URI;
42
+
43
+ this.init();
44
+ }
45
+
46
+ private init() {
47
+ this.connection = new autobahn.Connection({
48
+ realm: this.realm,
49
+ url: this.socketURI,
50
+ authmethods: ["wampcra"],
51
+ authid: this.serial_number,
52
+ max_retries: -1,
53
+ max_retry_delay: 5,
54
+ initial_retry_delay: 1,
55
+ // @ts-ignore
56
+ // serializers: [new MsgpackSerializer()],
57
+ // @ts-ignore
58
+ autoping_interval: 2,
59
+ autoping_timeout: 4,
60
+ onchallenge: (session, method, extra) => {
61
+ return autobahn.auth_cra.sign(this.serial_number, extra.challenge);
62
+ },
63
+ });
64
+
65
+ this.connection.onopen = async (session, details) => {
66
+ this.session = session;
67
+ this.session.caller_disclose_me = true;
68
+
69
+ this.emit('connected', this.serial_number)
70
+ };
71
+
72
+ this.connection.onclose = (reason, details) => {
73
+ this.emit('disconnected')
74
+
75
+ console.log("connection closed: ", reason, details);
76
+
77
+ console.log("retrying in 1 second..");
78
+
79
+ if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
80
+ this.reconnectTimeout = setTimeout(() => {
81
+ this.emit('reconnecting')
82
+
83
+ this.init();
84
+ }, 1000);
85
+
86
+ return true;
87
+ };
88
+
89
+ this.connection.open();
90
+ }
91
+
92
+ getSession() {
93
+ return this.session;
94
+ }
95
+
96
+ isConnected() {
97
+ return this.session && this.session.isOpen
98
+ }
99
+
100
+ /**
101
+ *
102
+ * @param {string} procedure
103
+ * @param {any | undefined} args
104
+ * @param {any | undefined} kwargs
105
+ * @param {autobahn.ICallOptions} options
106
+ */
107
+ call<T>(procedure: string, args?: any[], kwargs?: object, options?: any) {
108
+ if (!this.isConnected()) {
109
+ throw new Error("cannot call without a connected session");
110
+ }
111
+
112
+ return this.session?.call<T>(procedure, args, kwargs, options);
113
+ }
114
+
115
+ publish(topic: string, args?: any[], kwargs?: object, options?: any) {
116
+ if (!this.isConnected()) {
117
+ throw new Error("cannot call without a connected session");
118
+ }
119
+
120
+ return this.session?.publish(topic, args, kwargs, options);
121
+ }
122
+ }
@@ -0,0 +1,31 @@
1
+ import * as msgpack from 'msgpack-lite';
2
+
3
+ export class MsgpackSerializer {
4
+ public SERIALIZER_ID: string;
5
+ public BINARY: boolean;
6
+
7
+ constructor() {
8
+ this.SERIALIZER_ID = 'msgpack';
9
+ this.BINARY = true;
10
+ }
11
+
12
+ public serialize(obj: any): Buffer {
13
+ try {
14
+ const payload: Buffer = msgpack.encode(obj);
15
+ return payload;
16
+ } catch (e) {
17
+ console.warn('MessagePack encoding error', e);
18
+ throw e;
19
+ }
20
+ }
21
+
22
+ public unserialize(payload: Buffer): any {
23
+ try {
24
+ const obj: any = msgpack.decode(payload);
25
+ return obj;
26
+ } catch (e) {
27
+ console.warn('MessagePack decoding error', e);
28
+ throw e;
29
+ }
30
+ }
31
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { IronFlock } from "./ironflock";
2
+
3
+ export = IronFlock;
@@ -0,0 +1,91 @@
1
+ import { CrossbarConnection } from "./crossbar";
2
+
3
+ const { DEVICE_SERIAL_NUMBER, DEVICE_NAME } = process.env;
4
+
5
+ type TOptions = {
6
+ quiet?: boolean;
7
+ serialNumber?: string;
8
+ }
9
+
10
+ export class IronFlock {
11
+ private crossbarSession: CrossbarConnection;
12
+ private onConnectTasks: Function[] = [];
13
+ private options?: TOptions = { quiet: true }
14
+
15
+ constructor(options?: TOptions) {
16
+ if (!DEVICE_SERIAL_NUMBER && !options?.serialNumber) {
17
+ throw new Error(
18
+ "the DEVICE_SERIAL_NUMBER environment variable is not defined"
19
+ );
20
+ }
21
+
22
+ const device_serial_number = DEVICE_SERIAL_NUMBER ? DEVICE_SERIAL_NUMBER : options?.serialNumber as string
23
+
24
+ if (options) {
25
+ this.options = { ...this.options, ...options }
26
+ }
27
+
28
+ this.onConnected = this.onConnected.bind(this)
29
+ this.crossbarSession = new CrossbarConnection(device_serial_number);
30
+ this.crossbarSession.on("connected", this.onConnected)
31
+ }
32
+
33
+ private async onConnected() {
34
+ if (!this.options?.quiet) {
35
+ console.log(`Successfully established a socket connection as ${this.crossbarSession.serial_number} with ${this.crossbarSession.socketURI}!`)
36
+ }
37
+
38
+ return this.executeAllQueuedTasks()
39
+ }
40
+
41
+ private async executeAllQueuedTasks() {
42
+ await Promise.all(this.onConnectTasks.map(t => t()));
43
+
44
+ this.onConnectTasks = [];
45
+ }
46
+
47
+ private async queue(task: Function) {
48
+ if (this.crossbarSession.isConnected()) {
49
+ const taskResult = await task()
50
+ return taskResult ?? true;
51
+ }
52
+
53
+ return new Promise((resolve, reject) => {
54
+ this.onConnectTasks.push(async () => {
55
+ try {
56
+ const taskResult = await task();
57
+ resolve(taskResult ?? true);
58
+ } catch (error) {
59
+ reject(error)
60
+ }
61
+ });
62
+ });
63
+ }
64
+
65
+ private _publish(topic: string, data: any) {
66
+ // checks for null and undefined
67
+ if (data == null) {
68
+ throw new Error("null or undefined cannot be published as payload")
69
+ }
70
+
71
+ const args = Array.isArray(data) ? data : [data];
72
+ const kwargs = {
73
+ DEVICE_SERIAL_NUMBER: this.crossbarSession.serial_number,
74
+ DEVICE_NAME,
75
+ };
76
+
77
+ return this.crossbarSession.publish(topic, args, kwargs);
78
+ }
79
+
80
+ publish(topic: string, data: any) {
81
+ return this.queue(() => this._publish.apply(this, [topic, data]));
82
+ }
83
+
84
+ onConnect(cb: Function) {
85
+ this.crossbarSession.on("connected", () => cb());
86
+ }
87
+
88
+ onDisconnect(cb: Function) {
89
+ this.crossbarSession.on("disconnected", () => cb());
90
+ }
91
+ }
@@ -0,0 +1,28 @@
1
+ import { IronFlock } from "../ironflock";
2
+
3
+ (async function () {
4
+ const options = { serialNumber: '603fca74-c45b-4c5e-bd22-24988fd077e6' }
5
+ const ironflock = new IronFlock(options);
6
+
7
+ for (var i = 0; i < 20; i++) {
8
+ ironflock.publish("re.datapods.meetup", {
9
+ temperature: i,
10
+ });
11
+ }
12
+
13
+ console.log("Published 20 temperature entries")
14
+
15
+ console.log("Waiting 2 seconds...")
16
+
17
+ await new Promise((res) => setTimeout(res, 2000));
18
+
19
+ console.log("Done waiting 2 seconds!")
20
+
21
+ for (i = i; i < 40; i++) {
22
+ ironflock.publish("re.datapods.meetup", {
23
+ temperature: i,
24
+ });
25
+ }
26
+
27
+ console.log("Published another 20 temperature entries")
28
+ })();
@@ -0,0 +1,25 @@
1
+ function isObject(o: any) {
2
+ return Object.prototype.toString.call(o) === "[object Object]";
3
+ }
4
+
5
+ export function isPlainObject(o: any) {
6
+ var ctor, prot;
7
+
8
+ if (isObject(o) === false) return false;
9
+
10
+ // If has modified constructor
11
+ ctor = o.constructor;
12
+ if (ctor === undefined) return true;
13
+
14
+ // If has modified prototype
15
+ prot = ctor.prototype;
16
+ if (isObject(prot) === false) return false;
17
+
18
+ // If constructor does not have an Object-specific method
19
+ if (prot.hasOwnProperty("isPrototypeOf") === false) {
20
+ return false;
21
+ }
22
+
23
+ // Most likely a plain Object
24
+ return true;
25
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "module": "CommonJS",
5
+ "lib": [
6
+ "es2017",
7
+ "es7",
8
+ "es6"
9
+ ],
10
+ "moduleResolution": "node",
11
+ "declaration": true,
12
+ "outDir": "dist",
13
+ "strict": true,
14
+ "esModuleInterop": true
15
+ },
16
+ "include": ["src"],
17
+ "exclude": [
18
+ "node_modules",
19
+ "dist"
20
+ ]
21
+ }