@muze-nl/metro-oidc 0.3.1 → 0.3.3
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/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/browser.js +108 -61
- package/dist/browser.js.map +4 -4
- package/dist/browser.min.js +1 -1
- package/dist/browser.min.js.map +4 -4
- package/package.json +2 -2
- package/src/oidcmw.mjs +0 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-2025 Auke van Slooten
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Metro OpenID Connect middleware
|
|
2
|
+
|
|
3
|
+
[![Project stage: Experimental][project-stage-badge: Experimental]][project-stage-page]
|
|
4
|
+
|
|
5
|
+
The OpenID Connect middleware allows you to configure a [metro client](https://github.com/muze-nl/metro) to handle authorization and authentication using OpenID Connect:
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import oidcmw from '@muze-nl/metro-oidc'
|
|
9
|
+
|
|
10
|
+
const client = metro.client('https://oauth2api.example.com')
|
|
11
|
+
.with( oidcmw({
|
|
12
|
+
client_info: {
|
|
13
|
+
client_name: 'My Client',
|
|
14
|
+
redirect_uris: [
|
|
15
|
+
'https://www.example.com/my_app.html'
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
issuer: 'https://solidcommunity.net/'
|
|
19
|
+
}) )
|
|
20
|
+
|
|
21
|
+
async function fetchMovies() {
|
|
22
|
+
return await client.get('https://example.solidcommunity.net/movies/')
|
|
23
|
+
}
|
|
24
|
+
````
|
|
25
|
+
|
|
26
|
+
The OIDC middleware will automatically discover the configuration of the issuer, as well as do a dynamic client registration, if you haven't set a `client_info.client_id`.
|
|
27
|
+
It will then configure the correct OAuth2 settings and handle the request with [metro oauth2](https://github.com/muze-nl/metro-oauth2) middleware. It may redirect the browser to let the user login with the OIDC issuer. You can skip the automatic configuration step, if you provide the `openid_configuration` parameter set yourself. If you don't, the oidcmw middleware will only run the discovery process once, and store the information in localStorage. The same with the client_info and dynamic registration.
|
|
28
|
+
|
|
29
|
+
## Security features
|
|
30
|
+
|
|
31
|
+
metro.oidc uses OAuth2.1 by default, including PKCE and DPoP. The Keypair used in DPoP is created non-extractable, so they cannot be leaked. This means that the access_token and refresh_token, even if leaked, cannot be used anywhere else.
|
|
32
|
+
|
|
33
|
+
You can disable PCKE by setting `options.client_info.code_verifier` to false.
|
|
34
|
+
You can disable DPoP by setting `options.use_dpop` to false.
|
|
35
|
+
|
|
36
|
+
## id_token
|
|
37
|
+
|
|
38
|
+
The metro oidc middleware doesn't use the id_token, or verify any of its contents. This is left up to you, if you need it. You can retrieve the id_token by calling the `idToken` function, after the user is logged in:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import oidc from '@muze-nl/metro-oidc'
|
|
42
|
+
|
|
43
|
+
let id_token = oidc.idToken()
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
[project-stage-badge: Experimental]: https://img.shields.io/badge/Project%20Stage-Experimental-yellow.svg
|
|
47
|
+
[project-stage-page]: https://blog.pother.ca/project-stages/
|
package/dist/browser.js
CHANGED
|
@@ -591,19 +591,21 @@
|
|
|
591
591
|
thrower
|
|
592
592
|
}
|
|
593
593
|
});
|
|
594
|
-
globalThis.metro
|
|
594
|
+
if (!globalThis.metro) {
|
|
595
|
+
globalThis.metro = metro;
|
|
596
|
+
}
|
|
595
597
|
var everything_default = metro;
|
|
596
598
|
|
|
597
599
|
// node_modules/@muze-nl/metro-oauth2/src/oauth2.mjs
|
|
598
600
|
var oauth2_exports = {};
|
|
599
601
|
__export(oauth2_exports, {
|
|
600
|
-
authorizePopup: () => authorizePopup,
|
|
601
602
|
base64url_encode: () => base64url_encode,
|
|
602
603
|
createState: () => createState,
|
|
603
604
|
default: () => oauth2mw,
|
|
604
605
|
generateCodeChallenge: () => generateCodeChallenge,
|
|
605
606
|
generateCodeVerifier: () => generateCodeVerifier,
|
|
606
607
|
getExpires: () => getExpires,
|
|
608
|
+
isAuthorized: () => isAuthorized,
|
|
607
609
|
isExpired: () => isExpired,
|
|
608
610
|
isRedirected: () => isRedirected
|
|
609
611
|
});
|
|
@@ -630,15 +632,15 @@
|
|
|
630
632
|
return false;
|
|
631
633
|
}
|
|
632
634
|
}
|
|
633
|
-
return
|
|
635
|
+
return error2("data does not match oneOf patterns", data, patterns);
|
|
634
636
|
};
|
|
635
637
|
var anyOf = (...patterns) => (data) => {
|
|
636
638
|
if (!Array.isArray(data)) {
|
|
637
|
-
return
|
|
639
|
+
return error2("data is not an array", data, "anyOf");
|
|
638
640
|
}
|
|
639
641
|
for (let value of data) {
|
|
640
642
|
if (oneOf(...patterns)(value)) {
|
|
641
|
-
return
|
|
643
|
+
return error2("data does not match anyOf patterns", value, patterns);
|
|
642
644
|
}
|
|
643
645
|
}
|
|
644
646
|
return false;
|
|
@@ -650,10 +652,10 @@
|
|
|
650
652
|
}
|
|
651
653
|
let url2 = new URL(data);
|
|
652
654
|
if (url2.href != data) {
|
|
653
|
-
return
|
|
655
|
+
return error2("data is not a valid url", data, "validURL");
|
|
654
656
|
}
|
|
655
657
|
} catch (e) {
|
|
656
|
-
return
|
|
658
|
+
return error2("data is not a valid url", data, "validURL");
|
|
657
659
|
}
|
|
658
660
|
return false;
|
|
659
661
|
}
|
|
@@ -664,28 +666,28 @@
|
|
|
664
666
|
let problems = [];
|
|
665
667
|
if (pattern === Boolean) {
|
|
666
668
|
if (typeof data != "boolean") {
|
|
667
|
-
problems.push(
|
|
669
|
+
problems.push(error2("data is not a boolean", data, pattern));
|
|
668
670
|
}
|
|
669
671
|
} else if (pattern === Number) {
|
|
670
672
|
if (typeof data != "number") {
|
|
671
|
-
problems.push(
|
|
673
|
+
problems.push(error2("data is not a number", data, pattern));
|
|
672
674
|
}
|
|
673
675
|
} else if (pattern instanceof RegExp) {
|
|
674
676
|
if (Array.isArray(data)) {
|
|
675
677
|
let index = data.findIndex((element) => fails(element, pattern, root));
|
|
676
678
|
if (index > -1) {
|
|
677
|
-
problems.push(
|
|
679
|
+
problems.push(error2("data[" + index + "] does not match pattern", data[index], pattern));
|
|
678
680
|
}
|
|
679
681
|
} else if (!pattern.test(data)) {
|
|
680
|
-
problems.push(
|
|
682
|
+
problems.push(error2("data does not match pattern", data, pattern));
|
|
681
683
|
}
|
|
682
684
|
} else if (pattern instanceof Function) {
|
|
683
685
|
if (pattern(data, root)) {
|
|
684
|
-
problems.push(
|
|
686
|
+
problems.push(error2("data does not match function", data, pattern));
|
|
685
687
|
}
|
|
686
688
|
} else if (Array.isArray(pattern)) {
|
|
687
689
|
if (!Array.isArray(data)) {
|
|
688
|
-
problems.push(
|
|
690
|
+
problems.push(error2("data is not an array", data, []));
|
|
689
691
|
}
|
|
690
692
|
for (p of pattern) {
|
|
691
693
|
let problem = fails(data, p, root);
|
|
@@ -699,10 +701,10 @@
|
|
|
699
701
|
if (Array.isArray(data)) {
|
|
700
702
|
let index = data.findIndex((element) => fails(element, pattern, root));
|
|
701
703
|
if (index > -1) {
|
|
702
|
-
problems.push(
|
|
704
|
+
problems.push(error2("data[" + index + "] does not match pattern", data[index], pattern));
|
|
703
705
|
}
|
|
704
706
|
} else if (!data || typeof data != "object") {
|
|
705
|
-
problems.push(
|
|
707
|
+
problems.push(error2("data is not an object, pattern is", data, pattern));
|
|
706
708
|
} else {
|
|
707
709
|
if (data instanceof URLSearchParams) {
|
|
708
710
|
data = Object.fromEntries(data);
|
|
@@ -713,7 +715,7 @@
|
|
|
713
715
|
if (result) {
|
|
714
716
|
if (!p2 || typeof p2 == "string") {
|
|
715
717
|
p2 = {};
|
|
716
|
-
problems.push(
|
|
718
|
+
problems.push(error2(p2, data[wKey], wVal));
|
|
717
719
|
}
|
|
718
720
|
p2[wKey] = result.problems;
|
|
719
721
|
}
|
|
@@ -721,7 +723,7 @@
|
|
|
721
723
|
}
|
|
722
724
|
} else {
|
|
723
725
|
if (pattern != data) {
|
|
724
|
-
problems.push(
|
|
726
|
+
problems.push(error2("data and pattern are not equal", data, pattern));
|
|
725
727
|
}
|
|
726
728
|
}
|
|
727
729
|
if (problems.length) {
|
|
@@ -736,7 +738,7 @@
|
|
|
736
738
|
this.details = details;
|
|
737
739
|
}
|
|
738
740
|
};
|
|
739
|
-
function
|
|
741
|
+
function error2(message, found, expected) {
|
|
740
742
|
return {
|
|
741
743
|
message,
|
|
742
744
|
found,
|
|
@@ -1106,13 +1108,19 @@
|
|
|
1106
1108
|
}
|
|
1107
1109
|
return true;
|
|
1108
1110
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1111
|
+
function isAuthorized(tokens) {
|
|
1112
|
+
if (typeof tokens == "string") {
|
|
1113
|
+
tokens = tokenStore(tokens).tokens;
|
|
1114
|
+
}
|
|
1115
|
+
let accessToken = tokens.get("access_token");
|
|
1116
|
+
if (accessToken && !isExpired(accessToken)) {
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
let refreshToken = tokens.get("refresh_token");
|
|
1120
|
+
if (refreshToken) {
|
|
1121
|
+
return true;
|
|
1122
|
+
}
|
|
1123
|
+
return false;
|
|
1116
1124
|
}
|
|
1117
1125
|
|
|
1118
1126
|
// node_modules/@muze-nl/metro-oauth2/src/oauth2.mockserver.mjs
|
|
@@ -1127,7 +1135,7 @@
|
|
|
1127
1135
|
"Content-Type": "application/json"
|
|
1128
1136
|
}
|
|
1129
1137
|
};
|
|
1130
|
-
var badRequest = (
|
|
1138
|
+
var badRequest = (error5) => {
|
|
1131
1139
|
return {
|
|
1132
1140
|
status: 400,
|
|
1133
1141
|
statusText: "Bad Request",
|
|
@@ -1136,11 +1144,11 @@
|
|
|
1136
1144
|
},
|
|
1137
1145
|
body: JSON.stringify({
|
|
1138
1146
|
error: "invalid_request",
|
|
1139
|
-
error_description:
|
|
1147
|
+
error_description: error5
|
|
1140
1148
|
})
|
|
1141
1149
|
};
|
|
1142
1150
|
};
|
|
1143
|
-
var
|
|
1151
|
+
var error3;
|
|
1144
1152
|
var pkce = {};
|
|
1145
1153
|
function oauth2mockserver(options = {}) {
|
|
1146
1154
|
const defaultOptions = {
|
|
@@ -1152,12 +1160,12 @@
|
|
|
1152
1160
|
let url2 = everything_default.url(req.url);
|
|
1153
1161
|
switch (url2.pathname) {
|
|
1154
1162
|
case "/authorize/":
|
|
1155
|
-
if (
|
|
1163
|
+
if (error3 = fails(url2.searchParams, {
|
|
1156
1164
|
response_type: "code",
|
|
1157
1165
|
client_id: "mockClientId",
|
|
1158
1166
|
state: Optional(/.*/)
|
|
1159
1167
|
})) {
|
|
1160
|
-
return everything_default.response(badRequest(
|
|
1168
|
+
return everything_default.response(badRequest(error3));
|
|
1161
1169
|
}
|
|
1162
1170
|
if (url2.searchParams.has("code_challenge")) {
|
|
1163
1171
|
if (!url2.searchParams.has("code_challenge_method")) {
|
|
@@ -1179,17 +1187,17 @@
|
|
|
1179
1187
|
req.data.forEach((value, key) => body[key] = value);
|
|
1180
1188
|
req = req.with({ body });
|
|
1181
1189
|
}
|
|
1182
|
-
if (
|
|
1190
|
+
if (error3 = fails(req, {
|
|
1183
1191
|
method: "POST",
|
|
1184
1192
|
data: {
|
|
1185
1193
|
grant_type: oneOf("refresh_token", "authorization_code")
|
|
1186
1194
|
}
|
|
1187
1195
|
})) {
|
|
1188
|
-
return everything_default.response(badRequest(
|
|
1196
|
+
return everything_default.response(badRequest(error3));
|
|
1189
1197
|
}
|
|
1190
1198
|
switch (req.data.grant_type) {
|
|
1191
1199
|
case "refresh_token":
|
|
1192
|
-
if (
|
|
1200
|
+
if (error3 = fails(req.data, oneOf({
|
|
1193
1201
|
refresh_token: "mockRefreshToken",
|
|
1194
1202
|
client_id: "mockClientId",
|
|
1195
1203
|
client_secret: "mockClientSecret"
|
|
@@ -1198,11 +1206,11 @@
|
|
|
1198
1206
|
client_id: "mockClientId",
|
|
1199
1207
|
code_verifier: /.+/
|
|
1200
1208
|
}))) {
|
|
1201
|
-
return everything_default.response(badRequest(
|
|
1209
|
+
return everything_default.response(badRequest(error3));
|
|
1202
1210
|
}
|
|
1203
1211
|
break;
|
|
1204
1212
|
case "access_token":
|
|
1205
|
-
if (
|
|
1213
|
+
if (error3 = fails(req.data, oneOf({
|
|
1206
1214
|
client_id: "mockClientId",
|
|
1207
1215
|
client_secret: "mockClientSecret"
|
|
1208
1216
|
}, {
|
|
@@ -1211,7 +1219,7 @@
|
|
|
1211
1219
|
//FIXME: check that this matches code_verifier
|
|
1212
1220
|
code_challenge_method: "S256"
|
|
1213
1221
|
}))) {
|
|
1214
|
-
return everything_default.response(badRequest(
|
|
1222
|
+
return everything_default.response(badRequest(error3));
|
|
1215
1223
|
}
|
|
1216
1224
|
break;
|
|
1217
1225
|
}
|
|
@@ -1327,6 +1335,43 @@
|
|
|
1327
1335
|
throw everything_default.metroError("metro.oidcmw: Error while fetching " + issuer + ".wellknown/oauth_authorization_server", res);
|
|
1328
1336
|
}
|
|
1329
1337
|
|
|
1338
|
+
// node_modules/@muze-nl/metro-oauth2/src/oauth2.popup.mjs
|
|
1339
|
+
function handleRedirect() {
|
|
1340
|
+
let params2 = new URLSearchParams(window.location.search);
|
|
1341
|
+
if (!params2.has("code") && window.location.hash) {
|
|
1342
|
+
let query = window.location.hash.substr(1);
|
|
1343
|
+
params2 = new URLSearchParams("?" + query);
|
|
1344
|
+
}
|
|
1345
|
+
let parent = window.parent ? window.parent : window.opener;
|
|
1346
|
+
if (params2.has("code")) {
|
|
1347
|
+
parent.postMessage({
|
|
1348
|
+
authorization_code: params2.get("code")
|
|
1349
|
+
}, window.location.origin);
|
|
1350
|
+
} else if (params2.has("error")) {
|
|
1351
|
+
parent.postMessage({
|
|
1352
|
+
error
|
|
1353
|
+
}, window.location.origin);
|
|
1354
|
+
} else {
|
|
1355
|
+
parent.postMessage({
|
|
1356
|
+
error: "Could not find an authorization_code"
|
|
1357
|
+
}, window.location.origin);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
function authorizePopup(authorizationCodeURL) {
|
|
1361
|
+
return new Promise((resolve, reject) => {
|
|
1362
|
+
addEventListener("message", (evt) => {
|
|
1363
|
+
if (event.data.authorization_code) {
|
|
1364
|
+
resolve(event.data.authorization_code);
|
|
1365
|
+
} else if (event.data.error) {
|
|
1366
|
+
reject(event.data.error);
|
|
1367
|
+
} else {
|
|
1368
|
+
reject("Unknown authorization error");
|
|
1369
|
+
}
|
|
1370
|
+
}, { once: true });
|
|
1371
|
+
window.open(authorizationCodeURL);
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1330
1375
|
// node_modules/@muze-nl/metro-oauth2/src/keysstore.mjs
|
|
1331
1376
|
function keysStore() {
|
|
1332
1377
|
return new Promise((resolve, reject) => {
|
|
@@ -1636,7 +1681,9 @@
|
|
|
1636
1681
|
discover: oauth2_discovery_exports,
|
|
1637
1682
|
tokenstore: tokenStore,
|
|
1638
1683
|
dpopmw,
|
|
1639
|
-
keysstore: keysStore
|
|
1684
|
+
keysstore: keysStore,
|
|
1685
|
+
authorizePopup,
|
|
1686
|
+
popupHandleRedirect: handleRedirect
|
|
1640
1687
|
});
|
|
1641
1688
|
if (!globalThis.metro.oauth2) {
|
|
1642
1689
|
globalThis.metro.oauth2 = oauth2;
|
|
@@ -1665,7 +1712,7 @@
|
|
|
1665
1712
|
function Required2(pattern) {
|
|
1666
1713
|
return function _Required(data, root, path) {
|
|
1667
1714
|
if (data == null || typeof data == "undefined") {
|
|
1668
|
-
return
|
|
1715
|
+
return error4("data is required", data, pattern || "any value", path);
|
|
1669
1716
|
} else if (typeof pattern != "undefined") {
|
|
1670
1717
|
return fails2(data, pattern, root, path);
|
|
1671
1718
|
} else {
|
|
@@ -1690,17 +1737,17 @@
|
|
|
1690
1737
|
return false;
|
|
1691
1738
|
}
|
|
1692
1739
|
}
|
|
1693
|
-
return
|
|
1740
|
+
return error4("data does not match oneOf patterns", data, patterns, path);
|
|
1694
1741
|
};
|
|
1695
1742
|
}
|
|
1696
1743
|
function anyOf2(...patterns) {
|
|
1697
1744
|
return function _anyOf(data, root, path) {
|
|
1698
1745
|
if (!Array.isArray(data)) {
|
|
1699
|
-
return
|
|
1746
|
+
return error4("data is not an array", data, "anyOf", path);
|
|
1700
1747
|
}
|
|
1701
1748
|
for (let value of data) {
|
|
1702
1749
|
if (oneOf2(...patterns)(value)) {
|
|
1703
|
-
return
|
|
1750
|
+
return error4("data does not match anyOf patterns", value, patterns, path);
|
|
1704
1751
|
}
|
|
1705
1752
|
}
|
|
1706
1753
|
return false;
|
|
@@ -1714,7 +1761,7 @@
|
|
|
1714
1761
|
}
|
|
1715
1762
|
problems = problems.filter(Boolean);
|
|
1716
1763
|
if (problems.length) {
|
|
1717
|
-
return
|
|
1764
|
+
return error4("data does not match all given patterns", data, patterns, path, problems);
|
|
1718
1765
|
}
|
|
1719
1766
|
};
|
|
1720
1767
|
}
|
|
@@ -1726,29 +1773,29 @@
|
|
|
1726
1773
|
let url2 = new URL(data);
|
|
1727
1774
|
if (url2.href != data) {
|
|
1728
1775
|
if (!(url2.href + "/" == data || url2.href == data + "/")) {
|
|
1729
|
-
return
|
|
1776
|
+
return error4("data is not a valid url", data, "validURL", path);
|
|
1730
1777
|
}
|
|
1731
1778
|
}
|
|
1732
1779
|
} catch (e) {
|
|
1733
|
-
return
|
|
1780
|
+
return error4("data is not a valid url", data, "validURL", path);
|
|
1734
1781
|
}
|
|
1735
1782
|
}
|
|
1736
1783
|
function validEmail(data, root, path) {
|
|
1737
1784
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data)) {
|
|
1738
|
-
return
|
|
1785
|
+
return error4("data is not a valid email", data, "validEmail", path);
|
|
1739
1786
|
}
|
|
1740
1787
|
}
|
|
1741
1788
|
function instanceOf(constructor) {
|
|
1742
1789
|
return function _instanceOf(data, root, path) {
|
|
1743
1790
|
if (!(data instanceof constructor)) {
|
|
1744
|
-
return
|
|
1791
|
+
return error4("data is not an instanceof pattern", data, constructor, path);
|
|
1745
1792
|
}
|
|
1746
1793
|
};
|
|
1747
1794
|
}
|
|
1748
1795
|
function not(pattern) {
|
|
1749
1796
|
return function _not(data, root, path) {
|
|
1750
1797
|
if (!fails2(data, pattern, root, path)) {
|
|
1751
|
-
return
|
|
1798
|
+
return error4("data matches pattern, when required not to", data, pattern, path);
|
|
1752
1799
|
}
|
|
1753
1800
|
};
|
|
1754
1801
|
}
|
|
@@ -1759,29 +1806,29 @@
|
|
|
1759
1806
|
let problems = [];
|
|
1760
1807
|
if (pattern === Boolean) {
|
|
1761
1808
|
if (typeof data != "boolean" && !(data instanceof Boolean)) {
|
|
1762
|
-
problems.push(
|
|
1809
|
+
problems.push(error4("data is not a boolean", data, pattern, path));
|
|
1763
1810
|
}
|
|
1764
1811
|
} else if (pattern === Number) {
|
|
1765
1812
|
if (typeof data != "number" && !(data instanceof Number)) {
|
|
1766
|
-
problems.push(
|
|
1813
|
+
problems.push(error4("data is not a number", data, pattern, path));
|
|
1767
1814
|
}
|
|
1768
1815
|
} else if (pattern === String) {
|
|
1769
1816
|
if (typeof data != "string" && !(data instanceof String)) {
|
|
1770
|
-
problems.push(
|
|
1817
|
+
problems.push(error4("data is not a string", data, pattern, path));
|
|
1771
1818
|
}
|
|
1772
1819
|
if (data == "") {
|
|
1773
|
-
problems.push(
|
|
1820
|
+
problems.push(error4("data is an empty string, which is not allowed", data, pattern, path));
|
|
1774
1821
|
}
|
|
1775
1822
|
} else if (pattern instanceof RegExp) {
|
|
1776
1823
|
if (Array.isArray(data)) {
|
|
1777
1824
|
let index = data.findIndex((element, index2) => fails2(element, pattern, root, path + "[" + index2 + "]"));
|
|
1778
1825
|
if (index > -1) {
|
|
1779
|
-
problems.push(
|
|
1826
|
+
problems.push(error4("data[" + index + "] does not match pattern", data[index], pattern, path + "[" + index + "]"));
|
|
1780
1827
|
}
|
|
1781
1828
|
} else if (typeof data == "undefined") {
|
|
1782
|
-
problems.push(
|
|
1829
|
+
problems.push(error4("data is undefined, should match pattern", data, pattern, path));
|
|
1783
1830
|
} else if (!pattern.test(data)) {
|
|
1784
|
-
problems.push(
|
|
1831
|
+
problems.push(error4("data does not match pattern", data, pattern, path));
|
|
1785
1832
|
}
|
|
1786
1833
|
} else if (pattern instanceof Function) {
|
|
1787
1834
|
let problem = pattern(data, root, path);
|
|
@@ -1794,7 +1841,7 @@
|
|
|
1794
1841
|
}
|
|
1795
1842
|
} else if (Array.isArray(pattern)) {
|
|
1796
1843
|
if (!Array.isArray(data)) {
|
|
1797
|
-
problems.push(
|
|
1844
|
+
problems.push(error4("data is not an array", data, [], path));
|
|
1798
1845
|
}
|
|
1799
1846
|
for (let p2 of pattern) {
|
|
1800
1847
|
for (let index of data.keys()) {
|
|
@@ -1810,10 +1857,10 @@
|
|
|
1810
1857
|
if (Array.isArray(data)) {
|
|
1811
1858
|
let index = data.findIndex((element, index2) => fails2(element, pattern, root, path + "[" + index2 + "]"));
|
|
1812
1859
|
if (index > -1) {
|
|
1813
|
-
problems.push(
|
|
1860
|
+
problems.push(error4("data[" + index + "] does not match pattern", data[index], pattern, path + "[" + index + "]"));
|
|
1814
1861
|
}
|
|
1815
1862
|
} else if (!data || typeof data != "object") {
|
|
1816
|
-
problems.push(
|
|
1863
|
+
problems.push(error4("data is not an object, pattern is", data, pattern, path));
|
|
1817
1864
|
} else {
|
|
1818
1865
|
if (data instanceof URLSearchParams) {
|
|
1819
1866
|
data = Object.fromEntries(data);
|
|
@@ -1834,7 +1881,7 @@
|
|
|
1834
1881
|
}
|
|
1835
1882
|
} else {
|
|
1836
1883
|
if (pattern != data) {
|
|
1837
|
-
problems.push(
|
|
1884
|
+
problems.push(error4("data and pattern are not equal", data, pattern, path));
|
|
1838
1885
|
}
|
|
1839
1886
|
}
|
|
1840
1887
|
if (problems.length) {
|
|
@@ -1842,7 +1889,7 @@
|
|
|
1842
1889
|
}
|
|
1843
1890
|
return false;
|
|
1844
1891
|
}
|
|
1845
|
-
function
|
|
1892
|
+
function error4(message, found, expected, path, problems) {
|
|
1846
1893
|
let result = {
|
|
1847
1894
|
message,
|
|
1848
1895
|
found,
|
|
@@ -1860,13 +1907,13 @@
|
|
|
1860
1907
|
if (options.filter((o) => root.hasOwnKey(o)).length > 0) {
|
|
1861
1908
|
return false;
|
|
1862
1909
|
}
|
|
1863
|
-
return
|
|
1910
|
+
return error4("root data must have all of", root, options);
|
|
1864
1911
|
};
|
|
1865
1912
|
var MustInclude = (...options) => (value) => {
|
|
1866
1913
|
if (Array.isArray(value) && options.filter((o) => !value.includes(o)).length == 0) {
|
|
1867
1914
|
return false;
|
|
1868
1915
|
} else {
|
|
1869
|
-
return
|
|
1916
|
+
return error4("data must be an array which includes", value, options);
|
|
1870
1917
|
}
|
|
1871
1918
|
};
|
|
1872
1919
|
var validJWA = [
|