@breakside/jskit 2024.40.0 → 2024.41.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.
Files changed (45) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  3. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  4. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  5. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  6. package/Frameworks/Foundation.jsframework/JS/Foundation+HTML.js +3 -1
  7. package/Frameworks/Foundation.jsframework/JS/JSHTMLUserAgent.js +130 -0
  8. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  9. package/Frameworks/Foundation.jsframework/sources.json +3 -1
  10. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  11. package/Frameworks/SecurityKit.jsframework/JS/SECCBOR.js +4 -0
  12. package/Frameworks/SecurityKit.jsframework/JS/SECHTMLDeviceAuthentication.js +69 -16
  13. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  14. package/Info.json +2 -2
  15. package/Node/io.breakside.jskit-bundle.js +2 -2
  16. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  17. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  18. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  19. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  20. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  21. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  22. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  23. package/Root/Frameworks/DOM/Info.yaml +1 -1
  24. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  25. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  26. package/Root/Frameworks/Foundation/Foundation+HTML.js +3 -1
  27. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  28. package/Root/Frameworks/Foundation/JSHTMLUserAgent.js +130 -0
  29. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  30. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  31. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  32. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  33. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  34. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  35. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  36. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  37. package/Root/Frameworks/SecurityKit/SECCBOR.js +4 -0
  38. package/Root/Frameworks/SecurityKit/SECHTMLDeviceAuthentication.js +69 -16
  39. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  40. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  41. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  42. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  43. package/Root/Frameworks/UIKit/UIPlatform+HTML.js +2 -1
  44. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  45. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.DOM",
4
- "JSBundleVersion": "2024.40.0",
4
+ "JSBundleVersion": "2024.41.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "DOM+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
12
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
13
13
  }
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.DOM'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.DOM",
6
- "JSBundleVersion": "2024.40.0",
6
+ "JSBundleVersion": "2024.41.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.DOM'] = {
11
11
  "node": "DOM+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
14
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
15
15
  },
16
16
  "Resources": [],
17
17
  "ResourceLookup": {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.FontKit",
4
- "JSBundleVersion": "2024.40.0",
4
+ "JSBundleVersion": "2024.41.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
8
8
  "html": "FontKit+HTML.js"
9
9
  },
10
10
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
11
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
11
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
12
12
  }
@@ -3,14 +3,14 @@ JSBundle.bundles['io.breakside.JSKit.FontKit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.FontKit",
6
- "JSBundleVersion": "2024.40.0",
6
+ "JSBundleVersion": "2024.41.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
10
10
  "html": "FontKit+HTML.js"
11
11
  },
12
12
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
13
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
13
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
14
14
  },
15
15
  "Resources": [],
16
16
  "ResourceLookup": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.Foundation",
4
- "JSBundleVersion": "2024.40.0",
4
+ "JSBundleVersion": "2024.41.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "Foundation+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
12
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
13
13
  }
@@ -34,4 +34,6 @@
34
34
 
35
35
  // Subclasses
36
36
  // #import "JSHTMLFile.js"
