pryv 2.1.8
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/.jsdoc-conf.json +29 -0
- package/.mocharc.js +13 -0
- package/LICENSE.md +27 -0
- package/README.md +723 -0
- package/package.json +57 -0
- package/scripts/setup-environment-dev.sh +28 -0
- package/scripts/upload.sh +15 -0
- package/src/Auth/AuthController.js +276 -0
- package/src/Auth/AuthStates.js +20 -0
- package/src/Auth/LoginMessages.js +29 -0
- package/src/Auth/index.js +43 -0
- package/src/Browser/CookieUtils.js +51 -0
- package/src/Browser/LoginButton.js +199 -0
- package/src/Browser/index.js +55 -0
- package/src/Connection.js +331 -0
- package/src/Pryv.js +19 -0
- package/src/Service.js +197 -0
- package/src/ServiceAssets.js +162 -0
- package/src/index-socket.io-monitor.js +4 -0
- package/src/index.html +17 -0
- package/src/index.js +3 -0
- package/src/lib/browser-getEventStreamed.js +80 -0
- package/src/lib/json-parser.js +156 -0
- package/src/utils.js +136 -0
- package/test/Browser.AuthController.test.js +97 -0
- package/test/Browser.test.js +79 -0
- package/test/Connection.test.js +455 -0
- package/test/Service.test.js +89 -0
- package/test/ServiceAssets.test.js +79 -0
- package/test/Y.png +0 -0
- package/test/browser-index.js +11 -0
- package/test/browser-tests.html +31 -0
- package/test/helpers.js +8 -0
- package/test/load-test-account.js +108 -0
- package/test/test-data.js +92 -0
- package/test/utils.test.js +68 -0
- package/web-demos/auth-with-redirection.html +72 -0
- package/web-demos/auth.html +77 -0
- package/web-demos/custom-login-button.html +158 -0
- package/web-demos/index.html +186 -0
- package/web-demos/service-info.json +13 -0
- package/web-demos/stream-examples.html +80 -0
- package/webpack.config.js +71 -0
package/src/Service.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
|
|
2
|
+
const utils = require('./utils.js');
|
|
3
|
+
// Connection is required at the end of this file to allow circular requires.
|
|
4
|
+
const Assets = require('./ServiceAssets.js');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @class Pryv.Service
|
|
9
|
+
* A Pryv.io deployment is a unique "Service", as an example **Pryv Lab** is a service, deployed with the domain name **pryv.me**.
|
|
10
|
+
*
|
|
11
|
+
* `Pryv.Service` exposes tools to interact with Pryv.io at a "Platform" level.
|
|
12
|
+
*
|
|
13
|
+
* ##### Initizalization with a service info URL
|
|
14
|
+
```javascript
|
|
15
|
+
const service = new Pryv.Service('https://reg.pryv.me/service/info');
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- With the content of a serviceInfo configuration
|
|
19
|
+
|
|
20
|
+
Service information properties can be overriden with specific values. This might be usefull to test new designs on production platforms.
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
const serviceInfoUrl = 'https://reg.pryv.me/service/info';
|
|
24
|
+
const serviceCustomizations = {
|
|
25
|
+
name: 'Pryv Lab 2',
|
|
26
|
+
assets: {
|
|
27
|
+
definitions: 'https://pryv.github.io/assets-pryv.me/index.json'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const service = new Pryv.Service(serviceInfoUrl, serviceCustomizations);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
* @memberof Pryv
|
|
34
|
+
*
|
|
35
|
+
* @constructor
|
|
36
|
+
* @param {string} serviceInfoUrl Url point to /service/info of a Pryv platform see: {@link https://api.pryv.com/reference/#service-info}
|
|
37
|
+
*/
|
|
38
|
+
class Service {
|
|
39
|
+
|
|
40
|
+
constructor (serviceInfoUrl, serviceCustomizations) {
|
|
41
|
+
this._pryvServiceInfo = null;
|
|
42
|
+
this._assets = null;
|
|
43
|
+
this._polling = false;
|
|
44
|
+
this._pryvServiceInfoUrl = serviceInfoUrl;
|
|
45
|
+
this._pryvServiceCustomizations = serviceCustomizations;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Return service info parameters info known of fetch it if needed.
|
|
50
|
+
* Example
|
|
51
|
+
* - name of a platform
|
|
52
|
+
* `const serviceName = await service.info().name`
|
|
53
|
+
* @see PryvServiceInfo For details on available properties.
|
|
54
|
+
* @param {boolean?} forceFetch If true, will force fetching service info.
|
|
55
|
+
* @returns {Promise<PryvServiceInfo>} Promise to Service info Object
|
|
56
|
+
*/
|
|
57
|
+
async info (forceFetch) {
|
|
58
|
+
if (forceFetch || !this._pryvServiceInfo) {
|
|
59
|
+
let baseServiceInfo = {};
|
|
60
|
+
if (this._pryvServiceInfoUrl) {
|
|
61
|
+
const res = await utils.superagent.get(this._pryvServiceInfoUrl).set('Access-Control-Allow-Origin', '*').set('accept', 'json');
|
|
62
|
+
baseServiceInfo = res.body;
|
|
63
|
+
}
|
|
64
|
+
Object.assign(baseServiceInfo, this._pryvServiceCustomizations);
|
|
65
|
+
this.setServiceInfo(baseServiceInfo);
|
|
66
|
+
}
|
|
67
|
+
return this._pryvServiceInfo;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @private
|
|
72
|
+
* @param {PryvServiceInfo} serviceInfo
|
|
73
|
+
*/
|
|
74
|
+
setServiceInfo (serviceInfo) {
|
|
75
|
+
if (!serviceInfo.name) {
|
|
76
|
+
throw new Error('Invalid data from service/info');
|
|
77
|
+
}
|
|
78
|
+
// cleanup serviceInfo for eventual url not finishing by "/"
|
|
79
|
+
// code will be obsolete with next version of register
|
|
80
|
+
['access', 'api', 'register'].forEach((key) => {
|
|
81
|
+
if (serviceInfo[key].slice(-1) !== '/') {
|
|
82
|
+
serviceInfo[key] += '/';
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
this._pryvServiceInfo = serviceInfo;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Return assets property content
|
|
90
|
+
* @param {boolean?} forceFetch If true, will force fetching service info.
|
|
91
|
+
* @returns {Promise<ServiceAssets>} Promise to ServiceAssets
|
|
92
|
+
*/
|
|
93
|
+
async assets (forceFetch) {
|
|
94
|
+
if (!forceFetch && this._assets) {
|
|
95
|
+
return this._assets;
|
|
96
|
+
} else {
|
|
97
|
+
const serviceInfo = await this.info();
|
|
98
|
+
if (!serviceInfo.assets || !serviceInfo.assets.definitions) {
|
|
99
|
+
console.log('Warning: no assets for this service');
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
this._assets = await Assets.setup(serviceInfo.assets.definitions);
|
|
103
|
+
return this._assets;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Return service info parameters info known or null if not yet loaded
|
|
109
|
+
* @returns {PryvServiceInfo} Service Info definition
|
|
110
|
+
*/
|
|
111
|
+
infoSync () {
|
|
112
|
+
return this._pryvServiceInfo;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Return an API Endpoint from a username and token
|
|
117
|
+
* @param {string} username
|
|
118
|
+
* @param {string} [token]
|
|
119
|
+
* @return {PryvApiEndpoint}
|
|
120
|
+
*/
|
|
121
|
+
async apiEndpointFor (username, token) {
|
|
122
|
+
const serviceInfo = await this.info();
|
|
123
|
+
return Service.buildAPIEndpoint(serviceInfo, username, token);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Return an API Endpoint from a username and token and a PryvServiceInfo.
|
|
128
|
+
* This is method is rarely used. See **apiEndpointFor** as an alternative.
|
|
129
|
+
* @param {PryvServiceInfo} serviceInfo
|
|
130
|
+
* @param {string} username
|
|
131
|
+
* @param {string} [token]
|
|
132
|
+
* @return {PryvApiEndpoint}
|
|
133
|
+
*/
|
|
134
|
+
static buildAPIEndpoint (serviceInfo, username, token) {
|
|
135
|
+
const endpoint = serviceInfo.api.replace('{username}', username);
|
|
136
|
+
return utils.buildPryvApiEndpoint({ endpoint: endpoint, token: token });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Issue a "login call on the Service" return a Connection on success
|
|
141
|
+
* **! Warning**: the token of the connection will be a "Personal" token that expires
|
|
142
|
+
* @see https://api.pryv.com/reference-full/#login-user
|
|
143
|
+
* @param {string} username
|
|
144
|
+
* @param {string} password
|
|
145
|
+
* @param {string} appId
|
|
146
|
+
* @param {string} [originHeader=service-info.register] Only for Node.js. If not set will use the register value of service info. In browsers this will overridden by current page location.
|
|
147
|
+
* @throws {Error} on invalid login
|
|
148
|
+
*/
|
|
149
|
+
async login (username, password, appId, originHeader) {
|
|
150
|
+
const apiEndpoint = await this.apiEndpointFor(username);
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const headers = { accept: 'json' };
|
|
154
|
+
originHeader = originHeader || (await this.info()).register;
|
|
155
|
+
if (!utils.isBrowser()) {
|
|
156
|
+
headers.Origin = originHeader;
|
|
157
|
+
}
|
|
158
|
+
const res = await utils.superagent.post(apiEndpoint + 'auth/login')
|
|
159
|
+
.set(headers)
|
|
160
|
+
.send({ username: username, password: password, appId: appId });
|
|
161
|
+
|
|
162
|
+
if (!res.body.token) {
|
|
163
|
+
throw new Error('Invalid login response: ' + res.body);
|
|
164
|
+
}
|
|
165
|
+
return new Connection(
|
|
166
|
+
Service.buildAPIEndpoint(await this.info(), username, res.body.token),
|
|
167
|
+
this // Pre load Connection with service
|
|
168
|
+
);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
if (e.response && e.response.body
|
|
171
|
+
&& e.response.body.error
|
|
172
|
+
&& e.response.body.error.message) {
|
|
173
|
+
throw new Error(e.response.body.error.message)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = Service;
|
|
180
|
+
|
|
181
|
+
// Require is done after exports to allow circular references
|
|
182
|
+
const Connection = require('./Connection');
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Object to handle Pryv Service Informations https://api.pryv.com/reference/#service-info
|
|
186
|
+
* @typedef {Object} PryvServiceInfo
|
|
187
|
+
* @property {string} register The URL of the register service.
|
|
188
|
+
* @property {string} access The URL of the access page.
|
|
189
|
+
* @property {string} api The API endpoint format.
|
|
190
|
+
* @property {string} name The platform name.
|
|
191
|
+
* @property {string} home The URL of the platform's home page.
|
|
192
|
+
* @property {string} support The email or URL of the support page.
|
|
193
|
+
* @property {string} terms The terms and conditions, in plain text or the URL displaying them.
|
|
194
|
+
* @property {string} eventTypes The URL of the list of validated event types.
|
|
195
|
+
* @property {Object} [assets] Holder for service specific Assets (icons, css, ...)
|
|
196
|
+
* @property {String} [assets.definitions] URL to json object with assets definitions
|
|
197
|
+
*/
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const utils = require('./utils.js');
|
|
2
|
+
/**
|
|
3
|
+
* Holds Pryv Service informations.
|
|
4
|
+
*
|
|
5
|
+
* It's returned by `service.assets()`
|
|
6
|
+
*
|
|
7
|
+
* @memberof Pryv
|
|
8
|
+
**/
|
|
9
|
+
class ServiceAssets {
|
|
10
|
+
/**
|
|
11
|
+
* Private => use ServiceAssets.setup()
|
|
12
|
+
* @param { object} assets The content of service/info.assets properties.
|
|
13
|
+
* @param { string } pryvServiceAssetsSourceUrl Url point to assets of the service of a Pryv platform: https://api.pryv.com/reference/#service-info property `assets.src`
|
|
14
|
+
*/
|
|
15
|
+
constructor(assets, assetsURL) {
|
|
16
|
+
this._assets = assets;
|
|
17
|
+
this._assetsURL = assetsURL;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Load Assets definition
|
|
22
|
+
* @param {string} pryvServiceAssetsSourceUrl
|
|
23
|
+
* @returns {ServiceAssets}
|
|
24
|
+
*/
|
|
25
|
+
static async setup(pryvServiceAssetsSourceUrl) {
|
|
26
|
+
const res = await utils.superagent.get(pryvServiceAssetsSourceUrl).set('accept', 'json');
|
|
27
|
+
return new ServiceAssets(res.body, pryvServiceAssetsSourceUrl);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* get a value from path separated by `:`
|
|
32
|
+
* exemple of key `lib-js:buttonSignIn`
|
|
33
|
+
* @param {string} [keyPath] if null, will return the all assets
|
|
34
|
+
*/
|
|
35
|
+
get(keyPath) {
|
|
36
|
+
let result = Object.assign({}, this._assets);
|
|
37
|
+
if (keyPath) {
|
|
38
|
+
keyPath.split(':').forEach((key) => {
|
|
39
|
+
result = result[key];
|
|
40
|
+
if (typeof result === 'undefined') return result;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* get an Url from path separated by `:`
|
|
48
|
+
* identical to doing assets.relativeURL(assets.get(keyPath))
|
|
49
|
+
* exemple of key `lib-js:buttonSignIn`
|
|
50
|
+
* @param {string} [keyPath] if null, will return the all assets
|
|
51
|
+
*/
|
|
52
|
+
getUrl(keyPath) {
|
|
53
|
+
const url = this.get(keyPath);
|
|
54
|
+
if (typeof url !== 'string') {
|
|
55
|
+
throw new Error(url + ' returned ' + value);
|
|
56
|
+
}
|
|
57
|
+
return this.relativeURL(url);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* get relativeUrl
|
|
62
|
+
*/
|
|
63
|
+
relativeURL(url) {
|
|
64
|
+
return relPathToAbs(this._assets.baseUrl || this._assetsURL, url);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//---------------- Default service ressources
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Set all defaults Favicon, CSS
|
|
71
|
+
*/
|
|
72
|
+
async setAllDefaults() {
|
|
73
|
+
this.setFavicon();
|
|
74
|
+
await this.loadCSS();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set service Favicon to Web Page
|
|
79
|
+
*/
|
|
80
|
+
setFavicon() {
|
|
81
|
+
var link = document.querySelector("link[rel*='icon']") || document.createElement('link');
|
|
82
|
+
link.type = 'image/x-icon';
|
|
83
|
+
link.rel = 'shortcut icon';
|
|
84
|
+
link.href = this.relativeURL(this._assets.favicon.default.url);
|
|
85
|
+
document.getElementsByTagName('head')[0].appendChild(link);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Set default service CSS
|
|
90
|
+
*/
|
|
91
|
+
async loadCSS() {
|
|
92
|
+
loadCSS(this.relativeURL(this._assets.css.default.url));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---- Login
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Load CSS for Login button
|
|
99
|
+
*/
|
|
100
|
+
async loginButtonLoadCSS () {
|
|
101
|
+
loadCSS(this.relativeURL(this._assets['lib-js'].buttonSignIn.css));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get HTML for Login Button
|
|
106
|
+
*/
|
|
107
|
+
async loginButtonGetHTML() {
|
|
108
|
+
const res = await utils.superagent.get(this.relativeURL(this._assets['lib-js'].buttonSignIn.html)).set('accept', 'html');
|
|
109
|
+
return res.text;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get Messages strings for Login Button
|
|
114
|
+
*/
|
|
115
|
+
async loginButtonGetMessages() {
|
|
116
|
+
const res = await utils.superagent.get(this.relativeURL(this._assets['lib-js'].buttonSignIn.messages)).set('accept', 'json');
|
|
117
|
+
return res.body;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function loadCSS(url) {
|
|
123
|
+
var head = document.getElementsByTagName('head')[0];
|
|
124
|
+
var link = document.createElement('link');
|
|
125
|
+
link.id = url;
|
|
126
|
+
link.rel = 'stylesheet';
|
|
127
|
+
link.type = 'text/css';
|
|
128
|
+
link.href = url;
|
|
129
|
+
link.media = 'all';
|
|
130
|
+
head.appendChild(link);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/*\
|
|
134
|
+
|*| Modified version of
|
|
135
|
+
|*| :: translate relative paths to absolute paths ::
|
|
136
|
+
|*|
|
|
137
|
+
|*| https://developer.mozilla.org/en-US/docs/Web/API/document.cookie
|
|
138
|
+
|*|
|
|
139
|
+
|*| The following code is released under the GNU Public License, version 3 or later.
|
|
140
|
+
|*| http://www.gnu.org/licenses/gpl-3.0-standalone.html
|
|
141
|
+
|*|
|
|
142
|
+
\*/
|
|
143
|
+
|
|
144
|
+
function relPathToAbs (baseUrlString, sRelPath) {
|
|
145
|
+
var baseLocation = location;
|
|
146
|
+
if (baseUrlString) {
|
|
147
|
+
baseLocation = document.createElement('a');
|
|
148
|
+
baseLocation.href = baseUrlString;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
var nUpLn, sDir = "", sPath = baseLocation.pathname.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, "$1"));
|
|
152
|
+
for (var nEnd, nStart = 0; nEnd = sPath.indexOf("/../", nStart), nEnd > -1; nStart = nEnd + nUpLn) {
|
|
153
|
+
nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length;
|
|
154
|
+
sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp("(?:\\\/+[^\\\/]*){0," + ((nUpLn - 1) / 3) + "}$"),
|
|
155
|
+
"/");
|
|
156
|
+
}
|
|
157
|
+
let portStr = baseLocation.port ? ':' + baseLocation.port : '';
|
|
158
|
+
return baseLocation.protocol + '//' + baseLocation.hostname + portStr + sDir + sPath.substr(nStart);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
module.exports = ServiceAssets;
|
package/src/index.html
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<title>Pryv - Light - Javascript Lib</title>
|
|
6
|
+
|
|
7
|
+
<script src="./print.bundle.js"></script>
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
-
|
|
12
|
+
<script src="./bundle.js"></script>
|
|
13
|
+
+
|
|
14
|
+
<script src="./app.bundle.js"></script>
|
|
15
|
+
</body>
|
|
16
|
+
|
|
17
|
+
</html>
|
package/src/index.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @private
|
|
4
|
+
* Replacement for getEventStreamed for Browser
|
|
5
|
+
* To be used as long as superagent does not propose it.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
async function getEventStreamed(conn, queryParam, parser) {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Holds Parser's settings
|
|
12
|
+
*/
|
|
13
|
+
const parserSettings = {
|
|
14
|
+
ondata: null,
|
|
15
|
+
onend: null,
|
|
16
|
+
encoding: 'utf8'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Mock Response
|
|
21
|
+
*/
|
|
22
|
+
const fakeRes = {
|
|
23
|
+
setEncoding : function(encoding) {
|
|
24
|
+
parserSettings.encoding = encoding;
|
|
25
|
+
}, // will receive 'data' and 'end' callbacks
|
|
26
|
+
on: function(key, f) {
|
|
27
|
+
parserSettings['on' + key] = f;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Holds results from the parser
|
|
33
|
+
*/
|
|
34
|
+
let errResult;
|
|
35
|
+
let bodyObjectResult;
|
|
36
|
+
/**
|
|
37
|
+
*
|
|
38
|
+
*/
|
|
39
|
+
parser(fakeRes, function (err, bodyObject) {
|
|
40
|
+
errResult = err;
|
|
41
|
+
bodyObjectResult = bodyObject;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
// ------------ fetch ------------------- //
|
|
46
|
+
let url = new URL(conn.endpoint + 'events');
|
|
47
|
+
url.search = new URLSearchParams(queryParam);
|
|
48
|
+
let fetchParams = {method: 'GET', headers: {Accept: 'application/json'}};
|
|
49
|
+
if (conn.token) fetchParams.headers.Authorization = conn.token;
|
|
50
|
+
|
|
51
|
+
let response = await fetch(url,fetchParams);
|
|
52
|
+
const reader = response.body.getReader();
|
|
53
|
+
|
|
54
|
+
while (true) {
|
|
55
|
+
const { done, value } = await reader.read();
|
|
56
|
+
parserSettings.ondata(new TextDecoder(parserSettings.encoding).decode(value));
|
|
57
|
+
if (done) { parserSettings.onend(); break; }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (errResult) {
|
|
61
|
+
throw new Error(errResult);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// We're done!
|
|
65
|
+
const result = {
|
|
66
|
+
text: fakeRes.text, // from the parser
|
|
67
|
+
body: bodyObjectResult, // from the parser
|
|
68
|
+
statusCode: response.status,
|
|
69
|
+
headers: {}
|
|
70
|
+
}
|
|
71
|
+
// add headers to result
|
|
72
|
+
for (var pair of response.headers.entries()) {
|
|
73
|
+
result.headers[pair[0]] = pair[1];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
module.exports = getEventStreamed;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// there two steps 1 find events, then eventDeletions
|
|
2
|
+
const EVENTMARKERS = ['"events":[', '"eventDeletions":['];
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Customize superagent parser
|
|
6
|
+
* Work on 'node.js' and use by browser-getEventStreamed
|
|
7
|
+
*/
|
|
8
|
+
module.exports = function (foreachEvent, includeDeletions) {
|
|
9
|
+
let eventOrEventDeletions = 0; // start with event
|
|
10
|
+
let buffer = ''; // temp data
|
|
11
|
+
let body = null; // to be returned
|
|
12
|
+
|
|
13
|
+
//IN EVENTS VARS
|
|
14
|
+
let depth = 0; // level of depth in brackets
|
|
15
|
+
let inString = false; // cursor is in a String
|
|
16
|
+
let skipNextOne = false; // when a backslash is found
|
|
17
|
+
let cursorPos = 0; // position of Character Cursor
|
|
18
|
+
|
|
19
|
+
// counters
|
|
20
|
+
let eventsCount = 0;
|
|
21
|
+
let eventDeletionsCount = 0;
|
|
22
|
+
|
|
23
|
+
const states = {
|
|
24
|
+
A_BEFORE_EVENTS: 0,
|
|
25
|
+
B_IN_EVENTS: 1,
|
|
26
|
+
D_AFTER_EVENTS: 2
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let state = states.A_BEFORE_EVENTS;
|
|
30
|
+
|
|
31
|
+
function processBuffer() {
|
|
32
|
+
switch (state) {
|
|
33
|
+
case states.A_BEFORE_EVENTS:
|
|
34
|
+
searchStartEvents();
|
|
35
|
+
break;
|
|
36
|
+
case states.B_IN_EVENTS:
|
|
37
|
+
processEvents();
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
afterEvents();
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function searchStartEvents() {
|
|
47
|
+
// search for "events": and happend any info before to the body
|
|
48
|
+
var n = buffer.indexOf(EVENTMARKERS[eventOrEventDeletions]);
|
|
49
|
+
if (n > 0) {
|
|
50
|
+
if (eventOrEventDeletions === 0) { // do only once
|
|
51
|
+
body = buffer.substring(0, n);
|
|
52
|
+
}
|
|
53
|
+
buffer = buffer.substr(n + EVENTMARKERS[eventOrEventDeletions].length);
|
|
54
|
+
state = states.B_IN_EVENTS;
|
|
55
|
+
processEvents();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function processEvents() {
|
|
61
|
+
/// ---- in Event
|
|
62
|
+
while (cursorPos < buffer.length && (state === states.B_IN_EVENTS)) {
|
|
63
|
+
if (skipNextOne) { // ignore next character
|
|
64
|
+
skipNextOne = false;
|
|
65
|
+
cursorPos++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
switch (buffer.charCodeAt(cursorPos)) {
|
|
69
|
+
case 93: // ]
|
|
70
|
+
if (depth === 0) { // end of events
|
|
71
|
+
if (cursorPos !== 0) {
|
|
72
|
+
throw new Error('Found trailling ] in mid-course');
|
|
73
|
+
}
|
|
74
|
+
if (eventOrEventDeletions === 0 && includeDeletions) {
|
|
75
|
+
state = states.A_BEFORE_EVENTS;
|
|
76
|
+
eventOrEventDeletions = 1; // now look for eventDeletions
|
|
77
|
+
return;
|
|
78
|
+
} else { // done
|
|
79
|
+
state = states.D_AFTER_EVENTS;
|
|
80
|
+
let eventsOrDeletionMsg = '';
|
|
81
|
+
if (eventOrEventDeletions === 1) {
|
|
82
|
+
eventsOrDeletionMsg = '"eventDeletionsCount":' + eventDeletionsCount + ','
|
|
83
|
+
}
|
|
84
|
+
buffer = eventsOrDeletionMsg + '"eventsCount":' + eventsCount + '' + buffer.substr(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
case 92: // \
|
|
89
|
+
skipNextOne = true;
|
|
90
|
+
break;
|
|
91
|
+
case 123: // {
|
|
92
|
+
if (!inString) depth++;
|
|
93
|
+
break;
|
|
94
|
+
case 34: // "
|
|
95
|
+
inString = !inString;
|
|
96
|
+
break;
|
|
97
|
+
case 125: // }
|
|
98
|
+
if (!inString) depth--;
|
|
99
|
+
if (depth === 0) {
|
|
100
|
+
// ignore possible coma ',' if first char
|
|
101
|
+
const ignoreComa = (buffer.charCodeAt(0) === 44) ? 1 : 0;
|
|
102
|
+
const eventStr = buffer.substring(ignoreComa, cursorPos + 1);
|
|
103
|
+
|
|
104
|
+
if (eventOrEventDeletions === 0) {
|
|
105
|
+
eventsCount++;
|
|
106
|
+
} else {
|
|
107
|
+
eventDeletionsCount++;
|
|
108
|
+
}
|
|
109
|
+
buffer = buffer.substr(cursorPos + 1 );
|
|
110
|
+
addEvent(eventStr);
|
|
111
|
+
cursorPos = -1;
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
cursorPos++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function afterEvents() {
|
|
120
|
+
// just happend the end of message;
|
|
121
|
+
body += buffer;
|
|
122
|
+
buffer = '';
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return function (res, fn) {
|
|
127
|
+
res.setEncoding('utf8'); // Already UTF8 in browsers
|
|
128
|
+
res.on('data', chunk => {
|
|
129
|
+
buffer += chunk;
|
|
130
|
+
processBuffer();
|
|
131
|
+
});
|
|
132
|
+
res.on('end', () => {
|
|
133
|
+
let err;
|
|
134
|
+
let bodyObject;
|
|
135
|
+
try {
|
|
136
|
+
res.text = body + buffer;
|
|
137
|
+
bodyObject = res.text && JSON.parse(res.text);
|
|
138
|
+
} catch (err_) {
|
|
139
|
+
err = err_;
|
|
140
|
+
// issue #675: return the raw response if the response parsing fails
|
|
141
|
+
err.rawResponse = res.text || null;
|
|
142
|
+
// issue #876: return the http status code if the response parsing fails
|
|
143
|
+
err.statusCode = res.statusCode;
|
|
144
|
+
} finally {
|
|
145
|
+
fn(err, bodyObject);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
/// --- Direct Push
|
|
152
|
+
function addEvent(strEvent) {
|
|
153
|
+
foreachEvent(JSON.parse(strEvent));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
};
|