@aloma.io/integration-sdk 3.0.0-4

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 (56) hide show
  1. package/build/builder/index.d.mts +11 -0
  2. package/build/builder/index.mjs +51 -0
  3. package/build/builder/runtime-context.d.mts +7 -0
  4. package/build/builder/runtime-context.mjs +42 -0
  5. package/build/builder/transform/index.d.mts +5 -0
  6. package/build/builder/transform/index.mjs +72 -0
  7. package/build/controller/index.d.mts +19 -0
  8. package/build/controller/index.mjs +44 -0
  9. package/build/index.d.mts +2 -0
  10. package/build/index.mjs +2 -0
  11. package/build/internal/dispatcher/index.cjs +151 -0
  12. package/build/internal/dispatcher/index.d.cts +32 -0
  13. package/build/internal/index.cjs +461 -0
  14. package/build/internal/index.d.cts +14 -0
  15. package/build/internal/util/jwe/cli.cjs +13 -0
  16. package/build/internal/util/jwe/cli.d.cts +1 -0
  17. package/build/internal/util/jwe/index.cjs +57 -0
  18. package/build/internal/util/jwe/index.d.cts +32 -0
  19. package/build/internal/websocket/config.cjs +79 -0
  20. package/build/internal/websocket/config.d.cts +41 -0
  21. package/build/internal/websocket/connection/constants.cjs +21 -0
  22. package/build/internal/websocket/connection/constants.d.cts +2 -0
  23. package/build/internal/websocket/connection/index.cjs +53 -0
  24. package/build/internal/websocket/connection/index.d.cts +11 -0
  25. package/build/internal/websocket/connection/registration.cjs +31 -0
  26. package/build/internal/websocket/connection/registration.d.cts +5 -0
  27. package/build/internal/websocket/index.cjs +41 -0
  28. package/build/internal/websocket/index.d.cts +16 -0
  29. package/build/internal/websocket/transport/durable.cjs +61 -0
  30. package/build/internal/websocket/transport/durable.d.cts +20 -0
  31. package/build/internal/websocket/transport/index.cjs +148 -0
  32. package/build/internal/websocket/transport/index.d.cts +37 -0
  33. package/build/internal/websocket/transport/packet.cjs +44 -0
  34. package/build/internal/websocket/transport/packet.d.cts +16 -0
  35. package/build/internal/websocket/transport/processor.cjs +58 -0
  36. package/build/internal/websocket/transport/processor.d.cts +11 -0
  37. package/package.json +44 -0
  38. package/src/builder/index.mts +70 -0
  39. package/src/builder/runtime-context.mts +45 -0
  40. package/src/builder/transform/index.mts +89 -0
  41. package/src/controller/index.mts +58 -0
  42. package/src/index.mts +2 -0
  43. package/src/internal/dispatcher/index.cjs +189 -0
  44. package/src/internal/index.cjs +547 -0
  45. package/src/internal/util/jwe/cli.cjs +14 -0
  46. package/src/internal/util/jwe/index.cjs +69 -0
  47. package/src/internal/websocket/config.cjs +103 -0
  48. package/src/internal/websocket/connection/constants.cjs +25 -0
  49. package/src/internal/websocket/connection/index.cjs +70 -0
  50. package/src/internal/websocket/connection/registration.cjs +40 -0
  51. package/src/internal/websocket/index.cjs +46 -0
  52. package/src/internal/websocket/transport/durable.cjs +71 -0
  53. package/src/internal/websocket/transport/index.cjs +183 -0
  54. package/src/internal/websocket/transport/packet.cjs +54 -0
  55. package/src/internal/websocket/transport/processor.cjs +66 -0
  56. package/tsconfig.json +27 -0