37
- // #import "JSHTMLFileEnumerator.js"
37
+ // #import "JSHTMLFileEnumerator.js"
38
+
39
+ // #import "JSHTMLUserAgent.js"
@@ -0,0 +1,130 @@
1
+ // Copyright 2021 Breakside Inc.
2
+ //
3
+ // Licensed under the Breakside Public License, Version 1.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // If a copy of the License was not distributed with this file, you may
6
+ // obtain a copy at
7
+ //
8
+ // http://breakside.io/licenses/LICENSE-1.0.txt
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+
16
+ // #import "JSObject.js"
17
+ "use strict";
18
+
19
+ JSClass("JSHTMLUserAgent", JSObject, {
20
+
21
+ string: null,
22
+ products: null,
23
+ productsByIdentifier: null,
24
+ comments: null,
25
+ commentSet: null,
26
+
27
+ initWithString: function(string){
28
+ if (string === null || string === undefined){
29
+ return null;
30
+ }
31
+ this.string = string;
32
+ this.products = [];
33
+ this.productsByIdentifier = new Set();
34
+ this.comments = [];
35
+ this.commentSet = new Set();
36
+ this.parse();
37
+ },
38
+
39
+ parse: function(){
40
+ var str = this.string;
41
+ var i = 0;
42
+ var l = this.string.length;
43
+ var product = "";
44
+ var version = "";
45
+ var comment = "";
46
+ while (i < l){
47
+ while (str[i] == " " || str[i] == "\t"){
48
+ ++i;
49
+ }
50
+ if (i < l){
51
+ if (str[i] == "("){
52
+ comment = "";
53
+ ++i;
54
+ while (i < l && str[i] !== ")"){
55
+ if (str[i] == ";"){
56
+ comment = comment.trim();
57
+ if (comment !== ""){
58
+ this.addComment(comment);
59
+ }
60
+ comment = "";
61
+ }else{
62
+ comment += str[i];
63
+ }
64
+ ++i;
65
+ }
66
+ comment = comment.trim();
67
+ if (comment !== ""){
68
+ this.addComment(comment);
69
+ }
70
+ if (i < l){
71
+ ++i;
72
+ }
73
+ }else{
74
+ product = "";
75
+ version = "";
76
+ while (i < l && str[i] !== "/" && str[i] !== " " && str[i] !== "\t"){
77
+ product += str[i];
78
+ ++i;
79
+ }
80
+ if (i < l && str[i] == "/"){
81
+ ++i;
82
+ while (i < l && str[i] !== "(" && str[i] !== " " && str[i] !== "\t"){
83
+ version += str[i];
84
+ ++i;
85
+ }
86
+ }
87
+ this.addProduct(product, version);
88
+ }
89
+ }
90
+ }
91
+ },
92
+
93
+ addProduct: function(name, version){
94
+ if (version === "" || version === undefined){
95
+ version = null;
96
+ }
97
+ var product = {name: name, version: version};
98
+ this.products.push(product);
99
+ this.productsByIdentifier[name] = product;
100
+ if (version !== null){
101
+ this.productsByIdentifier["%s/%s".sprintf(name, version)] = product;
102
+ }
103
+ },
104
+
105
+ addComment: function(comment){
106
+ this.comments.push(comment);
107
+ this.commentSet.add(comment);
108
+ },
109
+
110
+ containsProduct: function(name, version){
111
+ if (version === undefined || version === null || version === ""){
112
+ return name in this.productsByIdentifier;
113
+ }
114
+ return "%s/%s".sprintf(name, version) in this.productsByIdentifier;
115
+ },
116
+
117
+ versionOfProduct: function(name){
118
+ var product = this.productsByIdentifier[name];
119
+ if (product){
120
+ return product.version;
121
+ }
122
+ return null;
123
+ },
124
+
125
+ containsComment: function(comment){
126
+ return this.commentSet.has(comment);
127
+ }
128
+
129
+ });
130
+
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.Foundation'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.Foundation",
6
- "JSBundleVersion": "2024.40.0",
6
+ "JSBundleVersion": "2024.41.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.Foundation'] = {
11
11
  "node": "Foundation+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
14
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
15
15
  },
16
16
  "Resources": [
17
17
  {
@@ -295,6 +295,7 @@
295
295
  "JSFile+HTML.js",
296
296
  "JSHTMLFile.js",
297
297
  "JSHTMLFileEnumerator.js",
298
+ "JSHTMLUserAgent.js",
298
299
  "Foundation+HTML.js"
299
300
  ],
300
301
  "features": [
@@ -316,7 +317,8 @@
316
317
  "JSHTMLFileManager",
317
318
  "JSHTMLFile",
318
319
  "JSHTMLFileListFileEnumerator",
319
- "JSHTMLFileSystemEntryFileEnumerator"
320
+ "JSHTMLFileSystemEntryFileEnumerator",
321
+ "JSHTMLUserAgent"
320
322
  ]
321
323
  },
322
324
  "node": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.SecurityKit",
4
- "JSBundleVersion": "2024.40.0",
4
+ "JSBundleVersion": "2024.41.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "SecurityKit+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
12
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
13
13
  }
