@cap-kit/ssl-pinning 8.0.1 → 8.0.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/android/build.gradle +1 -4
- package/android/src/main/java/io/capkit/sslpinning/SSLPinningConfig.kt +5 -2
- package/android/src/main/java/io/capkit/sslpinning/SSLPinningError.kt +12 -8
- package/android/src/main/java/io/capkit/sslpinning/utils/SSLPinningUtils.kt +6 -7
- package/dist/{plugin.cjs.js → plugin.cjs} +1 -1
- package/dist/plugin.cjs.map +1 -0
- package/ios/Sources/SSLPinningPlugin/Version.swift +2 -2
- package/package.json +13 -11
- package/scripts/{sync-version.js → sync-version.mjs} +6 -0
- package/dist/plugin.cjs.js.map +0 -1
- /package/scripts/{chmod.js → chmod.mjs} +0 -0
package/android/build.gradle
CHANGED
|
@@ -5,7 +5,7 @@ buildscript {
|
|
|
5
5
|
mavenCentral()
|
|
6
6
|
}
|
|
7
7
|
dependencies {
|
|
8
|
-
classpath 'com.android.tools.build:gradle:8.13.
|
|
8
|
+
classpath 'com.android.tools.build:gradle:8.13.2'
|
|
9
9
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -81,9 +81,6 @@ android {
|
|
|
81
81
|
sourceCompatibility = JavaVersion.VERSION_21
|
|
82
82
|
targetCompatibility = JavaVersion.VERSION_21
|
|
83
83
|
}
|
|
84
|
-
kotlinOptions {
|
|
85
|
-
jvmTarget = JavaVersion.VERSION_21
|
|
86
|
-
}
|
|
87
84
|
}
|
|
88
85
|
|
|
89
86
|
repositories {
|
|
@@ -15,7 +15,9 @@ import com.getcapacitor.Plugin
|
|
|
15
15
|
* - Treated as immutable runtime input
|
|
16
16
|
* - Accessible only from native code
|
|
17
17
|
*/
|
|
18
|
-
class SSLPinningConfig(
|
|
18
|
+
class SSLPinningConfig(
|
|
19
|
+
plugin: Plugin,
|
|
20
|
+
) {
|
|
19
21
|
/**
|
|
20
22
|
* Android application context.
|
|
21
23
|
* Exposed for native components that may require it.
|
|
@@ -58,7 +60,8 @@ class SSLPinningConfig(plugin: Plugin) {
|
|
|
58
60
|
|
|
59
61
|
// Multiple fingerprints (optional)
|
|
60
62
|
fingerprints =
|
|
61
|
-
config
|
|
63
|
+
config
|
|
64
|
+
.getArray("fingerprints")
|
|
62
65
|
?.toList()
|
|
63
66
|
?.mapNotNull { it as? String }
|
|
64
67
|
?.filter { it.isNotBlank() }
|
|
@@ -16,25 +16,29 @@ sealed class SSLPinningError(
|
|
|
16
16
|
* Feature or capability is not available
|
|
17
17
|
* due to device or configuration limitations.
|
|
18
18
|
*/
|
|
19
|
-
class Unavailable(
|
|
20
|
-
|
|
19
|
+
class Unavailable(
|
|
20
|
+
message: String,
|
|
21
|
+
) : SSLPinningError(message)
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Required permission was denied or not granted.
|
|
24
25
|
*/
|
|
25
|
-
class PermissionDenied(
|
|
26
|
-
|
|
26
|
+
class PermissionDenied(
|
|
27
|
+
message: String,
|
|
28
|
+
) : SSLPinningError(message)
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Plugin failed to initialize or perform
|
|
30
32
|
* a required operation.
|
|
31
33
|
*/
|
|
32
|
-
class InitFailed(
|
|
33
|
-
|
|
34
|
+
class InitFailed(
|
|
35
|
+
message: String,
|
|
36
|
+
) : SSLPinningError(message)
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
39
|
* Invalid or unsupported input was provided.
|
|
37
40
|
*/
|
|
38
|
-
class UnknownType(
|
|
39
|
-
|
|
41
|
+
class UnknownType(
|
|
42
|
+
message: String,
|
|
43
|
+
) : SSLPinningError(message)
|
|
40
44
|
}
|
|
@@ -19,14 +19,13 @@ object SSLPinningUtils {
|
|
|
19
19
|
* Non-HTTPS URLs are explicitly rejected
|
|
20
20
|
* to prevent insecure usage.
|
|
21
21
|
*/
|
|
22
|
-
fun httpsUrl(value: String): URL?
|
|
23
|
-
|
|
22
|
+
fun httpsUrl(value: String): URL? =
|
|
23
|
+
try {
|
|
24
24
|
val url = URL(value)
|
|
25
25
|
if (url.protocol == "https") url else null
|
|
26
26
|
} catch (_: Exception) {
|
|
27
27
|
null
|
|
28
28
|
}
|
|
29
|
-
}
|
|
30
29
|
|
|
31
30
|
/**
|
|
32
31
|
* Normalizes a fingerprint string by:
|
|
@@ -36,11 +35,10 @@ object SSLPinningUtils {
|
|
|
36
35
|
* Example:
|
|
37
36
|
* "AA:BB:CC" → "aabbcc"
|
|
38
37
|
*/
|
|
39
|
-
fun normalizeFingerprint(value: String): String
|
|
40
|
-
|
|
38
|
+
fun normalizeFingerprint(value: String): String =
|
|
39
|
+
value
|
|
41
40
|
.replace(":", "")
|
|
42
41
|
.lowercase()
|
|
43
|
-
}
|
|
44
42
|
|
|
45
43
|
/**
|
|
46
44
|
* Computes the SHA-256 fingerprint of an X.509 certificate.
|
|
@@ -50,7 +48,8 @@ object SSLPinningUtils {
|
|
|
50
48
|
*/
|
|
51
49
|
fun sha256Fingerprint(cert: Certificate): String {
|
|
52
50
|
val digest =
|
|
53
|
-
MessageDigest
|
|
51
|
+
MessageDigest
|
|
52
|
+
.getInstance("SHA-256")
|
|
54
53
|
.digest(cert.encoded)
|
|
55
54
|
|
|
56
55
|
return digest.joinToString(separator = ":") {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.cjs","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n// -----------------------------------------------------------------------------\n// Enums\n// -----------------------------------------------------------------------------\n/**\n * Standardized error codes for programmatic handling of SSL pinning failures.\n *\n * Errors are delivered via Promise rejection as `CapacitorException`\n * with one of the following codes.\n *\n * @since 0.0.15\n */\nexport var SSLPinningErrorCode;\n(function (SSLPinningErrorCode) {\n /** Required data is missing or the feature is not available. */\n SSLPinningErrorCode[\"UNAVAILABLE\"] = \"UNAVAILABLE\";\n /** The user denied a required permission or the feature is disabled. */\n SSLPinningErrorCode[\"PERMISSION_DENIED\"] = \"PERMISSION_DENIED\";\n /** The SSL pinning operation failed due to a runtime or initialization error. */\n SSLPinningErrorCode[\"INIT_FAILED\"] = \"INIT_FAILED\";\n /** Invalid or unsupported input was provided. */\n SSLPinningErrorCode[\"UNKNOWN_TYPE\"] = \"UNKNOWN_TYPE\";\n})(SSLPinningErrorCode || (SSLPinningErrorCode = {}));\n//# sourceMappingURL=definitions.js.map","/**\n * Import the `registerPlugin` method from the Capacitor core library.\n * This method is used to register a custom plugin.\n */\nimport { registerPlugin } from '@capacitor/core';\n/**\n * The SSLPinning plugin instance.\n * It automatically lazy-loads the web implementation if running in a browser environment.\n * Use this instance to access all ssl pinning functionality.\n */\nconst SSLPinning = registerPlugin('SSLPinning', {\n web: () => import('./web').then((m) => new m.SSLPinningWeb()),\n});\nexport * from './definitions';\nexport { SSLPinning };\n//# sourceMappingURL=index.js.map","/**\n * This module provides a web implementation of the SSLPinningPlugin.\n * The functionality is limited in a web context due to the lack of SSL certificate inspection capabilities in browsers.\n *\n * The implementation adheres to the SSLPinningPlugin interface but provides fallback behavior\n * because browsers do not allow direct inspection of SSL certificate details.\n */\nimport { CapacitorException, ExceptionCode, WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the SSLPinning plugin.\n *\n * SSL certificate inspection is not supported in browsers,\n * therefore all SSL pinning methods are unimplemented.\n */\nexport class SSLPinningWeb extends WebPlugin {\n /**\n * Checks a single SSL certificate against the expected fingerprint.\n * @return A promise that resolves to the result of the certificate check.\n * @throws CapacitorException indicating unimplemented functionality.\n */\n async checkCertificate() {\n throw this.createUnimplementedError();\n }\n /**\n * Checks multiple SSL certificates against their expected fingerprints.\n * @return A promise that resolves to an array of results for each certificate check.\n * @throws CapacitorException indicating unimplemented functionality.\n */\n async checkCertificates() {\n throw this.createUnimplementedError();\n }\n // --- Plugin Info ---\n /**\n * Returns the plugin version.\n *\n * On the Web, this value represents the JavaScript package version\n * rather than a native implementation.\n */\n async getPluginVersion() {\n return { version: 'web' };\n }\n /**\n * Creates a standardized exception for unimplemented methods.\n *\n * This utility method centralizes the creation of exceptions for functionality that is not supported\n * on the current platform, ensuring consistency in error reporting.\n *\n * @returns {CapacitorException} An exception with the code `Unimplemented` and a descriptive message.\n */\n createUnimplementedError() {\n return new CapacitorException('This plugin method is not implemented on this platform.', ExceptionCode.Unimplemented);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["SSLPinningErrorCode","registerPlugin","WebPlugin","CapacitorException","ExceptionCode"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACWA;AACX,CAAC,UAAU,mBAAmB,EAAE;AAChC;AACA,IAAI,mBAAmB,CAAC,aAAa,CAAC,GAAG,aAAa;AACtD;AACA,IAAI,mBAAmB,CAAC,mBAAmB,CAAC,GAAG,mBAAmB;AAClE;AACA,IAAI,mBAAmB,CAAC,aAAa,CAAC,GAAG,aAAa;AACtD;AACA,IAAI,mBAAmB,CAAC,cAAc,CAAC,GAAG,cAAc;AACxD,CAAC,EAAEA,2BAAmB,KAAKA,2BAAmB,GAAG,EAAE,CAAC,CAAC;;ACtBrD;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACK,MAAC,UAAU,GAAGC,mBAAc,CAAC,YAAY,EAAE;AAChD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;AACjE,CAAC;;ACZD;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,aAAa,SAASC,cAAS,CAAC;AAC7C;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,gBAAgB,GAAG;AAC7B,QAAQ,MAAM,IAAI,CAAC,wBAAwB,EAAE;AAC7C,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,iBAAiB,GAAG;AAC9B,QAAQ,MAAM,IAAI,CAAC,wBAAwB,EAAE;AAC7C,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,gBAAgB,GAAG;AAC7B,QAAQ,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AACjC,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,wBAAwB,GAAG;AAC/B,QAAQ,OAAO,IAAIC,uBAAkB,CAAC,yDAAyD,EAAEC,kBAAa,CAAC,aAAa,CAAC;AAC7H,IAAI;AACJ;;;;;;;;;"}
|
|
@@ -10,7 +10,7 @@ import Foundation
|
|
|
10
10
|
public enum PluginVersion {
|
|
11
11
|
/**
|
|
12
12
|
The semantic version string of the plugin.
|
|
13
|
-
Value synchronized from package.json: "8.0.
|
|
13
|
+
Value synchronized from package.json: "8.0.3"
|
|
14
14
|
*/
|
|
15
|
-
public static let number = "8.0.
|
|
15
|
+
public static let number = "8.0.3"
|
|
16
16
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-kit/ssl-pinning",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.3",
|
|
4
4
|
"description": "Capacitor plugin for runtime SSL certificate fingerprint pinning on iOS and Android",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -9,10 +9,18 @@
|
|
|
9
9
|
"node": ">=24.0.0",
|
|
10
10
|
"pnpm": ">=10.0.0"
|
|
11
11
|
},
|
|
12
|
-
"main": "dist/plugin.cjs
|
|
12
|
+
"main": "dist/plugin.cjs",
|
|
13
13
|
"module": "dist/esm/index.js",
|
|
14
14
|
"types": "dist/esm/index.d.ts",
|
|
15
15
|
"unpkg": "dist/plugin.js",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/esm/index.d.ts",
|
|
19
|
+
"import": "./dist/esm/index.js",
|
|
20
|
+
"require": "./dist/plugin.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
16
24
|
"files": [
|
|
17
25
|
"android/src/main/",
|
|
18
26
|
"android/build.gradle",
|
|
@@ -22,16 +30,10 @@
|
|
|
22
30
|
"Package.swift",
|
|
23
31
|
"CapKitSSLPinning.podspec",
|
|
24
32
|
"LICENSE",
|
|
33
|
+
"README.md",
|
|
25
34
|
"bin/",
|
|
26
35
|
"scripts/"
|
|
27
36
|
],
|
|
28
|
-
"exports": {
|
|
29
|
-
".": {
|
|
30
|
-
"types": "./dist/esm/index.d.ts",
|
|
31
|
-
"import": "./dist/esm/index.js",
|
|
32
|
-
"require": "./dist/plugin.cjs.js"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
37
|
"author": "CapKit Team",
|
|
36
38
|
"funding": {
|
|
37
39
|
"type": "github",
|
|
@@ -106,7 +108,7 @@
|
|
|
106
108
|
},
|
|
107
109
|
"scripts": {
|
|
108
110
|
"verify": "pnpm run verify:ios && pnpm run verify:android && pnpm run verify:web",
|
|
109
|
-
"verify:ios": "node ./scripts/sync-version.
|
|
111
|
+
"verify:ios": "node ./scripts/sync-version.mjs && xcodebuild -scheme CapKitSslPinning -destination generic/platform=iOS",
|
|
110
112
|
"verify:android": "cd android && ./gradlew clean build && cd ..",
|
|
111
113
|
"verify:web": "pnpm run build",
|
|
112
114
|
"lint:android": "cd android && ./gradlew ktlintCheck",
|
|
@@ -118,7 +120,7 @@
|
|
|
118
120
|
"prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
119
121
|
"swiftlint": "node-swiftlint lint ios/Sources",
|
|
120
122
|
"docgen": "docgen --api SSLPinningPlugin --output-readme README.md --output-json dist/docs.json",
|
|
121
|
-
"build": "node ./scripts/sync-version.
|
|
123
|
+
"build": "node ./scripts/sync-version.mjs && pnpm run clean && pnpm run docgen && tsc && rollup -c rollup.config.mjs && node scripts/chmod.mjs",
|
|
122
124
|
"clean": "rimraf ./dist",
|
|
123
125
|
"watch": "tsc --watch",
|
|
124
126
|
"test": "pnpm run verify",
|
|
@@ -13,6 +13,12 @@ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
|
13
13
|
// 3. Automatically find the iOS source directory
|
|
14
14
|
// Capacitor standard structure: ios/Sources/<PluginName>
|
|
15
15
|
const sourcesDir = path.join(__dirname, '../ios/Sources');
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(sourcesDir)) {
|
|
18
|
+
console.warn('⚠️ [CapKit] ios/Sources directory not found. Skipping Version.swift generation.');
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
const pluginFolderName = fs.readdirSync(sourcesDir).find((f) => fs.statSync(path.join(sourcesDir, f)).isDirectory());
|
|
17
23
|
|
|
18
24
|
if (!pluginFolderName) {
|
package/dist/plugin.cjs.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n// -----------------------------------------------------------------------------\n// Enums\n// -----------------------------------------------------------------------------\n/**\n * Standardized error codes for programmatic handling of SSL pinning failures.\n *\n * Errors are delivered via Promise rejection as `CapacitorException`\n * with one of the following codes.\n *\n * @since 0.0.15\n */\nexport var SSLPinningErrorCode;\n(function (SSLPinningErrorCode) {\n /** Required data is missing or the feature is not available. */\n SSLPinningErrorCode[\"UNAVAILABLE\"] = \"UNAVAILABLE\";\n /** The user denied a required permission or the feature is disabled. */\n SSLPinningErrorCode[\"PERMISSION_DENIED\"] = \"PERMISSION_DENIED\";\n /** The SSL pinning operation failed due to a runtime or initialization error. */\n SSLPinningErrorCode[\"INIT_FAILED\"] = \"INIT_FAILED\";\n /** Invalid or unsupported input was provided. */\n SSLPinningErrorCode[\"UNKNOWN_TYPE\"] = \"UNKNOWN_TYPE\";\n})(SSLPinningErrorCode || (SSLPinningErrorCode = {}));\n//# sourceMappingURL=definitions.js.map","/**\n * Import the `registerPlugin` method from the Capacitor core library.\n * This method is used to register a custom plugin.\n */\nimport { registerPlugin } from '@capacitor/core';\n/**\n * The SSLPinning plugin instance.\n * It automatically lazy-loads the web implementation if running in a browser environment.\n * Use this instance to access all ssl pinning functionality.\n */\nconst SSLPinning = registerPlugin('SSLPinning', {\n web: () => import('./web').then((m) => new m.SSLPinningWeb()),\n});\nexport * from './definitions';\nexport { SSLPinning };\n//# sourceMappingURL=index.js.map","/**\n * This module provides a web implementation of the SSLPinningPlugin.\n * The functionality is limited in a web context due to the lack of SSL certificate inspection capabilities in browsers.\n *\n * The implementation adheres to the SSLPinningPlugin interface but provides fallback behavior\n * because browsers do not allow direct inspection of SSL certificate details.\n */\nimport { CapacitorException, ExceptionCode, WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the SSLPinning plugin.\n *\n * SSL certificate inspection is not supported in browsers,\n * therefore all SSL pinning methods are unimplemented.\n */\nexport class SSLPinningWeb extends WebPlugin {\n /**\n * Checks a single SSL certificate against the expected fingerprint.\n * @return A promise that resolves to the result of the certificate check.\n * @throws CapacitorException indicating unimplemented functionality.\n */\n async checkCertificate() {\n throw this.createUnimplementedError();\n }\n /**\n * Checks multiple SSL certificates against their expected fingerprints.\n * @return A promise that resolves to an array of results for each certificate check.\n * @throws CapacitorException indicating unimplemented functionality.\n */\n async checkCertificates() {\n throw this.createUnimplementedError();\n }\n // --- Plugin Info ---\n /**\n * Returns the plugin version.\n *\n * On the Web, this value represents the JavaScript package version\n * rather than a native implementation.\n */\n async getPluginVersion() {\n return { version: 'web' };\n }\n /**\n * Creates a standardized exception for unimplemented methods.\n *\n * This utility method centralizes the creation of exceptions for functionality that is not supported\n * on the current platform, ensuring consistency in error reporting.\n *\n * @returns {CapacitorException} An exception with the code `Unimplemented` and a descriptive message.\n */\n createUnimplementedError() {\n return new CapacitorException('This plugin method is not implemented on this platform.', ExceptionCode.Unimplemented);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["SSLPinningErrorCode","registerPlugin","WebPlugin","CapacitorException","ExceptionCode"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACWA;AACX,CAAC,UAAU,mBAAmB,EAAE;AAChC;AACA,IAAI,mBAAmB,CAAC,aAAa,CAAC,GAAG,aAAa;AACtD;AACA,IAAI,mBAAmB,CAAC,mBAAmB,CAAC,GAAG,mBAAmB;AAClE;AACA,IAAI,mBAAmB,CAAC,aAAa,CAAC,GAAG,aAAa;AACtD;AACA,IAAI,mBAAmB,CAAC,cAAc,CAAC,GAAG,cAAc;AACxD,CAAC,EAAEA,2BAAmB,KAAKA,2BAAmB,GAAG,EAAE,CAAC,CAAC;;ACtBrD;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACK,MAAC,UAAU,GAAGC,mBAAc,CAAC,YAAY,EAAE;AAChD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;AACjE,CAAC;;ACZD;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,aAAa,SAASC,cAAS,CAAC;AAC7C;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,gBAAgB,GAAG;AAC7B,QAAQ,MAAM,IAAI,CAAC,wBAAwB,EAAE;AAC7C,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,iBAAiB,GAAG;AAC9B,QAAQ,MAAM,IAAI,CAAC,wBAAwB,EAAE;AAC7C,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,gBAAgB,GAAG;AAC7B,QAAQ,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AACjC,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,wBAAwB,GAAG;AAC/B,QAAQ,OAAO,IAAIC,uBAAkB,CAAC,yDAAyD,EAAEC,kBAAa,CAAC,aAAa,CAAC;AAC7H,IAAI;AACJ;;;;;;;;;"}
|
|
File without changes
|