sinricpro 4.0.0 → 6.0.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.
- package/README.md +43 -0
- package/dist/capabilities/EqualizerController.js +4 -3
- package/dist/capabilities/EqualizerController.js.map +1 -1
- package/dist/capabilities/ModeController.js +5 -3
- package/dist/capabilities/ModeController.js.map +1 -1
- package/dist/capabilities/RangeController.js +7 -4
- package/dist/capabilities/RangeController.js.map +1 -1
- package/dist/capabilities/SettingController.js +12 -3
- package/dist/capabilities/SettingController.js.map +1 -1
- package/dist/core/MessageQueue.js +6 -2
- package/dist/core/MessageQueue.js.map +1 -1
- package/dist/core/Signature.js +51 -18
- package/dist/core/Signature.js.map +1 -1
- package/dist/core/SinricPro.js +255 -42
- package/dist/core/SinricPro.js.map +1 -1
- package/dist/core/SinricProDevice.js +16 -10
- package/dist/core/SinricProDevice.js.map +1 -1
- package/dist/core/WebSocketClient.js +2 -1
- package/dist/core/WebSocketClient.js.map +1 -1
- package/dist/core/local/MdnsAnnouncer.js +107 -0
- package/dist/core/local/MdnsAnnouncer.js.map +1 -0
- package/dist/core/local/UdpListener.js +135 -0
- package/dist/core/local/UdpListener.js.map +1 -0
- package/dist/core/types.js +9 -1
- package/dist/core/types.js.map +1 -1
- package/dist/devices/SinricProCustomDevice.js +63 -0
- package/dist/devices/SinricProCustomDevice.js.map +1 -0
- package/dist/devices/SinricProSwitch.js +3 -1
- package/dist/devices/SinricProSwitch.js.map +1 -1
- package/dist/devices/SinricProTemperatureSensor.js +28 -0
- package/dist/devices/SinricProTemperatureSensor.js.map +1 -0
- package/dist/devices/index.js +3 -0
- package/dist/devices/index.js.map +1 -1
- package/dist/utils/network.js +58 -0
- package/dist/utils/network.js.map +1 -0
- package/package.json +14 -10
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Official SinricPro SDK for Node.js and TypeScript. Control your IoT devices with
|
|
|
10
10
|
- ✅ Modern async/await API
|
|
11
11
|
- ✅ WebSocket with automatic reconnection
|
|
12
12
|
- ✅ HMAC-SHA256 authentication
|
|
13
|
+
- ✅ Local control over the LAN (works while SinricPro is unreachable)
|
|
13
14
|
- ✅ Event rate limiting
|
|
14
15
|
- ✅ Multiple device types (Switch, Light, Thermostat, etc.)
|
|
15
16
|
- ✅ Comprehensive error handling
|
|
@@ -95,6 +96,48 @@ ts-node app.ts
|
|
|
95
96
|
|
|
96
97
|
---
|
|
97
98
|
|
|
99
|
+
## 🏠 Local Control
|
|
100
|
+
|
|
101
|
+
Devices also answer signed commands over the local network, so they keep working
|
|
102
|
+
while SinricPro is unreachable. It is on by default and needs no application
|
|
103
|
+
changes: a LAN request runs the same callbacks as a cloud request. UDP listener on port `3333`, joined to multicast group `224.9.9.9` and answering unicast on the same port. Replies go back to the peer that sent the request, never to the cloud websocket.
|
|
104
|
+
|
|
105
|
+
The mDNS announcement uses `bonjour-service`, installed with the SDK:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm install sinricpro
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Troubleshooting
|
|
114
|
+
|
|
115
|
+
### Connection Issues
|
|
116
|
+
|
|
117
|
+
1. **Check credentials** - Ensure APP_KEY and APP_SECRET are correct
|
|
118
|
+
2. **Check device ID** - Verify the device ID is exactly 24 hexadecimal characters
|
|
119
|
+
3. **Check network** - Ensure you have internet connectivity
|
|
120
|
+
4. **Enable debug logging** -
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
import { SinricProSdkLogger, LogLevel } from 'sinricpro';
|
|
124
|
+
SinricProSdkLogger.setLevel(LogLevel.DEBUG); // DEBUG, INFO, WARN, ERROR, NONE
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
### Local Control Issues
|
|
129
|
+
|
|
130
|
+
1. **No device found on the LAN** - check the log for
|
|
131
|
+
`Local control listening on UDP 3333`. A failed multicast join leaves nothing
|
|
132
|
+
listening, and the log line says so.
|
|
133
|
+
2. **No mDNS record** - install the extra: `pip install sinricpro[mdns]`.
|
|
134
|
+
3. **Discovery answers on the wrong network** - set `local_control_interface` to the
|
|
135
|
+
LAN address of the host.
|
|
136
|
+
4. **Android clients need a `WifiManager.MulticastLock`**, and iOS clients need
|
|
137
|
+
`_sinricpro._udp` listed in `NSBonjourServices`, or discovery returns nothing.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
98
141
|
## 🤝 Contributing
|
|
99
142
|
|
|
100
143
|
Contributions welcome! Please:
|
|
@@ -32,11 +32,11 @@ function EqualizerController(Base) {
|
|
|
32
32
|
}
|
|
33
33
|
return this.sendEvent('setBands', { bands }, cause);
|
|
34
34
|
}
|
|
35
|
-
async sendModeEvent(mode, cause = types_1.PHYSICAL_INTERACTION) {
|
|
35
|
+
async sendModeEvent(mode, instanceId = '', cause = types_1.PHYSICAL_INTERACTION) {
|
|
36
36
|
if (this.equalizerEventLimiter.isLimited()) {
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
|
-
return this.sendEvent('setMode', { mode }, cause);
|
|
39
|
+
return this.sendEvent('setMode', { mode }, cause, instanceId);
|
|
40
40
|
}
|
|
41
41
|
async handleEqualizerRequest(request) {
|
|
42
42
|
if (request.action === 'setBands' && this.setBandsCallback) {
|
|
@@ -79,7 +79,8 @@ function EqualizerController(Base) {
|
|
|
79
79
|
}
|
|
80
80
|
if (request.action === 'setMode' && this.setModeCallback) {
|
|
81
81
|
const mode = request.requestValue.mode;
|
|
82
|
-
const
|
|
82
|
+
const instanceId = request.instance || '';
|
|
83
|
+
const result = await this.setModeCallback(this.getDeviceId(), mode, instanceId);
|
|
83
84
|
// Handle both boolean and object return types
|
|
84
85
|
let success;
|
|
85
86
|
if (typeof result === 'boolean') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EqualizerController.js","sourceRoot":"","sources":["../../src/capabilities/EqualizerController.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAiCH,
|
|
1
|
+
{"version":3,"file":"EqualizerController.js","sourceRoot":"","sources":["../../src/capabilities/EqualizerController.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* ModeController - Adds generic mode control capability
|
|
4
|
+
* Supports optional instanceId for multi-instance mode control
|
|
4
5
|
*/
|
|
5
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
7
|
exports.ModeController = ModeController;
|
|
@@ -18,18 +19,19 @@ function ModeController(Base) {
|
|
|
18
19
|
onSetMode(callback) {
|
|
19
20
|
this.setModeCallback = callback;
|
|
20
21
|
}
|
|
21
|
-
async sendModeEvent(mode, cause = types_1.PHYSICAL_INTERACTION) {
|
|
22
|
+
async sendModeEvent(mode, instanceId = '', cause = types_1.PHYSICAL_INTERACTION) {
|
|
22
23
|
if (this.modeEventLimiter.isLimited()) {
|
|
23
24
|
return false;
|
|
24
25
|
}
|
|
25
|
-
return this.sendEvent('setMode', { mode }, cause);
|
|
26
|
+
return this.sendEvent('setMode', { mode }, cause, instanceId);
|
|
26
27
|
}
|
|
27
28
|
async handleModeRequest(request) {
|
|
28
29
|
if (request.action !== 'setMode' || !this.setModeCallback) {
|
|
29
30
|
return false;
|
|
30
31
|
}
|
|
31
32
|
const mode = request.requestValue.mode;
|
|
32
|
-
const
|
|
33
|
+
const instanceId = request.instance || '';
|
|
34
|
+
const result = await this.setModeCallback(this.getDeviceId(), mode, instanceId);
|
|
33
35
|
// Handle both boolean and object return types
|
|
34
36
|
let success;
|
|
35
37
|
if (typeof result === 'boolean') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModeController.js","sourceRoot":"","sources":["../../src/capabilities/ModeController.ts"],"names":[],"mappings":";AAAA;;
|
|
1
|
+
{"version":3,"file":"ModeController.js","sourceRoot":"","sources":["../../src/capabilities/ModeController.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAqBH,wCAsDC;AAxED,uDAAoD;AAEpD,yCAAwE;AAgBxE,SAAgB,cAAc,CAAyC,IAAO;IAC5E,OAAO,KAAM,SAAQ,IAAI;QAIvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YALT,oBAAe,GAA2B,IAAI,CAAC;YAC/C,qBAAgB,GAAG,IAAI,2BAAY,CAAC,yBAAiB,CAAC,CAAC;YAK7D,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,CAAC;QAED,SAAS,CAAC,QAAyB;YACjC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAClC,CAAC;QAED,KAAK,CAAC,aAAa,CACjB,IAAY,EACZ,aAAqB,EAAE,EACvB,QAAgB,4BAAoB;YAEpC,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAE,CAAC;gBACtC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAChE,CAAC;QAEO,KAAK,CAAC,iBAAiB,CAAC,OAAyB;YACvD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC1D,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC;YACvC,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAEhF,8CAA8C;YAC9C,IAAI,OAAgB,CAAC;YACrB,IAAI,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,OAAO,GAAG,MAAM,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;gBACzB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC;gBACxC,CAAC;YACH,CAAC;YAED,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC;YACpC,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* RangeController - Adds generic range control capability
|
|
4
|
+
* Supports optional instanceId for multi-instance range control
|
|
4
5
|
*/
|
|
5
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
7
|
exports.RangeController = RangeController;
|
|
@@ -22,16 +23,17 @@ function RangeController(Base) {
|
|
|
22
23
|
onAdjustRangeValue(callback) {
|
|
23
24
|
this.adjustRangeValueCallback = callback;
|
|
24
25
|
}
|
|
25
|
-
async sendRangeValueEvent(rangeValue, cause = types_1.PHYSICAL_INTERACTION) {
|
|
26
|
+
async sendRangeValueEvent(rangeValue, instanceId = '', cause = types_1.PHYSICAL_INTERACTION) {
|
|
26
27
|
if (this.rangeEventLimiter.isLimited()) {
|
|
27
28
|
return false;
|
|
28
29
|
}
|
|
29
|
-
return this.sendEvent('setRangeValue', { rangeValue }, cause);
|
|
30
|
+
return this.sendEvent('setRangeValue', { rangeValue }, cause, instanceId);
|
|
30
31
|
}
|
|
31
32
|
async handleRangeRequest(request) {
|
|
32
33
|
if (request.action === 'setRangeValue' && this.rangeValueCallback) {
|
|
33
34
|
const rangeValue = request.requestValue.rangeValue;
|
|
34
|
-
const
|
|
35
|
+
const instanceId = request.instance || '';
|
|
36
|
+
const success = await this.rangeValueCallback(this.getDeviceId(), rangeValue, instanceId);
|
|
35
37
|
if (success) {
|
|
36
38
|
request.responseValue.rangeValue = rangeValue;
|
|
37
39
|
}
|
|
@@ -39,7 +41,8 @@ function RangeController(Base) {
|
|
|
39
41
|
}
|
|
40
42
|
if (request.action === 'adjustRangeValue' && this.adjustRangeValueCallback) {
|
|
41
43
|
const rangeValueDelta = request.requestValue.rangeValueDelta;
|
|
42
|
-
const
|
|
44
|
+
const instanceId = request.instance || '';
|
|
45
|
+
const success = await this.adjustRangeValueCallback(this.getDeviceId(), rangeValueDelta, instanceId);
|
|
43
46
|
if (success) {
|
|
44
47
|
request.responseValue.rangeValue = rangeValueDelta;
|
|
45
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeController.js","sourceRoot":"","sources":["../../src/capabilities/RangeController.ts"],"names":[],"mappings":";AAAA;;
|
|
1
|
+
{"version":3,"file":"RangeController.js","sourceRoot":"","sources":["../../src/capabilities/RangeController.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AA4BH,0CAgEC;AAzFD,uDAAoD;AAEpD,yCAAwE;AAuBxE,SAAgB,eAAe,CAAyC,IAAO;IAC7E,OAAO,KAAM,SAAQ,IAAI;QAKvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YANT,uBAAkB,GAA8B,IAAI,CAAC;YACrD,6BAAwB,GAAoC,IAAI,CAAC;YACjE,sBAAiB,GAAG,IAAI,2BAAY,CAAC,yBAAiB,CAAC,CAAC;YAK9D,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,CAAC;QAED,YAAY,CAAC,QAA4B;YACvC,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;QACrC,CAAC;QAED,kBAAkB,CAAC,QAAkC;YACnD,IAAI,CAAC,wBAAwB,GAAG,QAAQ,CAAC;QAC3C,CAAC;QAED,KAAK,CAAC,mBAAmB,CACvB,UAAkB,EAClB,aAAqB,EAAE,EACvB,QAAgB,4BAAoB;YAEpC,IAAI,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,EAAE,CAAC;gBACvC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAC5E,CAAC;QAEO,KAAK,CAAC,kBAAkB,CAAC,OAAyB;YACxD,IAAI,OAAO,CAAC,MAAM,KAAK,eAAe,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAClE,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC;gBACnD,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;gBAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;gBAE1F,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,aAAa,CAAC,UAAU,GAAG,UAAU,CAAC;gBAChD,CAAC;gBAED,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,kBAAkB,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;gBAC3E,MAAM,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC;gBAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;gBAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACjD,IAAI,CAAC,WAAW,EAAE,EAClB,eAAe,EACf,UAAU,CACX,CAAC;gBAEF,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,aAAa,CAAC,UAAU,GAAG,eAAe,CAAC;gBACrD,CAAC;gBAED,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -4,26 +4,35 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SettingController = SettingController;
|
|
7
|
+
const EventLimiter_1 = require("../core/EventLimiter");
|
|
8
|
+
const types_1 = require("../core/types");
|
|
7
9
|
function SettingController(Base) {
|
|
8
10
|
return class extends Base {
|
|
9
11
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
12
|
constructor(...args) {
|
|
11
13
|
super(...args);
|
|
12
14
|
this.settingCallback = null;
|
|
15
|
+
this.settingEventLimiter = new EventLimiter_1.EventLimiter(types_1.EVENT_LIMIT_STATE);
|
|
13
16
|
this.registerRequestHandler(this.handleSettingRequest.bind(this));
|
|
14
17
|
}
|
|
15
18
|
onSetting(callback) {
|
|
16
19
|
this.settingCallback = callback;
|
|
17
20
|
}
|
|
21
|
+
async sendSettingEvent(settingId, value, cause = types_1.PHYSICAL_INTERACTION) {
|
|
22
|
+
if (this.settingEventLimiter.isLimited()) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return this.sendEvent('setSetting', { id: settingId, value }, cause);
|
|
26
|
+
}
|
|
18
27
|
async handleSettingRequest(request) {
|
|
19
28
|
if (request.action !== 'setSetting' || !this.settingCallback) {
|
|
20
29
|
return false;
|
|
21
30
|
}
|
|
22
|
-
const
|
|
31
|
+
const settingId = request.requestValue.id;
|
|
23
32
|
const value = request.requestValue.value;
|
|
24
|
-
const success = await this.settingCallback(this.getDeviceId(),
|
|
33
|
+
const success = await this.settingCallback(this.getDeviceId(), settingId, value);
|
|
25
34
|
if (success) {
|
|
26
|
-
request.responseValue.
|
|
35
|
+
request.responseValue.id = settingId;
|
|
27
36
|
request.responseValue.value = value;
|
|
28
37
|
}
|
|
29
38
|
return success;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingController.js","sourceRoot":"","sources":["../../src/capabilities/SettingController.ts"],"names":[],"mappings":";AAAA;;GAEG;;
|
|
1
|
+
{"version":3,"file":"SettingController.js","sourceRoot":"","sources":["../../src/capabilities/SettingController.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAqBH,8CA4CC;AA9DD,uDAAoD;AAEpD,yCAAwE;AAgBxE,SAAgB,iBAAiB,CAAyC,IAAO;IAC/E,OAAO,KAAM,SAAQ,IAAI;QAIvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YALT,oBAAe,GAA2B,IAAI,CAAC;YAC/C,wBAAmB,GAAiB,IAAI,2BAAY,CAAC,yBAAiB,CAAC,CAAC;YAK9E,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,SAAS,CAAC,QAAyB;YACjC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAClC,CAAC;QAED,KAAK,CAAC,gBAAgB,CACpB,SAAiB,EACjB,KAAc,EACd,QAAgB,4BAAoB;YAEpC,IAAI,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,EAAE,CAAC;gBACzC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;QACvE,CAAC;QAEO,KAAK,CAAC,oBAAoB,CAAC,OAAyB;YAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,YAAY,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC7D,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC;YACzC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;YAEjF,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,aAAa,CAAC,EAAE,GAAG,SAAS,CAAC;gBACrC,OAAO,CAAC,aAAa,CAAC,KAAK,GAAG,KAAK,CAAC;YACtC,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -8,8 +8,12 @@ class MessageQueue {
|
|
|
8
8
|
constructor() {
|
|
9
9
|
this.queue = [];
|
|
10
10
|
}
|
|
11
|
-
push(
|
|
12
|
-
this.queue.push(
|
|
11
|
+
push(entry) {
|
|
12
|
+
this.queue.push(entry);
|
|
13
|
+
}
|
|
14
|
+
/** Put an entry back at the head, preserving delivery order when a send is deferred. */
|
|
15
|
+
pushFront(entry) {
|
|
16
|
+
this.queue.unshift(entry);
|
|
13
17
|
}
|
|
14
18
|
pop() {
|
|
15
19
|
return this.queue.shift();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageQueue.js","sourceRoot":"","sources":["../../src/core/MessageQueue.ts"],"names":[],"mappings":";AAAA;;GAEG;;;
|
|
1
|
+
{"version":3,"file":"MessageQueue.js","sourceRoot":"","sources":["../../src/core/MessageQueue.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAIH,MAAa,YAAY;IAAzB;QACU,UAAK,GAAoB,EAAE,CAAC;IA0BtC,CAAC;IAxBC,IAAI,CAAC,KAAoB;QACvB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,wFAAwF;IACxF,SAAS,CAAC,KAAoB;QAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAClB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;IACjC,CAAC;CACF;AA3BD,oCA2BC"}
|
package/dist/core/Signature.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Signature = void 0;
|
|
7
7
|
const crypto_1 = require("crypto");
|
|
8
|
+
const PAYLOAD_MARKER = '"payload":';
|
|
9
|
+
const SIGNATURE_MARKER = ',"signature"';
|
|
8
10
|
class Signature {
|
|
9
11
|
constructor(appSecret) {
|
|
10
12
|
this.appSecret = appSecret;
|
|
@@ -14,17 +16,21 @@ class Signature {
|
|
|
14
16
|
*/
|
|
15
17
|
hmacBase64(message, key) {
|
|
16
18
|
const hmac = (0, crypto_1.createHmac)('sha256', key);
|
|
17
|
-
hmac.update(message);
|
|
19
|
+
hmac.update(message, 'utf8');
|
|
18
20
|
return hmac.digest('base64');
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
|
-
* Extract payload from
|
|
23
|
+
* Extract the payload from a message exactly as it was received.
|
|
24
|
+
*
|
|
25
|
+
* The bytes between `"payload":` and `,"signature"` are taken verbatim: the
|
|
26
|
+
* sender's key order and spacing are its own and re-encoding a parsed object
|
|
27
|
+
* would produce a different byte string and a failed verification.
|
|
22
28
|
*/
|
|
23
29
|
extractPayload(messageStr) {
|
|
24
|
-
const beginPayload = messageStr.indexOf(
|
|
25
|
-
const endPayload = messageStr.indexOf(
|
|
30
|
+
const beginPayload = messageStr.indexOf(PAYLOAD_MARKER);
|
|
31
|
+
const endPayload = messageStr.indexOf(SIGNATURE_MARKER, beginPayload);
|
|
26
32
|
if (beginPayload > 0 && endPayload > 0) {
|
|
27
|
-
return messageStr.substring(beginPayload +
|
|
33
|
+
return messageStr.substring(beginPayload + PAYLOAD_MARKER.length, endPayload);
|
|
28
34
|
}
|
|
29
35
|
return '';
|
|
30
36
|
}
|
|
@@ -37,31 +43,58 @@ class Signature {
|
|
|
37
43
|
return this.hmacBase64(payload, this.appSecret);
|
|
38
44
|
}
|
|
39
45
|
/**
|
|
40
|
-
* Sign a message object
|
|
46
|
+
* Sign a message object in place.
|
|
41
47
|
*/
|
|
42
48
|
sign(message) {
|
|
43
|
-
const
|
|
44
|
-
const signature = this.calculateSignature(payloadStr);
|
|
49
|
+
const signature = this.calculateSignature(JSON.stringify(message.payload));
|
|
45
50
|
if (!message.signature) {
|
|
46
51
|
message.signature = { HMAC: '' };
|
|
47
52
|
}
|
|
48
53
|
message.signature.HMAC = signature;
|
|
49
54
|
}
|
|
50
55
|
/**
|
|
51
|
-
*
|
|
56
|
+
* Sign a message and return the exact bytes to transmit.
|
|
57
|
+
*
|
|
58
|
+
* The payload is serialised once and spliced into the envelope, so the HMAC
|
|
59
|
+
* always covers the bytes that go on the wire. Serialising it a second time
|
|
60
|
+
* for transmission is what silently breaks verification on the peer.
|
|
61
|
+
*/
|
|
62
|
+
serialize(message) {
|
|
63
|
+
const payloadStr = JSON.stringify(message.payload);
|
|
64
|
+
const hmac = this.calculateSignature(payloadStr);
|
|
65
|
+
message.signature = { HMAC: hmac };
|
|
66
|
+
return (`{"header":${JSON.stringify(message.header)},` +
|
|
67
|
+
`"payload":${payloadStr},` +
|
|
68
|
+
`"signature":{"HMAC":${JSON.stringify(hmac)}}}`);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validate the signature of a message as it arrived on the wire.
|
|
72
|
+
*
|
|
73
|
+
* @param rawMessage - the received bytes, not a re-encoded object
|
|
52
74
|
*/
|
|
53
|
-
validate(
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
return
|
|
75
|
+
validate(rawMessage) {
|
|
76
|
+
const payload = this.extractPayload(rawMessage);
|
|
77
|
+
if (!payload)
|
|
78
|
+
return false;
|
|
79
|
+
let claimedSignature = '';
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(rawMessage);
|
|
82
|
+
claimedSignature = parsed?.signature?.HMAC ?? '';
|
|
57
83
|
}
|
|
58
|
-
|
|
84
|
+
catch {
|
|
59
85
|
return false;
|
|
60
86
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
if (!claimedSignature)
|
|
88
|
+
return false;
|
|
89
|
+
return Signature.constantTimeEquals(this.calculateSignature(payload), claimedSignature);
|
|
90
|
+
}
|
|
91
|
+
/** timingSafeEqual throws on unequal lengths; a length mismatch is never a match anyway. */
|
|
92
|
+
static constantTimeEquals(a, b) {
|
|
93
|
+
const bufferA = Buffer.from(a, 'utf8');
|
|
94
|
+
const bufferB = Buffer.from(b, 'utf8');
|
|
95
|
+
if (bufferA.length !== bufferB.length)
|
|
96
|
+
return false;
|
|
97
|
+
return (0, crypto_1.timingSafeEqual)(bufferA, bufferB);
|
|
65
98
|
}
|
|
66
99
|
}
|
|
67
100
|
exports.Signature = Signature;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Signature.js","sourceRoot":"","sources":["../../src/core/Signature.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,
|
|
1
|
+
{"version":3,"file":"Signature.js","sourceRoot":"","sources":["../../src/core/Signature.ts"],"names":[],"mappings":"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"}
|