@@ -44,6 +44,9 @@ JSClass("SECCBORParser", JSObject, {
44
44
  },
45
45
 
46
46
  readNext: function(){
47
+ if (this.offset >= this.data.length){
48
+ return null;
49
+ }
47
50
  var b = this.data[this.offset++];
48
51
  var info = b & 0x1F;
49
52
  var majorType = (b & 0xE0) >> 5;
@@ -65,6 +68,7 @@ JSClass("SECCBORParser", JSObject, {
65
68
  case SECCBOR.MajorType.other:
66
69
  return this.readOther(info);
67
70
  }
71
+ return null;
68
72
  },
69
73
 
70
74
  readUnsignedInteger: function(info){
@@ -13,6 +13,7 @@
13
13
  // See the License for the specific language governing permissions and
14
14
  // limitations under the License.
15
15
 
16
+ // #import Foundation
16
17
  // #import "SECDeviceAuthentication.js"
17
18
  // #import "SECCipher.js"
18
19
  // #import "SECSign.js"
@@ -99,21 +100,19 @@ JSClass("SECHTMLDeviceAuthentication", JSObject, {
99
100
  challenge: registration.challengeData,
100
101
  pubKeyCredParams: [],
101
102
  authenticatorSelection: {
102
- // Browser support for resident keys is spotty:
103
- // - Chrome works, requiring the user to set a PIN, but can't share with others
104
- // - Safari works, no PIN required, but can't share with others
105
- // - Safari will get PIN support in Safari 14
106
- // - Firefox doesn't work on macOS, but supposedly works on windows with a PIN
107
- // Without a resident key, we need something like a username that the user can
108
- // provide so we can lookup allowed key IDs on a server and send them back to
109
- // the client before authenticating
110
- requireResidentKey: false,
111
- residentKey: "discouraged",
103
+ // requiring a resident key is necessary for Android to
104
+ // actually save a passkey. Apple OSes will save a passkey
105
+ // regardless of resident key settings
106
+ requireResidentKey: registration.platform ? true : false,
107
+ residentKey: registration.platform ? "required" : "discouraged",
112
108
  userVerification: registration.platform && !registration.conditional ? "required" : "discouraged",
113
109
  authenticatorAttachment: registration.platform ? "platform" : "cross-platform"
114
110
  },
115
111
  attestation: "none",
116
- timeout: 10000
112
+ timeout: 60000,
113
+ extensions: {
114
+ credProps: true
115
+ }
117
116
  };
118
117
  if (registration.domain !== undefined){
119
118
  info.rp.id = registration.domain;
@@ -141,6 +140,7 @@ JSClass("SECHTMLDeviceAuthentication", JSObject, {
141
140
  var jwk = credential.response.getPublicJWK();
142
141
  var webauthn = credential.toJSON();
143
142
  var result = {
143
+ name: credential.jskitName,
144
144
  jwk: jwk,
145
145
  challenge: registration.challengeData,
146
146
  webauthn: webauthn
@@ -187,9 +187,9 @@ JSClass("SECHTMLDeviceAuthentication", JSObject, {
187
187
  }
188
188
  var info = {
189
189
  challenge: request.challengeData,
190
- userVerification: "discouraged",
191
- timeout: 10000,
192
- allowCredentials: []
190
+ userVerification: request.platform ? "required" : "discouraged",
191
+ timeout: 60000,
192
+ allowCredentials: [],
193
193
  };
194
194
  if (request.domain){
195
195
  info.rpId = request.domain;
@@ -207,7 +207,7 @@ JSClass("SECHTMLDeviceAuthentication", JSObject, {
207
207
  id: request.allowedKeyIDs[i].dataByDecodingBase64URL()
208
208
  };
209
209
  if (request.platform){
210
- allowedCredential.transports = ["internal"];
210
+ allowedCredential.transports = ["internal", "hybrid"];
211
211
  }
212
212
  info.allowCredentials.push(allowedCredential);
213
213
  }catch (e){
@@ -265,12 +265,65 @@ if (window.PublicKeyCredential){
265
265
  dictionary.rawId = JSData.initWithBuffer(this.rawId).base64URLStringRepresentation();
266
266
  dictionary.authenticatorAttachment = this.authenticatorAttachment || "";
267
267
  dictionary.type = this.type || "public-key";
268
- dictionary.clientExtensionResults = this.getClientExtensionResults ? this.getClientExtensionResults() : {};
268
+ dictionary.clientExtensionResults = this.getClientExtensionResults();
269
269
  dictionary.response = this.response.toJSON ? this.response.toJSON() : {};
270
270
  return dictionary;
271
271
  };
272
272
  }
273
273
 
274
+ if (!window.PublicKeyCredential.prototype.getClientExtensionResults){
275
+ window.PublicKeyCredential.prototype.getClientExtensionResults = function PublicKeyCredential_getClientExtensionResults(){
276
+ if (this._clientExtensionResults === undefined){
277
+ var authData = JSData.initWithBuffer(this.getAuthenticatorData());
278
+ var idLength = (authData[53] << 8) | authData[54];
279
+ var offset = 55 + idLength;
280
+ var cbor = SECCBORParser.initWithData(authData.subdataInRange(JSRange(offset, authData.length - offset)));
281
+ var coseKey = cbor.readNext(); // ingored, only reading to arrive at the extensions start location
282
+ this._clientExtensionResults = cbor.readNext() || {};
283
+ }
284
+ return this._clientExtensionResults;
285
+ };
286
+ }
287
+
288
+ Object.defineProperties(window.PublicKeyCredential.prototype, {
289
+ jskitName: {
290
+ enumerable: false,
291
+ configurable: false,
292
+ get: function PublicKeyCredential_getJSKitName(){
293
+ var extensions = this.getClientExtensionResults();
294
+ if (extensions.credProps){
295
+ if (extensions.credProps.authenticatorDisplayName){
296
+ return extensions.credProps.authenticatorDisplayName;
297
+ }
298
+ }
299
+ var ua = JSHTMLUserAgent.initWithString(navigator.userAgent);
300
+ var apple = ua.containsComment("Macintosh") || ua.containsComment("iPhone") || ua.containsComment("iPad");
301
+ var windows = ua.containsComment("Win64") || ua.containsComment("Windows NT 10.0");
302
+ var linux = ua.containsComment("Linux");
303
+ var android = false;
304
+ if (linux){
305
+ var i, l;
306
+ for (i = 0, l = ua.comments.length; i < l; ++i){
307
+ if (ua.comments[i].startsWith("Android")){
308
+ android = true;
309
+ break;
310
+ }
311
+ }
312
+ }
313
+ if (apple){
314
+ return "iCloud Keychain";
315
+ }
316
+ if (android){
317
+ return "Google Password Manager";
318
+ }
319
+ if (windows){
320
+ return "Windows Hello";
321
+ }
322
+ return "";
323
+ }
324
+ }
325
+ });
326
+
274
327
  }
275
328
 
276
329
  if (window.AuthenticatorAttestationResponse){
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.SecurityKit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.SecurityKit",
6
- "JSBundleVersion": "2024.40.0",
6
+ "JSBundleVersion": "2024.41.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.SecurityKit'] = {
11
11
  "node": "SecurityKit+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
14
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
15
15
  },
16
16
  "Resources": [],
17
17
  "ResourceLookup": {
package/Info.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "JSBundleType": "node",
3
3
  "JSBundleIdentifier": "io.breakside.jskit",
4
- "JSBundleVersion": "2024.40.0",
4
+ "JSBundleVersion": "2024.41.0",
5
5
  "JSExecutableName": "jskit",
6
6
  "NPMOrganization": "breakside",
7
7
  "JSResources": [
8
8
  "Tests/HTMLTestRunner.js",
9
9
  "Tests/NodeTestRunner.js"
10
10
  ],
11
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
11
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
12
12
  }
@@ -3,14 +3,14 @@ JSBundle.bundles['io.breakside.jskit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "node",
5
5
  "JSBundleIdentifier": "io.breakside.jskit",
6
- "JSBundleVersion": "2024.40.0",
6
+ "JSBundleVersion": "2024.41.0",
7
7
  "JSExecutableName": "jskit",
8
8
  "NPMOrganization": "breakside",
9
9
  "JSResources": [
10
10
  "Tests/HTMLTestRunner.js",
11
11
  "Tests/NodeTestRunner.js"
12
12
  ],
13
- "GitRevision": "f17cafe60642c3c9674095789dec1d1803b72586"
13
+ "GitRevision": "bf643e90b060044223efa5a59f0d727332802563"
14
14
  },
15
15
  "Resources": [
16
16
  {
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.APIKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSIncludeDirectories:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.APIKitTesting
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.AuthKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.CSSOM
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ChartKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2021 Breakside Inc.
6
6
  # JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ConferenceKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.DBKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.DOM
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.Dispatch
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSCopyright: Copyright © 2020 Breakside Inc.
5
5
  JSBundleEnvironments:
6
6
  html: Dispatch+HTML.js
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.FontKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -34,4 +34,6 @@
34
34
 
35
35
  // Subclasses
36
36
  // #import "JSHTMLFile.js"
37
- // #import "JSHTMLFileEnumerator.js"
37
+ // #import "JSHTMLFileEnumerator.js"
38
+
39
+ // #import "JSHTMLUserAgent.js"
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.Foundation
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -0,0 +1,130 @@
1
+ // Copyright 2021 Breakside Inc.
2
+ //
3
+ // Licensed under the Breakside Public License, Version 1.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // If a copy of the License was not distributed with this file, you may
6
+ // obtain a copy at
7
+ //
8
+ // http://breakside.io/licenses/LICENSE-1.0.txt
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+
16
+ // #import "JSObject.js"
17
+ "use strict";
18
+
19
+ JSClass("JSHTMLUserAgent", JSObject, {
20
+
21
+ string: null,
22
+ products: null,
23
+ productsByIdentifier: null,
24
+ comments: null,
25
+ commentSet: null,
26
+
27
+ initWithString: function(string){
28
+ if (string === null || string === undefined){
29
+ return null;
30
+ }
31
+ this.string = string;
32
+ this.products = [];
33
+ this.productsByIdentifier = new Set();
34
+ this.comments = [];
35
+ this.commentSet = new Set();
36
+ this.parse();
37
+ },
38
+
39
+ parse: function(){
40
+ var str = this.string;
41
+ var i = 0;
42
+ var l = this.string.length;
43
+ var product = "";
44
+ var version = "";
45
+ var comment = "";
46
+ while (i < l){
47
+ while (str[i] == " " || str[i] == "\t"){
48
+ ++i;
49
+ }
50
+ if (i < l){
51
+ if (str[i] == "("){
52
+ comment = "";
53
+ ++i;
54
+ while (i < l && str[i] !== ")"){
55
+ if (str[i] == ";"){
56
+ comment = comment.trim();
57
+ if (comment !== ""){
58
+ this.addComment(comment);
59
+ }
60
+ comment = "";
61
+ }else{
62
+ comment += str[i];
63
+ }
64
+ ++i;
65
+ }
66
+ comment = comment.trim();
67
+ if (comment !== ""){
68
+ this.addComment(comment);
69
+ }
70
+ if (i < l){
71
+ ++i;
72
+ }
73
+ }else{
74
+ product = "";
75
+ version = "";
76
+ while (i < l && str[i] !== "/" && str[i] !== " " && str[i] !== "\t"){
77
+ product += str[i];
78
+ ++i;
79
+ }
80
+ if (i < l && str[i] == "/"){
81
+ ++i;
82
+ while (i < l && str[i] !== "(" && str[i] !== " " && str[i] !== "\t"){
83
+ version += str[i];
84
+ ++i;
85
+ }
86
+ }
87
+ this.addProduct(product, version);
88
+ }
89
+ }
90
+ }
91
+ },
92
+
93
+ addProduct: function(name, version){
94
+ if (version === "" || version === undefined){
95
+ version = null;
96
+ }
97
+ var product = {name: name, version: version};
98
+ this.products.push(product);
99
+ this.productsByIdentifier[name] = product;
100
+ if (version !== null){
101
+ this.productsByIdentifier["%s/%s".sprintf(name, version)] = product;
102
+ }
103
+ },
104
+
105
+ addComment: function(comment){
106
+ this.comments.push(comment);
107
+ this.commentSet.add(comment);
108
+ },
109
+
110
+ containsProduct: function(name, version){
111
+ if (version === undefined || version === null || version === ""){
112
+ return name in this.productsByIdentifier;
113
+ }
114
+ return "%s/%s".sprintf(name, version) in this.productsByIdentifier;
115
+ },
116
+
117
+ versionOfProduct: function(name){
118
+ var product = this.productsByIdentifier[name];
119
+ if (product){
120
+ return product.version;
121
+ }
122
+ return null;
123
+ },
124
+
125
+ containsComment: function(comment){
126
+ return this.commentSet.has(comment);
127
+ }
128
+
129
+ });
130
+
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ImageKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.MediaKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.MediaKitUI
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.NotificationKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2021 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.PDFKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.QRKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.SearchKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.SecurityKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -44,6 +44,9 @@ JSClass("SECCBORParser", JSObject, {
44
44
  },
45
45
 
46
46
  readNext: function(){
47
+ if (this.offset >= this.data.length){
48
+ return null;
49
+ }
47
50
  var b = this.data[this.offset++];
48
51
  var info = b & 0x1F;
49
52
  var majorType = (b & 0xE0) >> 5;
@@ -65,6 +68,7 @@ JSClass("SECCBORParser", JSObject, {
65
68
  case SECCBOR.MajorType.other:
66
69
  return this.readOther(info);
67
70
  }
71
+ return null;
68
72
  },
69
73
 
70
74
  readUnsignedInteger: function(info){
@@ -13,6 +13,7 @@
13
13
  // See the License for the specific language governing permissions and
14
14
  // limitations under the License.
15
15
 
16
+ // #import Foundation
16
17
  // #import "SECDeviceAuthentication.js"
17
18
  // #import "SECCipher.js"
18
19
  // #import "SECSign.js"
@@ -99,21 +100,19 @@ JSClass("SECHTMLDeviceAuthentication", JSObject, {
99
100
  challenge: registration.challengeData,
100
101
  pubKeyCredParams: [],
101
102
  authenticatorSelection: {
102
- // Browser support for resident keys is spotty:
103
- // - Chrome works, requiring the user to set a PIN, but can't share with others
104
- // - Safari works, no PIN required, but can't share with others
105
- // - Safari will get PIN support in Safari 14
106
- // - Firefox doesn't work on macOS, but supposedly works on windows with a PIN
107
- // Without a resident key, we need something like a username that the user can
108
- // provide so we can lookup allowed key IDs on a server and send them back to
109
- // the client before authenticating
110
- requireResidentKey: false,
111
- residentKey: "discouraged",
103
+ // requiring a resident key is necessary for Android to
104
+ // actually save a passkey. Apple OSes will save a passkey
105
+ // regardless of resident key settings
106
+ requireResidentKey: registration.platform ? true : false,
107
+ residentKey: registration.platform ? "required" : "discouraged",
112
108
  userVerification: registration.platform && !registration.conditional ? "required" : "discouraged",
113
109
  authenticatorAttachment: registration.platform ? "platform" : "cross-platform"
114
110
  },
115
111
  attestation: "none",
116
- timeout: 10000
112
+ timeout: 60000,
113
+ extensions: {
114
+ credProps: true
115
+ }
117
116
  };
118
117
  if (registration.domain !== undefined){
119
118
  info.rp.id = registration.domain;
@@ -141,6 +140,7 @@ JSClass("SECHTMLDeviceAuthentication", JSObject, {
141
140
  var jwk = credential.response.getPublicJWK();
142
141
  var webauthn = credential.toJSON();
143
142
  var result = {
143
+ name: credential.jskitName,
144
144
  jwk: jwk,
145
145
  challenge: registration.challengeData,
146
146
  webauthn: webauthn
@@ -187,9 +187,9 @@ JSClass("SECHTMLDeviceAuthentication", JSObject, {
187
187
  }
188
188
  var info = {
189
189
  challenge: request.challengeData,
190
- userVerification: "discouraged",
191
- timeout: 10000,
192
- allowCredentials: []
190
+ userVerification: request.platform ? "required" : "discouraged",
191
+ timeout: 60000,
192
+ allowCredentials: [],
193
193
  };
194
194
  if (request.domain){
195
195
  info.rpId = request.domain;
@@ -207,7 +207,7 @@ JSClass("SECHTMLDeviceAuthentication", JSObject, {
207
207
  id: request.allowedKeyIDs[i].dataByDecodingBase64URL()
208
208
  };
209
209
  if (request.platform){
210
- allowedCredential.transports = ["internal"];
210
+ allowedCredential.transports = ["internal", "hybrid"];
211
211
  }
212
212
  info.allowCredentials.push(allowedCredential);
213
213
  }catch (e){
@@ -265,12 +265,65 @@ if (window.PublicKeyCredential){
265
265
  dictionary.rawId = JSData.initWithBuffer(this.rawId).base64URLStringRepresentation();
266
266
  dictionary.authenticatorAttachment = this.authenticatorAttachment || "";
267
267
  dictionary.type = this.type || "public-key";
268
- dictionary.clientExtensionResults = this.getClientExtensionResults ? this.getClientExtensionResults() : {};
268
+ dictionary.clientExtensionResults = this.getClientExtensionResults();
269
269
  dictionary.response = this.response.toJSON ? this.response.toJSON() : {};
270
270
  return dictionary;
271
271
  };
272
272
  }
273
273
 
274
+ if (!window.PublicKeyCredential.prototype.getClientExtensionResults){
275
+ window.PublicKeyCredential.prototype.getClientExtensionResults = function PublicKeyCredential_getClientExtensionResults(){
276
+ if (this._clientExtensionResults === undefined){
277
+ var authData = JSData.initWithBuffer(this.getAuthenticatorData());
278
+ var idLength = (authData[53] << 8) | authData[54];
279
+ var offset = 55 + idLength;
280
+ var cbor = SECCBORParser.initWithData(authData.subdataInRange(JSRange(offset, authData.length - offset)));
281
+ var coseKey = cbor.readNext(); // ingored, only reading to arrive at the extensions start location
282
+ this._clientExtensionResults = cbor.readNext() || {};
283
+ }
284
+ return this._clientExtensionResults;
285
+ };
286
+ }
287
+
288
+ Object.defineProperties(window.PublicKeyCredential.prototype, {
289
+ jskitName: {
290
+ enumerable: false,
291
+ configurable: false,
292
+ get: function PublicKeyCredential_getJSKitName(){
293
+ var extensions = this.getClientExtensionResults();
294
+ if (extensions.credProps){
295
+ if (extensions.credProps.authenticatorDisplayName){
296
+ return extensions.credProps.authenticatorDisplayName;
297
+ }
298
+ }
299
+ var ua = JSHTMLUserAgent.initWithString(navigator.userAgent);
300
+ var apple = ua.containsComment("Macintosh") || ua.containsComment("iPhone") || ua.containsComment("iPad");
301
+ var windows = ua.containsComment("Win64") || ua.containsComment("Windows NT 10.0");
302
+ var linux = ua.containsComment("Linux");
303
+ var android = false;
304
+ if (linux){
305
+ var i, l;
306
+ for (i = 0, l = ua.comments.length; i < l; ++i){
307
+ if (ua.comments[i].startsWith("Android")){
308
+ android = true;
309
+ break;
310
+ }
311
+ }
312
+ }
313
+ if (apple){
314
+ return "iCloud Keychain";
315
+ }
316
+ if (android){
317
+ return "Google Password Manager";
318
+ }
319
+ if (windows){
320
+ return "Windows Hello";
321
+ }
322
+ return "";
323
+ }
324
+ }
325
+ });
326
+
274
327
  }
275
328
 
276
329
  if (window.AuthenticatorAttestationResponse){
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ServerKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ServerKitTesting
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.TestKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.UIKit
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -21,8 +21,9 @@
21
21
 
22
22
  var mac = navigator.userAgent.indexOf("Macintosh") >= 0;
23
23
  var iphone = navigator.userAgent.indexOf("iPhone") >= 0;
24
+ var ipad = navigator.userAgent.indexOf("iPad") >= 0;
24
25
 
25
- if (mac || iphone){
26
+ if (mac || iphone || ipad){
26
27
  UIPlatform.shared = UIPlatform.initWithIdentifier(UIPlatform.Identifier.mac);
27
28
  }else{
28
29
  UIPlatform.shared = UIPlatform.initWithIdentifier(UIPlatform.Identifier.win);
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.UIKitTesting
3
- JSBundleVersion: 2024.40.0
3
+ JSBundleVersion: 2024.41.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "node": ">=10.10.0"
10
10
  },
11
11
  "name": "@breakside/jskit",
12
- "version": "2024.40.0",
12
+ "version": "2024.41.0",
13
13
  "license": "SEE LICENSE IN LICENSE.txt",
14
14
  "files": [
15
15
  "*"