@parselo/capacitor-mrz 0.2.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/ParseloCapacitorMrz.podspec +17 -0
- package/README.md +107 -0
- package/android/build.gradle +49 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/parselo/mrz/MrzPlugin.kt +72 -0
- package/dist/definitions.d.ts +16 -0
- package/dist/definitions.js +6 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7 -0
- package/dist/web.d.ts +8 -0
- package/dist/web.js +7 -0
- package/ios/MrzPlugin.m +7 -0
- package/ios/MrzPlugin.swift +100 -0
- package/package.json +50 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'ParseloCapacitorMrz'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = 'https://github.com/your-org/idscan-canada'
|
|
11
|
+
s.author = 'IDScan Canada'
|
|
12
|
+
s.source = { :git => 'https://github.com/your-org/idscan-canada.git', :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/**/*.{swift,h,m}'
|
|
14
|
+
s.ios.deployment_target = '13.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.1'
|
|
17
|
+
end
|
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# @parselo/capacitor-mrz
|
|
2
|
+
|
|
3
|
+
Capacitor plugin for on-device MRZ (Machine Readable Zone) text recognition.
|
|
4
|
+
Wraps Apple Vision (`VNRecognizeTextRequest`) on iOS and ML Kit Text Recognition
|
|
5
|
+
on Android. Returns the recognised text lines; pair with `@parselo/scanner-core`
|
|
6
|
+
to locate, parse, and validate the ICAO 9303 MRZ.
|
|
7
|
+
|
|
8
|
+
Supports all ICAO TD3 travel document types: standard passports (`P<`),
|
|
9
|
+
emergency travel documents (`PU`), permanent resident travel documents (`PR`),
|
|
10
|
+
and foreign passports.
|
|
11
|
+
|
|
12
|
+
## Platform support
|
|
13
|
+
|
|
14
|
+
| Platform | Backend | Min version |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| iOS | Vision — `VNRecognizeTextRequest`, `.accurate` level | iOS 13 |
|
|
17
|
+
| Android | ML Kit Text Recognition v2, Latin script | API 21 |
|
|
18
|
+
|
|
19
|
+
Language correction is disabled on both platforms — it would corrupt MRZ
|
|
20
|
+
tokens and check digits.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @parselo/capacitor-mrz @parselo/scanner-core
|
|
26
|
+
npx cap sync
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### iOS — camera permission
|
|
30
|
+
|
|
31
|
+
Add to `ios/App/App/Info.plist` (if not already present):
|
|
32
|
+
|
|
33
|
+
```xml
|
|
34
|
+
<key>NSCameraUsageDescription</key>
|
|
35
|
+
<string>Used to scan the MRZ on your travel document.</string>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
CocoaPods installs the `MrzPlugin.m` ObjC bridge automatically. If the plugin
|
|
39
|
+
throws "not implemented" at runtime, verify that `pod install` ran after
|
|
40
|
+
`cap sync` and that `MrzPlugin.m` is listed in the Xcode target's Compile
|
|
41
|
+
Sources.
|
|
42
|
+
|
|
43
|
+
### Android
|
|
44
|
+
|
|
45
|
+
No extra configuration needed — the ML Kit dependency is declared in the
|
|
46
|
+
plugin's `build.gradle`.
|
|
47
|
+
|
|
48
|
+
## API
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { Mrz } from "@parselo/capacitor-mrz";
|
|
52
|
+
|
|
53
|
+
const { lines, raw } = await Mrz.recognizeText({ image });
|
|
54
|
+
// lines: string[] — one element per Vision/ML Kit text observation
|
|
55
|
+
// raw: string — lines joined with "\n"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`image` accepts a `data:` URI, `file://` URI, bare absolute path, or raw base64 string.
|
|
59
|
+
|
|
60
|
+
Pass the full camera frame — the plugin sends the entire image to the OCR
|
|
61
|
+
engine. `scanner-core` locates the two 44-char MRZ lines anywhere in the
|
|
62
|
+
returned array and handles OCR noise in the surrounding biographical text.
|
|
63
|
+
|
|
64
|
+
## Integration with scanner-core
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { Scanner } from "@parselo/scanner-core";
|
|
68
|
+
import { Mrz } from "@parselo/capacitor-mrz";
|
|
69
|
+
|
|
70
|
+
const scanner = new Scanner({
|
|
71
|
+
license: {
|
|
72
|
+
token: "…",
|
|
73
|
+
bundleId: "com.example.app",
|
|
74
|
+
publicKeys: { "<kid>": "<base64-spki>" },
|
|
75
|
+
},
|
|
76
|
+
analytics: {
|
|
77
|
+
ingestUrl: "…",
|
|
78
|
+
store: { get, set },
|
|
79
|
+
},
|
|
80
|
+
native: { captureAndDecodePdf417: … }, // still needed for barcode scanning
|
|
81
|
+
mrzNative: {
|
|
82
|
+
captureAndRecognizeMrz: async () => {
|
|
83
|
+
const { lines } = await Mrz.recognizeText({ image: dataUrl });
|
|
84
|
+
return lines;
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await scanner.init();
|
|
90
|
+
const result = await scanner.scanPassport();
|
|
91
|
+
// result.document.documentType === "passport"
|
|
92
|
+
// result.document.fields.firstName.value, .dateOfBirth.value, .country.value, …
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### How name extraction works
|
|
96
|
+
|
|
97
|
+
OCR-B MRZ uses `<` as a word separator, which Vision and ML Kit frequently
|
|
98
|
+
misread as `C`, `S`, `K`, `@`, etc. `scanner-core` automatically cross-references
|
|
99
|
+
Vision's biographical-zone lines (the human-readable area above the MRZ, read
|
|
100
|
+
in a standard font) to recover correct first and last names even when the MRZ
|
|
101
|
+
separator characters are corrupted.
|
|
102
|
+
|
|
103
|
+
See the [full integration guide](https://github.com/parselo-io/parselo-sdk/blob/main/integration-guide.md) for a complete setup walkthrough including the camera UI component.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Android build for @parselo/capacitor-mrz.
|
|
2
|
+
// SDK/AGP/Kotlin versions fall back to the host app's rootProject.ext when synced.
|
|
3
|
+
|
|
4
|
+
ext {
|
|
5
|
+
mrzMlkitVersion = project.hasProperty('mlkitTextVersion') ? rootProject.ext.mlkitTextVersion : '16.0.1'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
ext.kotlin_version = project.hasProperty('kotlinVersion') ? rootProject.ext.kotlinVersion : '1.9.25'
|
|
10
|
+
repositories {
|
|
11
|
+
google()
|
|
12
|
+
mavenCentral()
|
|
13
|
+
}
|
|
14
|
+
dependencies {
|
|
15
|
+
classpath 'com.android.tools.build:gradle:8.2.1'
|
|
16
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
apply plugin: 'com.android.library'
|
|
21
|
+
apply plugin: 'kotlin-android'
|
|
22
|
+
|
|
23
|
+
android {
|
|
24
|
+
namespace "com.parselo.mrz"
|
|
25
|
+
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
|
|
26
|
+
defaultConfig {
|
|
27
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
|
|
28
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
|
|
29
|
+
}
|
|
30
|
+
compileOptions {
|
|
31
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
32
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
33
|
+
}
|
|
34
|
+
kotlinOptions {
|
|
35
|
+
jvmTarget = '17'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
repositories {
|
|
40
|
+
google()
|
|
41
|
+
mavenCentral()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
dependencies {
|
|
45
|
+
implementation project(':capacitor-android')
|
|
46
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
47
|
+
// ML Kit Text Recognition v2 — Latin script; no network call, fully on-device.
|
|
48
|
+
implementation "com.google.mlkit:text-recognition:$mrzMlkitVersion"
|
|
49
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
<!-- No permissions declared here: the plugin receives an already-captured
|
|
4
|
+
image and runs text recognition on it. Camera permission is the app's
|
|
5
|
+
responsibility, not the plugin's. -->
|
|
6
|
+
</manifest>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
package com.parselo.mrz
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.BitmapFactory
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.util.Base64
|
|
7
|
+
import com.getcapacitor.JSArray
|
|
8
|
+
import com.getcapacitor.JSObject
|
|
9
|
+
import com.getcapacitor.Plugin
|
|
10
|
+
import com.getcapacitor.PluginCall
|
|
11
|
+
import com.getcapacitor.PluginMethod
|
|
12
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
13
|
+
import com.google.mlkit.vision.common.InputImage
|
|
14
|
+
import com.google.mlkit.vision.text.TextRecognition
|
|
15
|
+
import com.google.mlkit.vision.text.latin.TextRecognizerOptions
|
|
16
|
+
|
|
17
|
+
@CapacitorPlugin(name = "Mrz")
|
|
18
|
+
class MrzPlugin : Plugin() {
|
|
19
|
+
|
|
20
|
+
@PluginMethod
|
|
21
|
+
fun recognizeText(call: PluginCall) {
|
|
22
|
+
val imageStr = call.getString("image") ?: run {
|
|
23
|
+
call.reject("Missing 'image'"); return
|
|
24
|
+
}
|
|
25
|
+
val bitmap = loadBitmap(imageStr) ?: run {
|
|
26
|
+
call.reject("Could not load image from the provided string"); return
|
|
27
|
+
}
|
|
28
|
+
recognize(bitmap) { lines ->
|
|
29
|
+
val arr = JSArray()
|
|
30
|
+
lines.forEach { arr.put(it) }
|
|
31
|
+
val ret = JSObject()
|
|
32
|
+
ret.put("lines", arr)
|
|
33
|
+
ret.put("raw", lines.joinToString("\n"))
|
|
34
|
+
call.resolve(ret)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ML Kit Text Recognition v2, Latin script, fully on-device.
|
|
39
|
+
private fun recognize(bitmap: Bitmap, callback: (List<String>) -> Unit) {
|
|
40
|
+
val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
|
|
41
|
+
val image = InputImage.fromBitmap(bitmap, 0)
|
|
42
|
+
recognizer.process(image)
|
|
43
|
+
.addOnSuccessListener { result ->
|
|
44
|
+
// Flatten to one entry per line so scanner-core can scan
|
|
45
|
+
// the array for consecutive 44-char TD3 lines.
|
|
46
|
+
val lines = result.textBlocks.flatMap { block ->
|
|
47
|
+
block.lines.map { line -> line.text }
|
|
48
|
+
}
|
|
49
|
+
callback(lines)
|
|
50
|
+
}
|
|
51
|
+
.addOnFailureListener { callback(emptyList()) }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Image loading: file path, file:// URI, data: URI, or bare base64.
|
|
55
|
+
private fun loadBitmap(s: String): Bitmap? {
|
|
56
|
+
return try {
|
|
57
|
+
when {
|
|
58
|
+
s.startsWith("data:") -> {
|
|
59
|
+
val b64 = s.substringAfter(",", "")
|
|
60
|
+
val bytes = Base64.decode(b64, Base64.DEFAULT)
|
|
61
|
+
BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
|
|
62
|
+
}
|
|
63
|
+
s.startsWith("file://") -> BitmapFactory.decodeFile(Uri.parse(s).path)
|
|
64
|
+
s.startsWith("/") -> BitmapFactory.decodeFile(s)
|
|
65
|
+
else -> {
|
|
66
|
+
val bytes = Base64.decode(s, Base64.DEFAULT)
|
|
67
|
+
BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (e: Exception) { null }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface MrzPlugin {
|
|
2
|
+
/**
|
|
3
|
+
* Run on-device text recognition on an image and return all detected lines.
|
|
4
|
+
* Pass the result to scanner-core's parseMrz() to locate and parse the MRZ.
|
|
5
|
+
*
|
|
6
|
+
* @param options.image file path, file:// URI, data: URI, or bare base64 string
|
|
7
|
+
* @returns lines — every text line detected by Vision / ML Kit
|
|
8
|
+
* @returns raw — lines joined by "\n" (convenience for logging/debugging)
|
|
9
|
+
*/
|
|
10
|
+
recognizeText(options: {
|
|
11
|
+
image: string;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
lines: string[];
|
|
14
|
+
raw: string;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// definitions.ts
|
|
2
|
+
// Capacitor plugin interface for native MRZ text recognition.
|
|
3
|
+
// The plugin recognises text in an image and returns all detected lines.
|
|
4
|
+
// scanner-core is responsible for locating the MRZ within those lines and
|
|
5
|
+
// parsing it — keeping all parsing logic cross-platform in the TS core.
|
|
6
|
+
export {};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/web.d.ts
ADDED
package/dist/web.js
ADDED
package/ios/MrzPlugin.m
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
import Vision
|
|
4
|
+
import UIKit
|
|
5
|
+
|
|
6
|
+
@objc(MrzPlugin)
|
|
7
|
+
public class MrzPlugin: CAPPlugin {
|
|
8
|
+
|
|
9
|
+
@objc func recognizeText(_ call: CAPPluginCall) {
|
|
10
|
+
guard let imageStr = call.getString("image") else {
|
|
11
|
+
print("[MrzPlugin] ERROR: Missing 'image' parameter")
|
|
12
|
+
call.reject("Missing 'image'")
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
print("[MrzPlugin] recognizeText called, image string length: \(imageStr.count)")
|
|
16
|
+
guard let image = Self.loadImage(imageStr) else {
|
|
17
|
+
print("[MrzPlugin] ERROR: Could not load image from the provided string")
|
|
18
|
+
call.reject("Could not load image from the provided string")
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
print("[MrzPlugin] Image loaded: \(Int(image.size.width))×\(Int(image.size.height)), orientation: \(image.imageOrientation.rawValue)")
|
|
22
|
+
Self.recognize(in: image) { lines in
|
|
23
|
+
print("[MrzPlugin] Vision returned \(lines.count) line(s):")
|
|
24
|
+
lines.enumerated().forEach { i, l in print("[MrzPlugin] [\(i)] (\(l.count) chars): \(l)") }
|
|
25
|
+
call.resolve([
|
|
26
|
+
"lines": lines,
|
|
27
|
+
"raw": lines.joined(separator: "\n"),
|
|
28
|
+
])
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// MARK: - Vision text recognition
|
|
33
|
+
|
|
34
|
+
private static func recognize(in image: UIImage, completion: @escaping ([String]) -> Void) {
|
|
35
|
+
guard let cgImage = image.cgImage else { completion([]); return }
|
|
36
|
+
|
|
37
|
+
let request = VNRecognizeTextRequest { req, err in
|
|
38
|
+
if let err = err {
|
|
39
|
+
print("[MrzPlugin] Vision error: \(err.localizedDescription)")
|
|
40
|
+
completion([]); return
|
|
41
|
+
}
|
|
42
|
+
guard let results = req.results as? [VNRecognizedTextObservation] else {
|
|
43
|
+
print("[MrzPlugin] Vision: no results")
|
|
44
|
+
completion([]); return
|
|
45
|
+
}
|
|
46
|
+
print("[MrzPlugin] Vision: \(results.count) text observation(s)")
|
|
47
|
+
let lines = results.compactMap { $0.topCandidates(1).first?.string }
|
|
48
|
+
completion(lines)
|
|
49
|
+
}
|
|
50
|
+
// Accurate mode is required for MRZ — fast mode misses characters.
|
|
51
|
+
request.recognitionLevel = .accurate
|
|
52
|
+
// Language correction must be off: it replaces valid MRZ tokens (e.g. "<<")
|
|
53
|
+
// with natural-language words and corrupts check digits.
|
|
54
|
+
request.usesLanguageCorrection = false
|
|
55
|
+
request.recognitionLanguages = ["en-US"]
|
|
56
|
+
|
|
57
|
+
let orientation = cgOrientation(from: image.imageOrientation)
|
|
58
|
+
let handler = VNImageRequestHandler(cgImage: cgImage, orientation: orientation, options: [:])
|
|
59
|
+
do {
|
|
60
|
+
try handler.perform([request])
|
|
61
|
+
} catch {
|
|
62
|
+
completion([])
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// MARK: - Image loading (file path, file:// URI, data: URI, or bare base64)
|
|
67
|
+
|
|
68
|
+
private static func loadImage(_ s: String) -> UIImage? {
|
|
69
|
+
if s.hasPrefix("data:") {
|
|
70
|
+
guard let comma = s.firstIndex(of: ","),
|
|
71
|
+
let data = Data(base64Encoded: String(s[s.index(after: comma)...])) else { return nil }
|
|
72
|
+
return UIImage(data: data)
|
|
73
|
+
}
|
|
74
|
+
if s.hasPrefix("file://") {
|
|
75
|
+
guard let path = URL(string: s)?.path else { return nil }
|
|
76
|
+
return UIImage(contentsOfFile: path)
|
|
77
|
+
}
|
|
78
|
+
if s.hasPrefix("/") {
|
|
79
|
+
return UIImage(contentsOfFile: s)
|
|
80
|
+
}
|
|
81
|
+
if let data = Data(base64Encoded: s) {
|
|
82
|
+
return UIImage(data: data)
|
|
83
|
+
}
|
|
84
|
+
return nil
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private static func cgOrientation(from o: UIImage.Orientation) -> CGImagePropertyOrientation {
|
|
88
|
+
switch o {
|
|
89
|
+
case .up: return .up
|
|
90
|
+
case .down: return .down
|
|
91
|
+
case .left: return .left
|
|
92
|
+
case .right: return .right
|
|
93
|
+
case .upMirrored: return .upMirrored
|
|
94
|
+
case .downMirrored: return .downMirrored
|
|
95
|
+
case .leftMirrored: return .leftMirrored
|
|
96
|
+
case .rightMirrored: return .rightMirrored
|
|
97
|
+
@unknown default: return .up
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@parselo/capacitor-mrz",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/parselo-io/parselo-sdk.git",
|
|
7
|
+
"directory": "packages/capacitor-mrz"
|
|
8
|
+
},
|
|
9
|
+
"description": "Native MRZ text recognition (Apple Vision / Android ML Kit) for Capacitor. Returns recognised lines; scanner-core locates and parses the MRZ.",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"module": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist/",
|
|
27
|
+
"ios/",
|
|
28
|
+
"android/",
|
|
29
|
+
"ParseloCapacitorMrz.podspec",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.json"
|
|
34
|
+
},
|
|
35
|
+
"capacitor": {
|
|
36
|
+
"ios": {
|
|
37
|
+
"src": "ios"
|
|
38
|
+
},
|
|
39
|
+
"android": {
|
|
40
|
+
"src": "android"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@capacitor/core": "^6.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@capacitor/core": "^6.0.0",
|
|
48
|
+
"typescript": "^5.5.0"
|
|
49
|
+
}
|
|
50
|
+
}
|