@bricks-soft/capacitor-thermal-printer 0.0.0-stage → 8.0.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 (35) hide show
  1. package/BricksSoftCapacitorThermalPrinter.podspec +17 -0
  2. package/LICENSE +201 -0
  3. package/Package.swift +13 -0
  4. package/README.md +80 -2
  5. package/android/build.gradle +40 -0
  6. package/android/src/main/AndroidManifest.xml +8 -0
  7. package/android/src/main/java/com/brickssoft/thermalprinter/ThermalPrinterPlugin.java +463 -0
  8. package/dist/esm/definitions.d.ts +73 -0
  9. package/dist/esm/definitions.js +1 -0
  10. package/dist/esm/index.d.ts +3 -0
  11. package/dist/esm/index.js +23 -0
  12. package/dist/esm/web.d.ts +13 -0
  13. package/dist/esm/web.js +33 -0
  14. package/dist/plugin.cjs.js +65 -0
  15. package/dist/plugin.js +68 -0
  16. package/ios/Sources/ThermalPrinter/EscPosBarcode.swift +121 -0
  17. package/ios/Sources/ThermalPrinter/EscPosBitmap.swift +178 -0
  18. package/ios/Sources/ThermalPrinter/EscPosBluetooth.swift +341 -0
  19. package/ios/Sources/ThermalPrinter/EscPosBluetoothConnection.swift +298 -0
  20. package/ios/Sources/ThermalPrinter/EscPosBluetoothRules.swift +78 -0
  21. package/ios/Sources/ThermalPrinter/EscPosCharsetEncoding.swift +115 -0
  22. package/ios/Sources/ThermalPrinter/EscPosCoreBluetoothLink.swift +287 -0
  23. package/ios/Sources/ThermalPrinter/EscPosDeviceConnection.swift +47 -0
  24. package/ios/Sources/ThermalPrinter/EscPosJavaCompat.swift +213 -0
  25. package/ios/Sources/ThermalPrinter/EscPosKnownPrinterStore.swift +44 -0
  26. package/ios/Sources/ThermalPrinter/EscPosPrinter.swift +55 -0
  27. package/ios/Sources/ThermalPrinter/EscPosPrinterCommands.swift +367 -0
  28. package/ios/Sources/ThermalPrinter/EscPosPrinterSize.swift +53 -0
  29. package/ios/Sources/ThermalPrinter/EscPosQRCode.swift +70 -0
  30. package/ios/Sources/ThermalPrinter/EscPosQueue.swift +30 -0
  31. package/ios/Sources/ThermalPrinter/EscPosTcpConnection.swift +265 -0
  32. package/ios/Sources/ThermalPrinter/PrinterTextParser.swift +622 -0
  33. package/ios/Sources/ThermalPrinter/ThermalPrinterPlugin.swift +85 -0
  34. package/ios/Sources/ThermalPrinter/ThermalPrinterService.swift +380 -0
  35. package/package.json +61 -4
