pryv 3.1.0 → 3.3.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 +2 -0
- package/package.json +1 -1
- package/src/index.d.ts +15 -0
- package/src/utils.js +82 -0
- package/test/utils.test.js +37 -0
package/README.md
CHANGED
|
@@ -57,6 +57,7 @@ Other distributions available:
|
|
|
57
57
|
|
|
58
58
|
- Socket.IO: [NPM package](https://www.npmjs.com/package/@pryv/socket.io), [README](https://github.com/pryv/lib-js/tree/master/components/pryv-socket.io#readme)
|
|
59
59
|
- Monitor: [NPM package](https://www.npmjs.com/package/@pryv/monitor), [README](https://github.com/pryv/lib-js/tree/master/components/pryv-monitor#readme)
|
|
60
|
+
- CMC (Cross-account Messaging & Consent) client helpers: [NPM package](https://www.npmjs.com/package/@pryv/cmc), [README](https://github.com/pryv/lib-js/tree/master/components/pryv-cmc#readme)
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
### Quick example
|
|
@@ -677,6 +678,7 @@ The project is structured as a monorepo with components (a.k.a. workspaces in NP
|
|
|
677
678
|
- `pryv`: the library
|
|
678
679
|
- `pryv-socket.io`: Socket.IO add-on
|
|
679
680
|
- `pryv-monitor`: Monitor add-on
|
|
681
|
+
- `pryv-cmc`: CMC (Cross-account Messaging & Consent) client helpers
|
|
680
682
|
|
|
681
683
|
|
|
682
684
|
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -1069,10 +1069,24 @@ declare module 'pryv' {
|
|
|
1069
1069
|
token: string | null;
|
|
1070
1070
|
};
|
|
1071
1071
|
|
|
1072
|
+
/**
|
|
1073
|
+
* Decomposed form of an APIEndpoint, returned by
|
|
1074
|
+
* `pryv.utils.decomposeAPIEndpoint`. `host` is the canonical platform
|
|
1075
|
+
* host (no `<username>.` subdomain prefix in subdomain-style
|
|
1076
|
+
* deployments) — the same identity cross-account features (CMC
|
|
1077
|
+
* counterparty slugs, etc.) key on.
|
|
1078
|
+
*/
|
|
1079
|
+
export type DecomposedAPIEndpoint = {
|
|
1080
|
+
token: string | null;
|
|
1081
|
+
username: string | null;
|
|
1082
|
+
host: string;
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1072
1085
|
export const utils: {
|
|
1073
1086
|
isBrowser(): boolean;
|
|
1074
1087
|
extractTokenAndAPIEndpoint(apiEndpoint: string): TokenAndAPIEndpoint;
|
|
1075
1088
|
buildAPIEndpoint(tokenAndAPI: TokenAndAPIEndpoint): string;
|
|
1089
|
+
decomposeAPIEndpoint(apiEndpoint: string, serviceInfoApi: string): DecomposedAPIEndpoint;
|
|
1076
1090
|
browserIsMobileOrTablet(navigator?: string | Navigator): boolean;
|
|
1077
1091
|
cleanURLFromPrYvParams(url: string): string;
|
|
1078
1092
|
getQueryParamsFromURL(url: string): KeyValue;
|
|
@@ -1107,6 +1121,7 @@ declare module 'pryv' {
|
|
|
1107
1121
|
isBrowser(): boolean;
|
|
1108
1122
|
extractTokenAndAPIEndpoint(apiEndpoint: string): TokenAndAPIEndpoint;
|
|
1109
1123
|
buildAPIEndpoint(tokenAndAPI: TokenAndAPIEndpoint): string;
|
|
1124
|
+
decomposeAPIEndpoint(apiEndpoint: string, serviceInfoApi: string): DecomposedAPIEndpoint;
|
|
1110
1125
|
browserIsMobileOrTablet(navigator?: string | Navigator): boolean;
|
|
1111
1126
|
cleanURLFromPrYvParams(url: string): string;
|
|
1112
1127
|
getQueryParamsFromURL(url: string): KeyValue;
|
package/src/utils.js
CHANGED
|
@@ -127,6 +127,76 @@ const utils = module.exports = {
|
|
|
127
127
|
return res[1] + '://' + tokenAndAPI.token + '@' + res[2];
|
|
128
128
|
},
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Decompose a Pryv apiEndpoint into `{ token, username, host }` using
|
|
132
|
+
* the platform's `service.info.api` URL template to invert whichever
|
|
133
|
+
* username placement the platform uses (subdomain vs path-style).
|
|
134
|
+
*
|
|
135
|
+
* Pryv apiEndpoints follow one of two URL shapes (the difference is
|
|
136
|
+
* platform-defined, encoded in `service.info.api`):
|
|
137
|
+
*
|
|
138
|
+
* subdomain template `https://{username}.<domain>/`
|
|
139
|
+
* endpoint `https://<token>@<username>.<domain>/`
|
|
140
|
+
* path-style template `https://<host>/{username}/`
|
|
141
|
+
* endpoint `https://<token>@<host>/<username>/`
|
|
142
|
+
*
|
|
143
|
+
* Returns the **canonical platform host** (no `<username>.` subdomain
|
|
144
|
+
* prefix in subdomain mode) — the identity cross-account features
|
|
145
|
+
* (e.g. CMC counterparty slugs) key on, regardless of which user the
|
|
146
|
+
* endpoint belongs to.
|
|
147
|
+
*
|
|
148
|
+
* `token` and `username` are null when the endpoint carries no token /
|
|
149
|
+
* when the endpoint shape doesn't match the template; `host` is
|
|
150
|
+
* best-effort in that case.
|
|
151
|
+
*
|
|
152
|
+
* @memberof pryv.utils
|
|
153
|
+
* @param {APIEndpoint} apiEndpoint e.g. 'https://t0k3n@alice.pryv.me/'
|
|
154
|
+
* @param {string} serviceInfoApi e.g. 'https://{username}.pryv.me/'
|
|
155
|
+
* from /service/info → field `api`.
|
|
156
|
+
* @returns {DecomposedAPIEndpoint}
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* const conn = new pryv.Connection(apiEndpoint);
|
|
160
|
+
* const info = await conn.service.info();
|
|
161
|
+
* const me = pryv.utils.decomposeAPIEndpoint(apiEndpoint, info.api);
|
|
162
|
+
* // → { token: 't0k3n', username: 'alice', host: 'pryv.me' }
|
|
163
|
+
*/
|
|
164
|
+
decomposeAPIEndpoint: function (apiEndpoint, serviceInfoApi) {
|
|
165
|
+
const { token, endpoint } = utils.extractTokenAndAPIEndpoint(apiEndpoint);
|
|
166
|
+
const tplIdx = serviceInfoApi.indexOf('{username}');
|
|
167
|
+
if (tplIdx < 0) {
|
|
168
|
+
// Template doesn't carry {username} — operator-defined, can't decompose.
|
|
169
|
+
let host = '';
|
|
170
|
+
try { host = new URL(endpoint).host; } catch (_e) {}
|
|
171
|
+
return { token, username: null, host };
|
|
172
|
+
}
|
|
173
|
+
const tplPrefix = serviceInfoApi.slice(0, tplIdx);
|
|
174
|
+
const tplSuffix = serviceInfoApi.slice(tplIdx + '{username}'.length);
|
|
175
|
+
if (!endpoint.startsWith(tplPrefix) || !endpoint.endsWith(tplSuffix)) {
|
|
176
|
+
let host = '';
|
|
177
|
+
try { host = new URL(endpoint).host; } catch (_e) {}
|
|
178
|
+
return { token, username: null, host };
|
|
179
|
+
}
|
|
180
|
+
const username = endpoint.slice(tplPrefix.length, endpoint.length - tplSuffix.length);
|
|
181
|
+
// Disambiguate by where {username} sits in the template:
|
|
182
|
+
// - subdomain → prefix ends with `://` (username right after scheme)
|
|
183
|
+
// - path-style → prefix has more after `://` (host already in prefix)
|
|
184
|
+
const isSubdomainTemplate = /:\/\/$/.test(tplPrefix);
|
|
185
|
+
let host;
|
|
186
|
+
if (isSubdomainTemplate) {
|
|
187
|
+
// tplSuffix starts with the domain (e.g. '.pryv.me/')
|
|
188
|
+
host = tplSuffix.replace(/^\.+/, '').replace(/\/+$/, '');
|
|
189
|
+
} else {
|
|
190
|
+
// path-style — host is in the prefix's URL
|
|
191
|
+
try {
|
|
192
|
+
host = new URL(tplPrefix).host;
|
|
193
|
+
} catch (_e) {
|
|
194
|
+
host = '';
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return { token, username, host };
|
|
198
|
+
},
|
|
199
|
+
|
|
130
200
|
/**
|
|
131
201
|
* Check if the browser is running on a mobile device or tablet
|
|
132
202
|
* @memberof pryv.utils
|
|
@@ -251,6 +321,18 @@ utils.buildPryvApiEndpoint = utils.buildAPIEndpoint;
|
|
|
251
321
|
* @typedef {string} APIEndpoint
|
|
252
322
|
*/
|
|
253
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Decomposed form of an APIEndpoint, returned by `decomposeAPIEndpoint`.
|
|
326
|
+
* `token` and `username` are `null` when the endpoint doesn't carry a
|
|
327
|
+
* token / doesn't match the `service.info.api` template. `host` is the
|
|
328
|
+
* canonical platform host (no `<username>.` subdomain prefix in
|
|
329
|
+
* subdomain-style deployments).
|
|
330
|
+
* @typedef {Object} DecomposedAPIEndpoint
|
|
331
|
+
* @property {string|null} token
|
|
332
|
+
* @property {string|null} username
|
|
333
|
+
* @property {string} host
|
|
334
|
+
*/
|
|
335
|
+
|
|
254
336
|
/**
|
|
255
337
|
* Common Meta are returned by each standard call on the API https://api.pryv.com/reference/#in-method-results
|
|
256
338
|
* @typedef {Object} CommonMeta
|
package/test/utils.test.js
CHANGED
|
@@ -103,4 +103,41 @@ describe('[UTLX] utils', function () {
|
|
|
103
103
|
expect(err.data.currentSerial).to.equal(2);
|
|
104
104
|
expect(err.name).to.equal('StaleAccessIdError');
|
|
105
105
|
});
|
|
106
|
+
|
|
107
|
+
describe('[UTLM] decomposeAPIEndpoint', function () {
|
|
108
|
+
it('[UTLMA] subdomain template — host strips the {username}. prefix', function () {
|
|
109
|
+
const r = pryv.utils.decomposeAPIEndpoint(
|
|
110
|
+
'https://t0k3n@alice.pryv.me/',
|
|
111
|
+
'https://{username}.pryv.me/'
|
|
112
|
+
);
|
|
113
|
+
expect(r).to.eql({ token: 't0k3n', username: 'alice', host: 'pryv.me' });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('[UTLMB] path-style template — host is the literal hostname[:port]', function () {
|
|
117
|
+
const r = pryv.utils.decomposeAPIEndpoint(
|
|
118
|
+
'http://t0k3n@127.0.0.1:3000/alice/',
|
|
119
|
+
'http://127.0.0.1:3000/{username}/'
|
|
120
|
+
);
|
|
121
|
+
expect(r).to.eql({ token: 't0k3n', username: 'alice', host: '127.0.0.1:3000' });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('[UTLMC] returns username:null when the endpoint doesn\'t match the template', function () {
|
|
125
|
+
const r = pryv.utils.decomposeAPIEndpoint(
|
|
126
|
+
'https://t0k3n@somewhere.else.com/',
|
|
127
|
+
'https://{username}.pryv.me/'
|
|
128
|
+
);
|
|
129
|
+
expect(r.username).to.equal(null);
|
|
130
|
+
expect(r.token).to.equal('t0k3n');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('[UTLMD] returns token:null when endpoint carries no token', function () {
|
|
134
|
+
const r = pryv.utils.decomposeAPIEndpoint(
|
|
135
|
+
'https://alice.pryv.me/',
|
|
136
|
+
'https://{username}.pryv.me/'
|
|
137
|
+
);
|
|
138
|
+
expect(r.token).to.equal(null);
|
|
139
|
+
expect(r.username).to.equal('alice');
|
|
140
|
+
expect(r.host).to.equal('pryv.me');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
106
143
|
});
|