@@ -0,0 +1,103 @@
1
+ const C = require('./connection/constants.cjs');
2
+ const JWE = require('../util/jwe/index.cjs');
3
+
4
+ class Config {
5
+ constructor({
6
+ registrationToken,
7
+ version,
8
+ name,
9
+ id,
10
+ endpoint,
11
+ wsEndpoint,
12
+ privateKey,
13
+ publicKey,
14
+ introspect,
15
+ configSchema,
16
+ }) {
17
+ this._token = null;
18
+ this._registrationToken = registrationToken;
19
+ this._version = version;
20
+ this._name = name;
21
+ this._endpoint = endpoint;
22
+ this._wsEndpoint = wsEndpoint;
23
+ this._id = id;
24
+ this._data = {};
25
+ this._privateKey = privateKey;
26
+ this._publicKey = publicKey;
27
+ this._jwe = new JWE({});
28
+ this._introspect = introspect;
29
+ this._configSchema = configSchema;
30
+
31
+ if (!registrationToken) throw new Error('empty registration token (set env.REGISTRATION_TOKEN)');
32
+ if (!endpoint) throw new Error('empty endpoint (set env.DEVICE_ENDPOINT)');
33
+ if (!wsEndpoint) throw new Error('empty registration token (set env.WEBSOCKET_ENDPOINT)');
34
+
35
+ if (!this._id || !this._version) throw new Error('need connector id and version');
36
+ }
37
+
38
+ async validateKeys(algorithm) {
39
+ if (!this._privateKey || !this._publicKey) throw new Error('need private and public key');
40
+
41
+ await this._jwe.importBase64Pair({
42
+ publicKey: this._publicKey,
43
+ privateKey: this._privateKey,
44
+ algorithm,
45
+ });
46
+
47
+ return this._jwe;
48
+ }
49
+
50
+ data(what) {
51
+ if (what) {
52
+ this._data = what;
53
+ }
54
+
55
+ return this._data;
56
+ }
57
+
58
+ introspect() {
59
+ return this._introspect();
60
+ }
61
+
62
+ configSchema() {
63
+ return this._configSchema();
64
+ }
65
+
66
+ publicKey() {
67
+ return this._publicKey;
68
+ }
69
+
70
+ id() {
71
+ return this._id;
72
+ }
73
+
74
+ version() {
75
+ return this._version;
76
+ }
77
+
78
+ name() {
79
+ return this._name;
80
+ }
81
+
82
+ url() {
83
+ return this._endpoint;
84
+ }
85
+
86
+ wsUrl() {
87
+ return this._wsEndpoint;
88
+ }
89
+
90
+ registrationToken() {
91
+ return this._registrationToken;
92
+ }
93
+
94
+ token() {
95
+ return this._token;
96
+ }
97
+
98
+ setToken(what) {
99
+ this._token = what;
100
+ }
101
+ }
102
+
103
+ module.exports = {Config};
@@ -0,0 +1,25 @@
1
+ const AUTHORIZATION = 'Authorization';
2
+
3
+ module.exports = {
4
+ augmentRequest: (what, config) => {
5
+ what.headers = {
6
+ ...what.headers,
7
+ 'User-Agent': config.id() + '/' + config.version(),
8
+ };
9
+
10
+ what.headers[AUTHORIZATION] = `Connector ${config.token()}`;
11
+
12
+ return what;
13
+ },
14
+
15
+ augmentRegistration: (what, config) => {
16
+ what.headers = {
17
+ ...what.headers,
18
+ 'User-Agent': config.id() + '/' + config.version(),
19
+ };
20
+
21
+ what.headers[AUTHORIZATION] = `Connector ${config.registrationToken()}`;
22
+
23
+ return what;
24
+ },
25
+ };
@@ -0,0 +1,70 @@
1
+ const fetch = require('node-fetch');
2
+ const {Registration} = require('./registration.cjs');
3
+ const C = require('./constants.cjs');
4
+
5
+ class Connection {
6
+ constructor({config, onStart}) {
7
+ this.config = config;
8
+ this.onStart = onStart;
9
+ }
10
+
11
+ async start() {
12
+ var local = this,
13
+ config = local.config;
14
+
15
+ try {
16
+ const response = await fetch(
17
+ config.url() + 'connect',
18
+ C.augmentRequest(
19
+ {
20
+ method: 'POST',
21
+ body: JSON.stringify({}),
22
+ headers: {'Content-Type': 'application/json'},
23
+ },
24
+ config
25
+ )
26
+ );
27
+
28
+ if (response.status === 401) {
29
+ config.setToken(await new Registration(local.config).run());
30
+
31
+ return await local.start();
32
+ } else if (response.status === 200) {
33
+ config.data(await response.json());
34
+
35
+ await local.onStart(() => local.onDisconnect());
36
+ } else {
37
+ setTimeout(() => local.start(), 5000);
38
+ }
39
+ } catch (e) {
40
+ console.log(e);
41
+ setTimeout(() => local.start(), 5000);
42
+ }
43
+ }
44
+
45
+ onDisconnect() {
46
+ var local = this;
47
+
48
+ setTimeout(() => local.start(), 5000);
49
+ }
50
+
51
+ async close() {
52
+ try {
53
+ await fetch(
54
+ this.config.url() + 'disconnect',
55
+ C.augmentRequest(
56
+ {
57
+ method: 'POST',
58
+ body: JSON.stringify({}),
59
+ headers: {'Content-Type': 'application/json'},
60
+ },
61
+ this.config
62
+ )
63
+ );
64
+ } catch (e) {
65
+ // blank
66
+ }
67
+ }
68
+ }
69
+
70
+ module.exports = {Connection};
@@ -0,0 +1,40 @@
1
+ const fetch = require('node-fetch');
2
+ const C = require('./constants.cjs');
3
+
4
+ class Registration {
5
+ constructor(config) {
6
+ this.config = config;
7
+ }
8
+
9
+ async run() {
10
+ var local = this;
11
+ const config = this.config;
12
+ const configSchema = config.configSchema();
13
+ const intro = await config.introspect();
14
+
15
+ const response = await fetch(
16
+ config.url() + 'register',
17
+ C.augmentRegistration(
18
+ {
19
+ method: 'POST',
20
+ body: JSON.stringify({
21
+ deployment: process.env.DEPLOYMENT || '',
22
+ name: config.name(),
23
+ version: config.version(),
24
+ id: config.id(),
25
+ publicKey: config.publicKey(),
26
+ schema: {configSchema, introspect: intro},
27
+ }),
28
+ headers: {'Content-Type': 'application/json'},
29
+ },
30
+ config
31
+ )
32
+ );
33
+
34
+ if (response.status === 200) return (await response.json()).key;
35
+
36
+ throw new Error('authentication failed');
37
+ }
38
+ }
39
+
40
+ module.exports = {Registration};
@@ -0,0 +1,46 @@
1
+ const WebSocket = require('ws');
2
+ const {Connection} = require('./connection/index.cjs');
3
+ const {Transport} = require('./transport/index.cjs');
4
+
5
+ class WebsocketConnector {
6
+ constructor({config, onMessage, onConnect}) {
7
+ var local = this;
8
+ this.config = config;
9
+ this.transport = new Transport({config, onMessage, onConnect});
10
+ }
11
+
12
+ async start() {
13
+ var local = this;
14
+ const config = this.config;
15
+
16
+ local.connection = new Connection({
17
+ config,
18
+ onStart: () => {
19
+ local.transport.start(() => setTimeout(() => local.start(), 1000));
20
+ },
21
+ });
22
+
23
+ await local.connection.start();
24
+ }
25
+
26
+ send(message) {
27
+ if (!message) return;
28
+
29
+ this.transport.send(this.transport.newPacket(message));
30
+ }
31
+
32
+ async close() {
33
+ var local = this;
34
+
35
+ if (local.transport) await local.transport.close();
36
+ }
37
+
38
+ async leaving() {
39
+ var local = this;
40
+
41
+ if (local.transport) await local.transport.leaving();
42
+ if (local.connection) await local.connection.close();
43
+ }
44
+ }
45
+
46
+ module.exports = {WebsocketConnector};
@@ -0,0 +1,71 @@
1
+ const WebSocket = require('ws');
2
+
3
+ class DurableWebsocket {
4
+ constructor({endpoint, secret, onConnect, onMessage}) {
5
+ this.endpoint = endpoint;
6
+ this.secret = secret;
7
+ this.onConnect = onConnect;
8
+ this.onMessage = onMessage;
9
+ this.failed = [];
10
+ this.fails = 0;
11
+ this.start();
12
+ }
13
+
14
+ async send(what) {
15
+ try {
16
+ return await this.ws.send(JSON.stringify(what));
17
+ } catch (e) {
18
+ this.failed.push(what);
19
+ }
20
+ }
21
+
22
+ start() {
23
+ var local = this;
24
+
25
+ if (local.connecting || local.closed) return;
26
+ local.connecting = true;
27
+
28
+ const ws = (local.ws = new WebSocket(local.endpoint, [], {
29
+ rejectUnauthorized: false,
30
+ headers: {Authorization: `Bearer ${local.secret}`},
31
+ }));
32
+
33
+ ws.on('open', () => {
34
+ local.connecting = false;
35
+ local.fails = 0;
36
+
37
+ var item;
38
+
39
+ while ((item = local.failed.shift())) {
40
+ local.send(item);
41
+ }
42
+
43
+ local.onConnect(local);
44
+ });
45
+
46
+ ws.on('message', (message) => {
47
+ setImmediate(() => local.onMessage(JSON.parse(message)));
48
+ });
49
+
50
+ ws.on('error', (message) => {
51
+ if (local.fails > 50) console.log('error:', message.message);
52
+
53
+ ++local.fails;
54
+ });
55
+
56
+ ws.on('close', (message) => {
57
+ local.connecting = false;
58
+
59
+ if (!local.closed) setTimeout(() => local.start(), 5000);
60
+ });
61
+ }
62
+
63
+ async close() {
64
+ if (this.closed) return;
65
+ this.closed = true;
66
+
67
+ await this.ws?.close();
68
+ }
69
+ }
70
+
71
+ module.exports = {DurableWebsocket};
@@ -0,0 +1,183 @@
1
+ const fetch = require('node-fetch');
2
+ const C = require('../connection/constants.cjs');
3
+ const cuid = require('@paralleldrive/cuid2').init({length: 32});
4
+ const {DurableWebsocket} = require('./durable.cjs');
5
+ const WebSocket = require('ws');
6
+ const {Packet, Callback} = require('./packet.cjs');
7
+
8
+ const cleanInterval = 45 * 1000;
9
+ const pingInterval = 30 * 1000;
10
+
11
+ class Transport {
12
+ constructor({config, onMessage, onConnect}) {
13
+ var local = this;
14
+
15
+ this.config = config;
16
+ this.callbacks = {};
17
+ this.running = true;
18
+ this.pinger = null;
19
+ this.cleaner = setInterval(() => local.clean(), cleanInterval);
20
+
21
+ this.acks = [];
22
+ this.packets = [];
23
+ this.onConnect = onConnect;
24
+ this._onMessage = onMessage;
25
+ }
26
+
27
+ schedule() {
28
+ var local = this;
29
+
30
+ setTimeout(() => local.schedule0(), 0);
31
+ }
32
+
33
+ schedule0() {
34
+ var local = this,
35
+ packets = [],
36
+ packet;
37
+
38
+ if (!local.packets.length || !local.connected) return;
39
+
40
+ while ((packet = local.packets.shift())) {
41
+ packets.push(packet.data);
42
+ }
43
+
44
+ if (!packets.length) return;
45
+
46
+ try {
47
+ local.ws.send(JSON.stringify({p: packets}));
48
+ } catch (e) {
49
+ console.log('could not send packets ', e);
50
+ packets.forEach((packet) => local.packets.unshift(packet));
51
+ }
52
+ }
53
+
54
+ async start(ondisconnect) {
55
+ var local = this,
56
+ config = local.config;
57
+
58
+ if (local._leaving) return;
59
+
60
+ local.close();
61
+
62
+ this.running = true;
63
+
64
+ const ws = (local.ws = new WebSocket(config.wsUrl(), ['connector'], C.augmentRequest({headers: {}}, config)));
65
+
66
+ ws.on('open', () => {
67
+ console.log('websocket connected');
68
+ local.connected = true;
69
+ local.pinger = setInterval(() => ws.ping(() => null), pingInterval);
70
+
71
+ local.onConnect(local);
72
+ });
73
+
74
+ ws.on('message', (message) => {
75
+ setTimeout(() => local.onMessages(JSON.parse(message)), 0);
76
+ });
77
+
78
+ ws.on('error', (message) => {
79
+ console.log('error:', message);
80
+ });
81
+
82
+ ws.on('close', (message) => {
83
+ local.connected = false;
84
+ clearInterval(local.pinger);
85
+
86
+ if (local.running) ondisconnect();
87
+ });
88
+ }
89
+
90
+ newDurableWebsocket({endpoint, secret, onConnect, onMessage}) {
91
+ return new DurableWebsocket({endpoint, secret, onConnect, onMessage});
92
+ }
93
+
94
+ send(packet) {
95
+ if (!packet) return;
96
+
97
+ this.packets.push(packet);
98
+ this.schedule();
99
+ }
100
+
101
+ onMessages(msgs) {
102
+ var local = this;
103
+
104
+ msgs.p?.forEach((item) => {
105
+ local.onMessage(item);
106
+ });
107
+ }
108
+
109
+ onMessage(data) {
110
+ if (!data) return;
111
+
112
+ const packet = new Packet(data);
113
+
114
+ if (packet.cb() && this.callbacks[packet.cb()]) {
115
+ try {
116
+ this.callbacks[packet.cb()].cb(packet.args());
117
+ } catch (e) {
118
+ console.log('error processing packet', e, packet);
119
+ } finally {
120
+ delete this.callbacks[packet.cb()];
121
+ }
122
+
123
+ return;
124
+ }
125
+
126
+ return this._onMessage(packet, this);
127
+ }
128
+
129
+ newPacket(data, cb, cbKey) {
130
+ const packet = new Packet({...data});
131
+
132
+ if (cb) {
133
+ this.callbacks[cbKey || packet.id()] = new Callback({cb});
134
+ packet.cb(cbKey || packet.id());
135
+ }
136
+
137
+ return packet;
138
+ }
139
+
140
+ clean() {
141
+ var local = this;
142
+ const cbs = {...this.callbacks},
143
+ then = Date.now() - 5 * 60 * 1000;
144
+
145
+ Object.keys(cbs).forEach((key) => {
146
+ const cb = cbs[key];
147
+ if (!cb) return;
148
+
149
+ if (cb.created < then) {
150
+ console.log('callback timeout', key);
151
+
152
+ try {
153
+ cb.cb({error: 'timeout'});
154
+ } catch (e) {
155
+ console.log('error while callback', key, cb, e);
156
+ }
157
+
158
+ delete local.callbacks[key];
159
+ }
160
+ });
161
+ }
162
+
163
+ leaving() {
164
+ this._leaving = true;
165
+ this.running = false;
166
+ }
167
+
168
+ close() {
169
+ clearInterval(this.pinger);
170
+ clearInterval(this.cleaner);
171
+
172
+ this.running = false;
173
+ this.connected = false;
174
+
175
+ try {
176
+ local.ws.terminate();
177
+ } catch (e) {
178
+ // blank
179
+ }
180
+ }
181
+ }
182
+
183
+ module.exports = {Transport};
@@ -0,0 +1,54 @@
1
+ const fetch = require('node-fetch');
2
+ const cuid = require('@paralleldrive/cuid2').init({length: 32});
3
+
4
+ class Packet {
5
+ constructor(data = {}) {
6
+ this.data = data;
7
+ this.data.id ||= cuid();
8
+ }
9
+
10
+ id() {
11
+ return this.data.id;
12
+ }
13
+
14
+ cb(what) {
15
+ if (what) {
16
+ this.data.c = what;
17
+ }
18
+
19
+ return this.data.c;
20
+ }
21
+
22
+ method(what) {
23
+ if (what) {
24
+ this.data.m = what;
25
+ }
26
+
27
+ return this.data.m;
28
+ }
29
+
30
+ event(what) {
31
+ if (what) {
32
+ this.data.e = what;
33
+ }
34
+
35
+ return this.data.e;
36
+ }
37
+
38
+ args(what) {
39
+ if (what) {
40
+ this.data.a = what;
41
+ }
42
+
43
+ return this.data.a;
44
+ }
45
+ }
46
+
47
+ class Callback {
48
+ constructor({cb}) {
49
+ this.cb = cb;
50
+ this.created = Date.now();
51
+ }
52
+ }
53
+
54
+ module.exports = {Callback, Packet};
@@ -0,0 +1,66 @@
1
+ const {Packet, Callback} = require('./packet.cjs');
2
+
3
+ class Processor {
4
+ constructor({transport, processPacket}) {
5
+ var local = this;
6
+
7
+ this.transport = transport;
8
+ this._processPacket = processPacket;
9
+ }
10
+
11
+ async onMessages(what) {
12
+ var local = this;
13
+ const messages = what.p;
14
+
15
+ const packets = messages.map((message) => {
16
+ const packet = new Packet(message);
17
+ local.transport.acks.push(packet.id());
18
+
19
+ return local.processPacket(packet);
20
+ });
21
+
22
+ await Promise.all(packets);
23
+ }
24
+
25
+ async processPacket(packet) {
26
+ var local = this;
27
+
28
+ if (packet.args().packet) {
29
+ await local.processPacket0(local.transport.newPacket(packet.args().packet), packet);
30
+ } else {
31
+ await local.processPacket0(packet, packet);
32
+ }
33
+ }
34
+
35
+ async processPacket0(packet, original) {
36
+ var local = this,
37
+ callbacks = local.transport.callbacks;
38
+
39
+ if (packet.cb() && callbacks[packet.cb()]) {
40
+ try {
41
+ callbacks[packet.cb()](packet.args());
42
+ } catch (e) {
43
+ console.log('error in callback', callbacks[packet.cb()], packet);
44
+ }
45
+
46
+ delete local.transport.callbacks[packet.cb()];
47
+ } else if (packet.event()) {
48
+ console.log('handle event packet', packet);
49
+ } else {
50
+ try {
51
+ const result = await local._processPacket(packet);
52
+ const reply = local.transport.newPacket({});
53
+
54
+ reply.method('connector.reply');
55
+ reply.cb(original.cb());
56
+ reply.args({...result});
57
+
58
+ local.transport.send(reply);
59
+ } catch (e) {
60
+ console.log('error processing packet', e, packet);
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ module.exports = {Processor};
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2022",
4
+ "skipLibCheck": true,
5
+ "module": "node16",
6
+ "moduleResolution": "node16",
7
+ "lib": ["es2015", "es6", "esnext"],
8
+ "declaration": true,
9
+ "allowJs": true,
10
+ "outDir": "build",
11
+ "rootDir": "src/",
12
+ "strict": true,
13
+ "noImplicitAny": false,
14
+ "esModuleInterop": true,
15
+ "resolveJsonModule": true,
16
+ "experimentalDecorators": true,
17
+ "emitDecoratorMetadata": true,
18
+ "allowSyntheticDefaultImports": true
19
+ },
20
+ "include": [
21
+ "src/**/*"
22
+ ],
23
+ "exclude": [
24
+ "node_modules/**/*",
25
+ "dist/**/*"
26
+ ]
27
+ }