@moddable/pebbleproxy 0.1.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/package.json +17 -0
- package/proxy.js +479 -0
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@moddable/pebbleproxy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Proxy network protocols for PebbleOS",
|
|
5
|
+
"license": "LGPLv3",
|
|
6
|
+
"main": "proxy.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./proxy.js"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/Moddable-OpenSource/moddable"
|
|
16
|
+
}
|
|
17
|
+
}
|
package/proxy.js
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Moddable Tech, Inc.
|
|
3
|
+
*
|
|
4
|
+
* This file is part of the Moddable SDK Runtime.
|
|
5
|
+
*
|
|
6
|
+
* The Moddable SDK Runtime is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* The Moddable SDK Runtime is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Lesser General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
* along with the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
console.log("moddable proxies launched");
|
|
22
|
+
|
|
23
|
+
const HTTP_BASE = 15000;
|
|
24
|
+
const WS_BASE = 15050;
|
|
25
|
+
|
|
26
|
+
const gHTTPRequests = new Map;
|
|
27
|
+
const gWSRequests = new Map;
|
|
28
|
+
|
|
29
|
+
function eventReceived(e) {
|
|
30
|
+
if (state.log) {
|
|
31
|
+
console.log("moddable appmessage received");
|
|
32
|
+
|
|
33
|
+
for (let key in e.payload) {
|
|
34
|
+
console.log(key);
|
|
35
|
+
let value = e.payload[key];
|
|
36
|
+
if (Array.isArray(value)) {
|
|
37
|
+
let tt = Uint8Array.from(value);
|
|
38
|
+
tt = String.fromCharCode(...tt);
|
|
39
|
+
console.log("binary: " + tt);
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
console.log((typeof value) + ": " + value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let id = e.payload[HTTP_BASE + 1];
|
|
47
|
+
if (undefined !== id) {
|
|
48
|
+
try {
|
|
49
|
+
httpMessage(id, e);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.log("moddable pxoxy http exception" + error);
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
id = e.payload[WS_BASE + 1];
|
|
58
|
+
if (undefined !== id) {
|
|
59
|
+
try {
|
|
60
|
+
wsMessage(id, e);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.log("moddable pxoxy ws exception" + error);
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return false;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
function httpMessage(id, e) {
|
|
72
|
+
if (state.log)
|
|
73
|
+
console.log(" connection: " + id);
|
|
74
|
+
|
|
75
|
+
if (!gHTTPRequests.has(id))
|
|
76
|
+
gHTTPRequests.set(id, {id, state: "configure", kind: "http"});
|
|
77
|
+
const request = gHTTPRequests.get(id);
|
|
78
|
+
|
|
79
|
+
switch (request.state) {
|
|
80
|
+
case "configure": {
|
|
81
|
+
const [protocol, method, host, port, path, bufferSize, headersMask] = arrayToString(e.payload[HTTP_BASE + 2]).split(":");
|
|
82
|
+
request.bufferSize = parseInt(bufferSize);
|
|
83
|
+
if ("/" === path)
|
|
84
|
+
request.path = "";
|
|
85
|
+
else
|
|
86
|
+
request.path = path || "";
|
|
87
|
+
request.port = port;
|
|
88
|
+
request.host = host;
|
|
89
|
+
request.protocol = protocol;
|
|
90
|
+
request.method = method;
|
|
91
|
+
request.headersMask = headersMask ? headersMask.split(",") : "*";
|
|
92
|
+
|
|
93
|
+
request.state = "recieveHeaders";
|
|
94
|
+
request.headers = "";
|
|
95
|
+
} break;
|
|
96
|
+
|
|
97
|
+
case "recieveHeaders":
|
|
98
|
+
if (e.payload[HTTP_BASE + 3]) {
|
|
99
|
+
request.headers += arrayToString(e.payload[HTTP_BASE + 3]);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
request.requestBody = new Uint8Array(0);
|
|
103
|
+
request.state = "receiveBody";
|
|
104
|
+
// deliberate fall through
|
|
105
|
+
|
|
106
|
+
case "receiveBody":
|
|
107
|
+
if (e.payload[HTTP_BASE + 4]) {
|
|
108
|
+
const fragment = arrayToUint8Array(e.payload[HTTP_BASE + 4]);
|
|
109
|
+
const requestBody = new Uint8Array(fragment.length + request.requestBody.length);
|
|
110
|
+
requestBody.set(request.requestBody);
|
|
111
|
+
requestBody.set(fragment, request.requestBody.length);;
|
|
112
|
+
request.requestBody = requestBody;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
request.state = "makeRequest";
|
|
116
|
+
// deliberate fall through
|
|
117
|
+
|
|
118
|
+
case "makeRequest": {
|
|
119
|
+
if (!e.payload[HTTP_BASE + 5])
|
|
120
|
+
throw new Error("expected property missing");
|
|
121
|
+
|
|
122
|
+
if (state.log) {
|
|
123
|
+
console.log("make the request")
|
|
124
|
+
console.log(` method: ${request.method}`);
|
|
125
|
+
console.log(` protocol: ${request.protocol}`);
|
|
126
|
+
console.log(` host: ${request.host}`);
|
|
127
|
+
console.log(` port: ${request.port}`);
|
|
128
|
+
console.log(` path: ${request.path}`);
|
|
129
|
+
console.log(` bufferSize: ${request.bufferSize}`);
|
|
130
|
+
console.log(` headersMask: ${request.headersMask}`);
|
|
131
|
+
console.log(` requestBody: ${request.requestBody.length} bytes`);
|
|
132
|
+
request.headers.split("\n").forEach(line => console.log(" " + line));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
request.xhr = new XMLHttpRequest;
|
|
136
|
+
const url = `${request.protocol}://${request.host}${request.port ? ":" + request.port : ""}/${request.path}`;
|
|
137
|
+
if (state.log)
|
|
138
|
+
console.log(` url: ${url}`);
|
|
139
|
+
request.xhr.open(request.method, url, true);
|
|
140
|
+
request.xhr.responseType = 'arraybuffer';
|
|
141
|
+
|
|
142
|
+
request.headers.split("\n").forEach(line => {
|
|
143
|
+
const [key, value] = line.split(":");
|
|
144
|
+
request.xhr.setRequestHeader(key, value);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
request.xhr.onload = function () {
|
|
148
|
+
request.messages = [];
|
|
149
|
+
|
|
150
|
+
request.messages.push({
|
|
151
|
+
[HTTP_BASE + 1]: request.id,
|
|
152
|
+
[HTTP_BASE + 6]: request.xhr.status,
|
|
153
|
+
[HTTP_BASE + 11]: request.xhr.statusText
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const headers = request.xhr.getAllResponseHeaders().split("\r\n").filter(header => {
|
|
157
|
+
if ("*" === request.headersMask)
|
|
158
|
+
return true; // no mask, return all
|
|
159
|
+
header = header.split(":");
|
|
160
|
+
return request.headersMask.includes(header[0].trim().toLowerCase());
|
|
161
|
+
}).map(header => {
|
|
162
|
+
header = header.split(":");
|
|
163
|
+
header[0] = header[0].trim().toLowerCase();
|
|
164
|
+
header[1] = header[1].trim();
|
|
165
|
+
return header.join(":");
|
|
166
|
+
}).join("\n");
|
|
167
|
+
for (let position = 0, fragmentSize = request.bufferSize - 32 /* @@ */; position < headers.length; position += fragmentSize) {
|
|
168
|
+
const fragment = headers.slice(position, position + fragmentSize);
|
|
169
|
+
request.messages.push({
|
|
170
|
+
[HTTP_BASE + 1]: request.id,
|
|
171
|
+
[HTTP_BASE + 7]: fragment
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (let position = 0, response = new Uint8Array(request.xhr.response), fragmentSize = request.bufferSize - 32 /* @@ */; position < response.byteLength; position += fragmentSize) {
|
|
176
|
+
const fragment = response.slice(position, position + fragmentSize);
|
|
177
|
+
request.messages.push({
|
|
178
|
+
[HTTP_BASE + 1]: request.id,
|
|
179
|
+
[HTTP_BASE + 8]: Array.from(fragment) // sendAppMessage won't accept ArrayBuffer or Uint8Array. only Array.
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
request.messages.push({
|
|
183
|
+
[HTTP_BASE + 1]: request.id,
|
|
184
|
+
[HTTP_BASE + 9]: 0 // done. success.
|
|
185
|
+
});
|
|
186
|
+
request.state = "sendMessages";
|
|
187
|
+
|
|
188
|
+
sendRequestMessage(request);
|
|
189
|
+
}
|
|
190
|
+
request.xhr.onerror = function () {
|
|
191
|
+
console.log("ON error!!!!")
|
|
192
|
+
Pebble.sendAppMessage({
|
|
193
|
+
[HTTP_BASE + 1]: request.id,
|
|
194
|
+
[HTTP_BASE + 9]: -1 // done. failure.
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (request.requestBody.length)
|
|
198
|
+
request.xhr.send(request.requestBody.buffer);
|
|
199
|
+
else
|
|
200
|
+
request.xhr.send();
|
|
201
|
+
request.state = "waitResponse";
|
|
202
|
+
} break;
|
|
203
|
+
|
|
204
|
+
default:
|
|
205
|
+
console.log("unexpected state " + request.state + "\n");
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function wsMessage(id, e) {
|
|
211
|
+
if (!gWSRequests.has(id))
|
|
212
|
+
gWSRequests.set(id, {id, state: "configure", kind: "ws"});
|
|
213
|
+
const request = gWSRequests.get(id);
|
|
214
|
+
|
|
215
|
+
switch (request.state) {
|
|
216
|
+
case "configure": {
|
|
217
|
+
const [protocol, subprotocol, host, port, path, bufferSize] = arrayToString(e.payload[WS_BASE + 2]).split(":");
|
|
218
|
+
request.bufferSize = parseInt(bufferSize);
|
|
219
|
+
if ("/" === path)
|
|
220
|
+
request.path = "";
|
|
221
|
+
else
|
|
222
|
+
request.path = path || "";
|
|
223
|
+
request.port = port;
|
|
224
|
+
request.host = host;
|
|
225
|
+
request.protocol = protocol;
|
|
226
|
+
request.subprotocol = subprotocol ? subprotocol.split(",") : [];
|
|
227
|
+
|
|
228
|
+
request.state = "waitHandshake";
|
|
229
|
+
request.headers = ""; //@@ to do
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// deliberate fall through
|
|
233
|
+
|
|
234
|
+
case "connecting": {
|
|
235
|
+
if (state.log) {
|
|
236
|
+
console.log("websocket connect")
|
|
237
|
+
console.log(` protocol: ${request.protocol}`);
|
|
238
|
+
console.log(` host: ${request.host}`);
|
|
239
|
+
console.log(` port: ${request.port}`);
|
|
240
|
+
console.log(` path: ${request.path}`);
|
|
241
|
+
console.log(` subprotocol: ${request.subprotocol}`);
|
|
242
|
+
console.log(` bufferSize: ${request.bufferSize}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const url = `${request.protocol}://${request.host}${request.port ? ":" + request.port : ""}/${request.path}`;
|
|
246
|
+
if (state.log)
|
|
247
|
+
console.log(` url: ${url}`);
|
|
248
|
+
|
|
249
|
+
//@@ use of subprotocol gives exception in pypkjs request.ws = request.subprotocol ? new WebSocket(url, request.subprotocol) : new WebSocket(url);
|
|
250
|
+
request.ws = new WebSocket(url);
|
|
251
|
+
request.ws.binaryType = "arraybuffer";
|
|
252
|
+
|
|
253
|
+
request.ws.onopen = event => {
|
|
254
|
+
if (state.log)
|
|
255
|
+
console.log("websocket connected to host");
|
|
256
|
+
request.state = "connected";
|
|
257
|
+
request.messages = [];
|
|
258
|
+
request.messages.sending = false;
|
|
259
|
+
Pebble.sendAppMessage({
|
|
260
|
+
[WS_BASE + 1]: request.id,
|
|
261
|
+
[WS_BASE + 2]: 0 // connected. success.
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
request.ws.onerror = event => {
|
|
265
|
+
if (state.log)
|
|
266
|
+
console.log("websocket connection failed");
|
|
267
|
+
request.state = "error";
|
|
268
|
+
Pebble.sendAppMessage({
|
|
269
|
+
[WS_BASE + 1]: request.id,
|
|
270
|
+
[WS_BASE + 3]: -1 // disconnected error.
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
request.ws.onclose = event => {
|
|
274
|
+
if (state.log)
|
|
275
|
+
console.log("websocket connection closed");
|
|
276
|
+
request.state = "closed";
|
|
277
|
+
let reason = event.reason ? stringToArray(event.reason) : [];
|
|
278
|
+
let bytes = new Uint8Array(2 + reason.length);
|
|
279
|
+
let code = event.code ? event.code : 0;
|
|
280
|
+
bytes[0] = event.code >> 8;
|
|
281
|
+
bytes[1] = event.code;
|
|
282
|
+
if (state.log)
|
|
283
|
+
console.log(`close code ${code} reason ${arrayToString(reason)}`);
|
|
284
|
+
if (reason.byteLength)
|
|
285
|
+
bytes.set(arrayToUint8Array(reason.slice(2)), 2);
|
|
286
|
+
Pebble.sendAppMessage({
|
|
287
|
+
[WS_BASE + 1]: request.id,
|
|
288
|
+
[WS_BASE + 3]: 0, // disconnected clean.
|
|
289
|
+
[WS_BASE + 10]: Array.from(bytes) // sendAppMessage wants an Array
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
request.ws.onmessage = event => {
|
|
293
|
+
let data = event.data; // either ArrayBuffer or String
|
|
294
|
+
if (data instanceof ArrayBuffer)
|
|
295
|
+
data = new Uint8Array(data);
|
|
296
|
+
const binary = "string" !== typeof data;
|
|
297
|
+
if (binary)
|
|
298
|
+
data = Array.from(data); // sendAppMessage wants an Array
|
|
299
|
+
else
|
|
300
|
+
data = stringToArray(data); // sendAppMessage wants an Array
|
|
301
|
+
|
|
302
|
+
for (let position = 0, fragmentSize = request.bufferSize - 64 /* @@ */; position < data.length; position += fragmentSize) {
|
|
303
|
+
const fragment = data.slice(position, position + fragmentSize);
|
|
304
|
+
const more = (position + fragment.length) < data.length;
|
|
305
|
+
const part = (binary ? 4 : 6) + (more ? 1 : 0);
|
|
306
|
+
|
|
307
|
+
request.messages.push({
|
|
308
|
+
[WS_BASE + 1]: request.id,
|
|
309
|
+
[WS_BASE + part]: data
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (!request.messages.sending)
|
|
314
|
+
sendRequestMessage(request);
|
|
315
|
+
};
|
|
316
|
+
} break;
|
|
317
|
+
|
|
318
|
+
case "connected": {
|
|
319
|
+
let binary;
|
|
320
|
+
if (!request.pendingWrite)
|
|
321
|
+
request.pendingWrite = [];
|
|
322
|
+
|
|
323
|
+
if (e.payload[WS_BASE + 4]) { // binary no more
|
|
324
|
+
request.pendingWrite.push(e.payload[WS_BASE + 4]);
|
|
325
|
+
binary = true;
|
|
326
|
+
}
|
|
327
|
+
else if (e.payload[WS_BASE + 5]) // binary more
|
|
328
|
+
request.pendingWrite.push(e.payload[WS_BASE + 5]);
|
|
329
|
+
else if (e.payload[WS_BASE + 6]) { // text no more
|
|
330
|
+
request.pendingWrite.push(e.payload[WS_BASE + 6]);
|
|
331
|
+
binary = false;
|
|
332
|
+
}
|
|
333
|
+
else if (e.payload[WS_BASE + 7]) // text more
|
|
334
|
+
request.pendingWrite.push(e.payload[WS_BASE + 7]);
|
|
335
|
+
else if (e.payload[WS_BASE + 8]) { // close
|
|
336
|
+
request.state = "closing";
|
|
337
|
+
const bytes = arrayToUint8Array(e.payload[WS_BASE + 8]);
|
|
338
|
+
let code, reason;
|
|
339
|
+
if (bytes.byteLength >= 2) {
|
|
340
|
+
code = (new DataView(bytes.buffer)).getInt16(0, false);
|
|
341
|
+
if (state.log)
|
|
342
|
+
console.log(`code ${code}`);
|
|
343
|
+
if (bytes.byteLength > 2) {
|
|
344
|
+
reason = arrayToString(e.payload[WS_BASE + 8].slice(2));
|
|
345
|
+
if (state.log)
|
|
346
|
+
console.log(`reason ${reason}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// if (undefined === code)
|
|
350
|
+
request.ws.close();
|
|
351
|
+
// else if (undefined === reason)
|
|
352
|
+
// request.ws.close(code);
|
|
353
|
+
// else
|
|
354
|
+
// request.ws.close(code, reason);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
console.log("no payload found!");
|
|
359
|
+
throw new Error("surrender");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (undefined !== binary) {
|
|
363
|
+
let total = 0;
|
|
364
|
+
request.pendingWrite.forEach(fragment => total += fragment.length);
|
|
365
|
+
let msg = new Uint8Array(total);
|
|
366
|
+
for (let i = 0, offset = 0; i < request.pendingWrite.length; offset += request.pendingWrite[i++].length)
|
|
367
|
+
msg.set(arrayToUint8Array(request.pendingWrite[i]), offset);
|
|
368
|
+
if (binary)
|
|
369
|
+
request.ws.send(msg);
|
|
370
|
+
else
|
|
371
|
+
request.ws.send(arrayToString(msg));
|
|
372
|
+
delete request.pendingWrite;
|
|
373
|
+
}
|
|
374
|
+
} break;
|
|
375
|
+
|
|
376
|
+
default:
|
|
377
|
+
console.log("unexpected state " + request.state + "\n");
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function sendRequestMessage(request) {
|
|
383
|
+
if ("http" === request.kind)
|
|
384
|
+
sendRequestMessageHTTP(request);
|
|
385
|
+
else if ("ws" === request.kind)
|
|
386
|
+
sendRequestMessageWS(request);
|
|
387
|
+
else
|
|
388
|
+
throw new Error("unexpected request kind: " + request.kind);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function sendRequestMessageHTTP(request)
|
|
392
|
+
{
|
|
393
|
+
Pebble.sendAppMessage(
|
|
394
|
+
request.messages.shift(),
|
|
395
|
+
function () {
|
|
396
|
+
if (request.messages.length)
|
|
397
|
+
sendRequestMessage(request);
|
|
398
|
+
else
|
|
399
|
+
request.state = "done";
|
|
400
|
+
},
|
|
401
|
+
function () {
|
|
402
|
+
console.log("message send FAILED");
|
|
403
|
+
|
|
404
|
+
Pebble.sendAppMessage({
|
|
405
|
+
[HTTP_BASE + 1]: request.id,
|
|
406
|
+
[HTTP_BASE + 9]: -1 // done. failure.
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function sendRequestMessageWS(request) {
|
|
413
|
+
Pebble.sendAppMessage(
|
|
414
|
+
request.messages.shift(),
|
|
415
|
+
function () {
|
|
416
|
+
if (request.messages.length)
|
|
417
|
+
sendRequestMessage(request);
|
|
418
|
+
else
|
|
419
|
+
request.messages.sending = false;
|
|
420
|
+
},
|
|
421
|
+
function (e) {
|
|
422
|
+
console.log("message send FAILED " + JSON.stringify(e));
|
|
423
|
+
|
|
424
|
+
Pebble.sendAppMessage({
|
|
425
|
+
[WS_BASE + 1]: request.id,
|
|
426
|
+
[WS_BASE + 3]: -1 // done. failure.
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
request.messages.sending = true;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function arrayToString(a) {
|
|
434
|
+
return String.fromCharCode(...a);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function arrayToUint8Array(a) {
|
|
438
|
+
return Uint8Array.from(a);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function stringToArray(str) {
|
|
442
|
+
const result = [];
|
|
443
|
+
|
|
444
|
+
for (let i = 0; i < str.length; i++) {
|
|
445
|
+
const charCode = str.charCodeAt(i);
|
|
446
|
+
|
|
447
|
+
if (charCode < 0x80)
|
|
448
|
+
result.push(charCode);
|
|
449
|
+
else if (charCode < 0x800)
|
|
450
|
+
result.push( 0xc0 | (charCode >> 6),
|
|
451
|
+
0x80 | (charCode & 0x3f));
|
|
452
|
+
else if (charCode < 0xd800 || charCode >= 0xe000)
|
|
453
|
+
result.push( 0xe0 | (charCode >> 12),
|
|
454
|
+
0x80 | ((charCode >> 6) & 0x3f),
|
|
455
|
+
0x80 | (charCode & 0x3f));
|
|
456
|
+
else {
|
|
457
|
+
i++;
|
|
458
|
+
if (i >= str.length)
|
|
459
|
+
throw new Error('Unmatched surrogate pair');
|
|
460
|
+
|
|
461
|
+
const surrogate1 = charCode, surrogate2 = str.charCodeAt(i);
|
|
462
|
+
const codePoint = 0x10000 + ((surrogate1 - 0xd800) << 10) + (surrogate2 - 0xdc00);
|
|
463
|
+
|
|
464
|
+
result.push( 0xf0 | (codePoint >> 18),
|
|
465
|
+
0x80 | ((codePoint >> 12) & 0x3f),
|
|
466
|
+
0x80 | ((codePoint >> 6) & 0x3f),
|
|
467
|
+
0x80 | (codePoint & 0x3f));
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return result;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const state = {
|
|
475
|
+
eventReceived,
|
|
476
|
+
log: false
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
module.exports = state;
|