@@ -0,0 +1,85 @@
1
+ import Capacitor
2
+ import Foundation
3
+
4
+ @objc(ThermalPrinterPlugin)
5
+ public final class ThermalPrinterPlugin: CAPPlugin, CAPBridgedPlugin {
6
+ public let identifier = "ThermalPrinterPlugin"
7
+ public let jsName = "ThermalPrinter"
8
+ public let pluginMethods: [CAPPluginMethod] = [
9
+ CAPPluginMethod(name: "listPrinters", returnType: CAPPluginReturnPromise),
10
+ CAPPluginMethod(name: "printFormattedText", returnType: CAPPluginReturnPromise),
11
+ CAPPluginMethod(name: "printFormattedTextAndCut", returnType: CAPPluginReturnPromise),
12
+ CAPPluginMethod(name: "getEncoding", returnType: CAPPluginReturnPromise),
13
+ CAPPluginMethod(name: "disconnectPrinter", returnType: CAPPluginReturnPromise),
14
+ CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
15
+ CAPPluginMethod(name: "requestBTPermissions", returnType: CAPPluginReturnPromise),
16
+ CAPPluginMethod(name: "requestUSBPermissions", returnType: CAPPluginReturnPromise),
17
+ CAPPluginMethod(name: "bitmapToHexadecimalString", returnType: CAPPluginReturnPromise),
18
+ CAPPluginMethod(name: "bytesToHexadecimalString", returnType: CAPPluginReturnPromise),
19
+ ]
20
+ private let service = ThermalPrinterService()
21
+
22
+ deinit {
23
+ service.cancelPendingRequests()
24
+ }
25
+
26
+ @objc public func listPrinters(_ call: CAPPluginCall) {
27
+ run("listPrinters", call)
28
+ }
29
+
30
+ @objc public func printFormattedText(_ call: CAPPluginCall) {
31
+ run("printFormattedText", call)
32
+ }
33
+
34
+ @objc public func printFormattedTextAndCut(_ call: CAPPluginCall) {
35
+ run("printFormattedTextAndCut", call)
36
+ }
37
+
38
+ @objc public func getEncoding(_ call: CAPPluginCall) {
39
+ run("getEncoding", call)
40
+ }
41
+
42
+ @objc public func disconnectPrinter(_ call: CAPPluginCall) {
43
+ run("disconnectPrinter", call)
44
+ }
45
+
46
+ @objc override public func requestPermissions(_ call: CAPPluginCall) {
47
+ run("requestPermissions", call)
48
+ }
49
+
50
+ @objc public func requestBTPermissions(_ call: CAPPluginCall) {
51
+ run("requestBTPermissions", call)
52
+ }
53
+
54
+ @objc public func requestUSBPermissions(_ call: CAPPluginCall) {
55
+ run("requestPermissions", call)
56
+ }
57
+
58
+ @objc public func bitmapToHexadecimalString(_ call: CAPPluginCall) {
59
+ run("bitmapToHexadecimalString", call)
60
+ }
61
+
62
+ @objc public func bytesToHexadecimalString(_ call: CAPPluginCall) {
63
+ guard let bytes = call.getArray("bytes", Int.self), bytes.allSatisfy({ (0...255).contains($0) }) else {
64
+ call.reject("bytes must contain PNG or JPEG bytes in the range 0...255")
65
+ return
66
+ }
67
+ var options = call.options as? [String: Any] ?? [:]
68
+ options["base64"] = Data(bytes.map(UInt8.init)).base64EncodedString()
69
+ run("bitmapToHexadecimalString", call, options: options)
70
+ }
71
+
72
+ private func run(_ action: String, _ call: CAPPluginCall, options: [String: Any]? = nil) {
73
+ service.execute(action, [options ?? (call.options as? [String: Any] ?? [:])]) { result in
74
+ switch result {
75
+ case .success(let payload):
76
+ call.resolve(payload.map { ["value": $0] } ?? [:])
77
+ case .error(let payload):
78
+ let data = payload as? [String: Any]
79
+ call.reject(data?["error"] as? String ?? String(describing: payload), nil, nil, data)
80
+ case .cancelled:
81
+ call.reject("Printer request cancelled")
82
+ }
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,380 @@
1
+ import CoreBluetooth
2
+ import Foundation
3
+
4
+ enum ThermalPrinterResult {
5
+ case success(Any?)
6
+ case error(Any)
7
+ case cancelled
8
+ }
9
+
10
+ struct ThermalPrinterFailure: Error {
11
+ let payload: Any
12
+ }
13
+
14
+ struct ThermalPrinterJSONError: Error {
15
+ let message: String
16
+ }
17
+
18
+ struct ThermalPrinterArguments {
19
+ let values: [String: Any]
20
+
21
+ init(_ values: [String: Any]) {
22
+ self.values = values
23
+ }
24
+
25
+ func has(_ key: String) -> Bool {
26
+ values[key] != nil
27
+ }
28
+
29
+ func getString(_ key: String) throws -> String {
30
+ guard let value = values[key] else {
31
+ throw ThermalPrinterJSONError(message: "No value for \(key)")
32
+ }
33
+ return ThermalPrinterArguments.javaString(value)
34
+ }
35
+
36
+ func optString(_ key: String, _ fallback: String = "") -> String {
37
+ guard let value = values[key] else { return fallback }
38
+ return ThermalPrinterArguments.javaString(value)
39
+ }
40
+
41
+ func optInt(_ key: String, _ fallback: Int) -> Int {
42
+ guard let value = values[key] else { return fallback }
43
+ if ThermalPrinterArguments.isBoolean(value) { return fallback }
44
+ if let number = value as? NSNumber { return JavaMath.toInt(number.doubleValue) }
45
+ if let string = value as? String, let parsed = JavaMath.parseDouble(string) { return JavaMath.toInt(parsed) }
46
+ return fallback
47
+ }
48
+
49
+ func optDouble(_ key: String, _ fallback: Double) -> Double {
50
+ guard let value = values[key] else { return fallback }
51
+ if ThermalPrinterArguments.isBoolean(value) { return fallback }
52
+ if let number = value as? NSNumber { return number.doubleValue }
53
+ if let string = value as? String, let parsed = JavaMath.parseDouble(string) { return parsed }
54
+ return fallback
55
+ }
56
+
57
+ func getDouble(_ key: String) throws -> Double {
58
+ guard let value = values[key] else {
59
+ throw ThermalPrinterJSONError(message: "No value for \(key)")
60
+ }
61
+ if !ThermalPrinterArguments.isBoolean(value) {
62
+ if let number = value as? NSNumber { return number.doubleValue }
63
+ if let string = value as? String, let parsed = JavaMath.parseDouble(string) { return parsed }
64
+ }
65
+ throw ThermalPrinterJSONError(message: "Value \(ThermalPrinterArguments.javaString(value)) at \(key) cannot be converted to double")
66
+ }
67
+
68
+ func optObject(_ key: String) -> ThermalPrinterArguments? {
69
+ guard let object = values[key] as? [String: Any] else { return nil }
70
+ return ThermalPrinterArguments(object)
71
+ }
72
+
73
+ private static func isBoolean(_ value: Any) -> Bool {
74
+ guard let number = value as? NSNumber else { return false }
75
+ return CFGetTypeID(number) == CFBooleanGetTypeID()
76
+ }
77
+
78
+ private static func javaString(_ value: Any) -> String {
79
+ if value is NSNull { return "null" }
80
+ if let string = value as? String { return string }
81
+ if let number = value as? NSNumber {
82
+ if isBoolean(number) { return number.boolValue ? "true" : "false" }
83
+ return number.stringValue
84
+ }
85
+ return String(describing: value)
86
+ }
87
+ }
88
+
89
+ final class ThermalPrinterService {
90
+ private let queue = DispatchQueue(label: "de.paystory.thermal-printer.commands")
91
+ private var connections: [String: EscPosDeviceConnection] = [:]
92
+ private var bluetoothCentral: EscPosBluetoothCentral?
93
+ private let makeBluetoothCentral: () -> EscPosBluetoothCentral
94
+ private let makeTcpConnection: (String, Int) -> EscPosDeviceConnection
95
+
96
+ init(
97
+ makeBluetoothCentral: @escaping () -> EscPosBluetoothCentral = { EscPosBluetoothCentral() },
98
+ makeTcpConnection: @escaping (String, Int) -> EscPosDeviceConnection = { EscPosTcpConnection(address: $0, port: $1) }
99
+ ) {
100
+ self.makeBluetoothCentral = makeBluetoothCentral
101
+ self.makeTcpConnection = makeTcpConnection
102
+ }
103
+
104
+ deinit {
105
+ bluetoothCentral?.cancelAuthorizationRequests()
106
+ }
107
+
108
+ private var bluetooth: EscPosBluetoothCentral {
109
+ if let central = bluetoothCentral {
110
+ return central
111
+ }
112
+ let central = makeBluetoothCentral()
113
+ bluetoothCentral = central
114
+ return central
115
+ }
116
+
117
+ func execute(_ action: String, _ arguments: [Any], completion: @escaping (ThermalPrinterResult) -> Void) {
118
+ queue.async {
119
+ if action == "requestBTPermissions", arguments.first is [String: Any] {
120
+ self.requestBTPermissions(completion)
121
+ return
122
+ }
123
+ completion(self.run(action, arguments))
124
+ }
125
+ }
126
+
127
+ func cancelPendingRequests() {
128
+ queue.async {
129
+ self.bluetoothCentral?.cancelAuthorizationRequests()
130
+ }
131
+ }
132
+
133
+ func run(_ action: String, _ arguments: [Any]) -> ThermalPrinterResult {
134
+ guard let object = arguments.first as? [String: Any] else {
135
+ return .error("Value at 0 is not a JSONObject")
136
+ }
137
+ let data = ThermalPrinterArguments(object)
138
+ do {
139
+ switch action {
140
+ case "listPrinters":
141
+ return try listPrinters(data)
142
+ case "printFormattedText", "printFormattedTextAndCut":
143
+ return try printFormattedText(action, data)
144
+ case "getEncoding":
145
+ return try getEncoding(data)
146
+ case "disconnectPrinter":
147
+ return try disconnectPrinter(data)
148
+ case "requestPermissions":
149
+ return try requestUSBPermissions(data)
150
+ case "bitmapToHexadecimalString":
151
+ return try bitmapToHexadecimalString(data)
152
+ default:
153
+ return .error("Invalid action")
154
+ }
155
+ } catch let failure as ThermalPrinterFailure {
156
+ return .error(failure.payload)
157
+ } catch let failure as ThermalPrinterJSONError {
158
+ return .error(failure.message)
159
+ } catch let failure as EscPosError {
160
+ return .error(["error": failure.message])
161
+ } catch {
162
+ return .error(["error": error.localizedDescription])
163
+ }
164
+ }
165
+
166
+ private func requestBTPermissions(_ completion: @escaping (ThermalPrinterResult) -> Void) {
167
+ bluetooth.requestAuthorization { answer in
168
+ switch answer {
169
+ case .some(.allowedAlways):
170
+ completion(.success(["granted": true]))
171
+ case .none:
172
+ completion(.cancelled)
173
+ default:
174
+ completion(.error(["granted": false]))
175
+ }
176
+ }
177
+ }
178
+
179
+ private func listPrinters(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
180
+ let type = try data.getString("type")
181
+ guard type == "bluetooth" else {
182
+ return .success([[String: Any]]())
183
+ }
184
+ try checkBluetooth()
185
+ let printers: [[String: Any]] = bluetooth.listDevices().map { device in
186
+ var printer: [String: Any] = [
187
+ "address": device.identifier.uuidString,
188
+ "type": 2,
189
+ ]
190
+ if let name = device.name {
191
+ printer["name"] = name
192
+ }
193
+ if !device.services.isEmpty {
194
+ printer["features"] = device.services.map(\.uuidString)
195
+ }
196
+ return printer
197
+ }
198
+ return .success(printers)
199
+ }
200
+
201
+ private func printFormattedText(_ action: String, _ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
202
+ try printFormattedText(action, data, printer: getPrinter(data))
203
+ }
204
+
205
+ func printFormattedText(_ action: String, _ data: ThermalPrinterArguments, printer: EscPosPrinter) throws -> ThermalPrinterResult {
206
+ do {
207
+ let dotsFeedPaper = data.has("mmFeedPaper")
208
+ ? printer.mmToPx(Float(try data.getDouble("mmFeedPaper")))
209
+ : data.optInt("dotsFeedPaper", 20)
210
+ let text = try data.getString("text")
211
+ if action.hasSuffix("Cut") {
212
+ try printer.printFormattedTextAndCut(text, dotsFeedPaper: dotsFeedPaper)
213
+ } else {
214
+ try printer.printFormattedText(text, dotsFeedPaper: dotsFeedPaper)
215
+ }
216
+ return .success(nil)
217
+ } catch let failure as EscPosError {
218
+ printer.printer?.printerConnection.clearBuffer()
219
+ throw ThermalPrinterFailure(payload: ["error": failure.message])
220
+ } catch let failure as ThermalPrinterJSONError {
221
+ printer.printer?.printerConnection.clearBuffer()
222
+ throw ThermalPrinterFailure(payload: ["error": failure.message])
223
+ }
224
+ }
225
+
226
+ private func getEncoding(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
227
+ try encodingResult(getPrinter(data))
228
+ }
229
+
230
+ func encodingResult(_ printer: EscPosPrinter) -> ThermalPrinterResult {
231
+ guard let encoding = printer.encoding else {
232
+ return .success("null")
233
+ }
234
+ return .success([
235
+ "name": encoding.name,
236
+ "command": encoding.command.map { Int(Int8(bitPattern: $0)) },
237
+ ])
238
+ }
239
+
240
+ private func disconnectPrinter(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
241
+ let printer = try getPrinter(data)
242
+ printer.disconnectPrinter()
243
+ return .success(nil)
244
+ }
245
+
246
+ private func requestUSBPermissions(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
247
+ _ = try getPrinterConnection(data)
248
+ return .error(["error": "USB printers are not supported on iOS."])
249
+ }
250
+
251
+ private func bitmapToHexadecimalString(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
252
+ let encoded = try data.getString("base64")
253
+ let payload: Substring
254
+ if let comma = encoded.firstIndex(of: ",") {
255
+ payload = encoded[encoded.index(after: comma)...]
256
+ } else {
257
+ payload = Substring(encoded)
258
+ }
259
+ guard let bytes = ThermalPrinterService.decodeBase64(payload) else {
260
+ return .error(["error": "bad base-64"])
261
+ }
262
+ let printer = try getPrinter(data)
263
+ guard let bitmap = EscPosBitmap.decode(bytes) else {
264
+ return .error(["error": "Unable to decode the image."])
265
+ }
266
+ return .success(PrinterTextParserImg.bitmapToHexadecimalString(printer, bitmap))
267
+ }
268
+
269
+ static func decodeBase64(_ text: Substring) -> Data? {
270
+ var cleaned = text.filter { !$0.isWhitespace }
271
+ let remainder = cleaned.count % 4
272
+ if remainder == 1 {
273
+ return nil
274
+ }
275
+ if remainder > 0 {
276
+ cleaned.append(String(repeating: "=", count: 4 - remainder))
277
+ }
278
+ return Data(base64Encoded: cleaned)
279
+ }
280
+
281
+ func getPrinter(_ data: ThermalPrinterArguments) throws -> EscPosPrinter {
282
+ let connection = try getPrinterConnection(data)
283
+ return try makePrinter(connection: connection, data: data)
284
+ }
285
+
286
+ func makePrinter(connection: EscPosDeviceConnection, data: ThermalPrinterArguments) throws -> EscPosPrinter {
287
+ var charsetEncoding: EscPosCharsetEncoding?
288
+ if let charset = data.optObject("charsetEncoding") {
289
+ charsetEncoding = EscPosCharsetEncoding(
290
+ name: charset.optString("charsetName", "windows-1252"),
291
+ escPosCharsetId: charset.optInt("charsetId", 16)
292
+ )
293
+ }
294
+ do {
295
+ return try EscPosPrinter(
296
+ connection: connection,
297
+ printerDpi: data.optInt("printerDpi", 203),
298
+ printerWidthMM: Float(data.optDouble("printerWidthMM", 48)),
299
+ printerNbrCharactersPerLine: data.optInt("printerNbrCharactersPerLine", 32),
300
+ charsetEncoding: charsetEncoding
301
+ )
302
+ } catch let failure as EscPosError {
303
+ throw ThermalPrinterFailure(payload: ["error": failure.message])
304
+ }
305
+ }
306
+
307
+ private func getPrinterConnection(_ data: ThermalPrinterArguments) throws -> EscPosDeviceConnection {
308
+ let type = try data.getString("type")
309
+ let id = try data.getString("id")
310
+ let hashKey = "\(type)-\(id)"
311
+ guard let connection = try getDevice(
312
+ hashKey: hashKey,
313
+ type: type,
314
+ id: data.optString("id"),
315
+ address: data.optString("address"),
316
+ port: data.optInt("port", 9100)
317
+ ) else {
318
+ throw ThermalPrinterFailure(payload: [
319
+ "error": "Device not found or not connected!",
320
+ "type": type,
321
+ "id": id,
322
+ ])
323
+ }
324
+ connections[hashKey] = connection
325
+ return connection
326
+ }
327
+
328
+ private func getDevice(hashKey: String, type: String, id: String, address: String, port: Int) throws -> EscPosDeviceConnection? {
329
+ if let existing = connections[hashKey] {
330
+ if existing.isConnected {
331
+ return existing
332
+ }
333
+ connections.removeValue(forKey: hashKey)
334
+ }
335
+
336
+ switch type {
337
+ case "bluetooth":
338
+ try checkBluetooth()
339
+ if id == "first" {
340
+ for peripheral in bluetooth.candidatesForFirst() {
341
+ let connection = bluetooth.connection(for: peripheral)
342
+ if (try? connection.connect()) != nil {
343
+ return connection
344
+ }
345
+ }
346
+ return nil
347
+ }
348
+ guard let peripheral = bluetooth.findPeripheral(id: id, address: address) else {
349
+ return nil
350
+ }
351
+ return bluetooth.connection(for: peripheral)
352
+ case "tcp":
353
+ return makeTcpConnection(address, port)
354
+ default:
355
+ return nil
356
+ }
357
+ }
358
+
359
+ private func checkBluetooth() throws {
360
+ if bluetooth.authorization == .notDetermined {
361
+ _ = bluetooth.waitForAuthorization()
362
+ }
363
+ switch bluetooth.authorization {
364
+ case .denied, .restricted, .notDetermined:
365
+ throw ThermalPrinterFailure(payload: ["error": "Missing permission for Bluetooth!"])
366
+ default:
367
+ break
368
+ }
369
+ switch bluetooth.waitForState() {
370
+ case .poweredOn:
371
+ return
372
+ case .unsupported:
373
+ throw ThermalPrinterFailure(payload: ["error": "Device doesn't support Bluetooth!"])
374
+ case .unauthorized:
375
+ throw ThermalPrinterFailure(payload: ["error": "Missing permission for Bluetooth!"])
376
+ default:
377
+ throw ThermalPrinterFailure(payload: ["error": "Device not enabled Bluetooth!"])
378
+ }
379
+ }
380
+ }
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@bricks-soft/capacitor-thermal-printer",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "8.0.0",
4
+ "description": "ESC/POS receipt printing over Bluetooth, TCP and USB for Capacitor.",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Sources/",
14
+ "Package.swift",
15
+ "BricksSoftCapacitorThermalPrinter.podspec"
16
+ ],
17
+ "author": "Bricks Soft",
18
+ "license": "Apache-2.0",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/bricks-soft/capacitor-thermal-printer.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/bricks-soft/capacitor-thermal-printer/issues"
25
+ },
26
+ "homepage": "https://github.com/bricks-soft/capacitor-thermal-printer#readme",
27
+ "keywords": [
28
+ "capacitor",
29
+ "plugin",
30
+ "thermal",
31
+ "printer",
32
+ "escpos"
33
+ ],
34
+ "scripts": {
35
+ "clean": "rimraf dist",
36
+ "build": "npm run clean && tsc && rollup -c rollup.config.mjs",
37
+ "test": "npm run build && node --test test/*.mjs",
38
+ "prepare": "npm run build"
39
+ },
40
+ "devDependencies": {
41
+ "@capacitor/core": "8.5.2",
42
+ "@capacitor/android": "8.5.2",
43
+ "@capacitor/cli": "8.5.2",
44
+ "@capacitor/ios": "8.5.2",
45
+ "rimraf": "^6.1.3",
46
+ "rollup": "^4.53.2",
47
+ "typescript": "~5.9.3"
48
+ },
49
+ "peerDependencies": {
50
+ "@capacitor/core": ">=8.0.0 <9.0.0"
51
+ },
52
+ "capacitor": {
53
+ "android": {
54
+ "src": "android"
55
+ },
56
+ "ios": {
57
+ "src": "ios"
58
+ }
59
+ },
60
+ "publishConfig": {
61
+ "access": "public"
62
+ }
63
+ }