one 2.6.20 → 2.6.22

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 (154) hide show
  1. package/android/src/main/java/dev/onejs/onenative/OneNativeCameraManager.kt +24 -0
  2. package/android/src/main/java/dev/onejs/onenative/OneNativeCameraView.kt +197 -2
  3. package/cpp/HybridOneCrypto.cpp +327 -1
  4. package/cpp/HybridOneCrypto.hpp +8 -0
  5. package/dist/cjs/index.cjs +25 -22
  6. package/dist/cjs/index.native.cjs +25 -22
  7. package/dist/cjs/index.native.js +25 -22
  8. package/dist/cjs/index.native.js.map +1 -1
  9. package/dist/cjs/one.cjs +1 -0
  10. package/dist/cjs/one.native.cjs +1 -0
  11. package/dist/cjs/one.native.js +1 -0
  12. package/dist/cjs/one.native.js.map +1 -1
  13. package/dist/cjs/platform/camera/index.cjs +3 -2
  14. package/dist/cjs/platform/camera/index.native.cjs +89 -3
  15. package/dist/cjs/platform/camera/index.native.js +89 -3
  16. package/dist/cjs/platform/camera/index.native.js.map +1 -1
  17. package/dist/cjs/platform/crypto/aes.cjs +259 -0
  18. package/dist/cjs/platform/crypto/aes.native.cjs +343 -0
  19. package/dist/cjs/platform/crypto/aes.native.js +345 -0
  20. package/dist/cjs/platform/crypto/aes.native.js.map +1 -0
  21. package/dist/cjs/platform/crypto/bytes.cjs +132 -0
  22. package/dist/cjs/platform/crypto/bytes.native.cjs +175 -0
  23. package/dist/cjs/platform/crypto/bytes.native.js +177 -0
  24. package/dist/cjs/platform/crypto/bytes.native.js.map +1 -0
  25. package/dist/cjs/platform/crypto/digest-api.cjs +65 -0
  26. package/dist/cjs/platform/crypto/digest-api.native.cjs +69 -0
  27. package/dist/cjs/platform/crypto/digest-api.native.js +71 -0
  28. package/dist/cjs/platform/crypto/digest-api.native.js.map +1 -0
  29. package/dist/cjs/platform/crypto/digest.cjs +26 -15
  30. package/dist/cjs/platform/crypto/digest.native.cjs +26 -15
  31. package/dist/cjs/platform/crypto/digest.native.js +26 -15
  32. package/dist/cjs/platform/crypto/digest.native.js.map +1 -1
  33. package/dist/cjs/platform/crypto/index.cjs +19 -1
  34. package/dist/cjs/platform/crypto/index.native.cjs +39 -1
  35. package/dist/cjs/platform/crypto/index.native.js +39 -1
  36. package/dist/cjs/platform/crypto/index.native.js.map +1 -1
  37. package/dist/cjs/platform/crypto/types.cjs +16 -0
  38. package/dist/cjs/platform/crypto/types.native.cjs +18 -0
  39. package/dist/cjs/platform/crypto/types.native.js +20 -0
  40. package/dist/cjs/platform/crypto/types.native.js.map +1 -0
  41. package/dist/cjs/platform/crypto/web.cjs +73 -0
  42. package/dist/cjs/platform/extras.cjs +4 -0
  43. package/dist/cjs/platform/extras.native.cjs +4 -0
  44. package/dist/cjs/platform/extras.native.js +4 -0
  45. package/dist/cjs/platform/extras.native.js.map +1 -1
  46. package/dist/cjs/platform/specs/OneNativeCameraNativeComponent.cjs +37 -21
  47. package/dist/cjs/platform/specs/OneNativeCameraNativeComponent.native.cjs +37 -21
  48. package/dist/cjs/platform/specs/OneNativeCameraNativeComponent.native.js +37 -21
  49. package/dist/cjs/platform/specs/OneNativeCameraNativeComponent.native.js.map +3 -3
  50. package/dist/cjs/router/router.cjs +4 -1
  51. package/dist/esm/index.js +2 -2
  52. package/dist/esm/index.mjs +2 -2
  53. package/dist/esm/index.native.js +2 -2
  54. package/dist/esm/one.mjs +2 -1
  55. package/dist/esm/one.mjs.map +1 -1
  56. package/dist/esm/one.native.js +2 -1
  57. package/dist/esm/one.native.js.map +1 -1
  58. package/dist/esm/platform/camera/index.mjs +4 -2
  59. package/dist/esm/platform/camera/index.mjs.map +1 -1
  60. package/dist/esm/platform/camera/index.native.js +90 -4
  61. package/dist/esm/platform/camera/index.native.js.map +1 -1
  62. package/dist/esm/platform/crypto/aes.mjs +236 -0
  63. package/dist/esm/platform/crypto/aes.mjs.map +1 -0
  64. package/dist/esm/platform/crypto/aes.native.js +318 -0
  65. package/dist/esm/platform/crypto/aes.native.js.map +1 -0
  66. package/dist/esm/platform/crypto/bytes.mjs +104 -0
  67. package/dist/esm/platform/crypto/bytes.mjs.map +1 -0
  68. package/dist/esm/platform/crypto/bytes.native.js +145 -0
  69. package/dist/esm/platform/crypto/bytes.native.js.map +1 -0
  70. package/dist/esm/platform/crypto/digest-api.mjs +42 -0
  71. package/dist/esm/platform/crypto/digest-api.mjs.map +1 -0
  72. package/dist/esm/platform/crypto/digest-api.native.js +44 -0
  73. package/dist/esm/platform/crypto/digest-api.native.js.map +1 -0
  74. package/dist/esm/platform/crypto/digest.mjs +23 -15
  75. package/dist/esm/platform/crypto/digest.mjs.map +1 -1
  76. package/dist/esm/platform/crypto/digest.native.js +23 -15
  77. package/dist/esm/platform/crypto/digest.native.js.map +1 -1
  78. package/dist/esm/platform/crypto/index.mjs +14 -1
  79. package/dist/esm/platform/crypto/index.mjs.map +1 -1
  80. package/dist/esm/platform/crypto/index.native.js +34 -1
  81. package/dist/esm/platform/crypto/index.native.js.map +1 -1
  82. package/dist/esm/platform/crypto/types.mjs +0 -0
  83. package/dist/esm/platform/crypto/types.native.js +0 -0
  84. package/dist/esm/platform/crypto/web.mjs +50 -0
  85. package/dist/esm/platform/crypto/web.mjs.map +1 -0
  86. package/dist/esm/platform/extras.mjs +2 -1
  87. package/dist/esm/platform/extras.native.js +2 -1
  88. package/dist/esm/platform/specs/OneNativeCameraNativeComponent.mjs +25 -9
  89. package/dist/esm/platform/specs/OneNativeCameraNativeComponent.mjs.map +3 -3
  90. package/dist/esm/platform/specs/OneNativeCameraNativeComponent.native.js +25 -9
  91. package/dist/esm/platform/specs/OneNativeCameraNativeComponent.native.js.map +3 -3
  92. package/dist/esm/router/router.mjs +5 -2
  93. package/dist/esm/router/router.mjs.map +1 -1
  94. package/ios/OneNativeCameraComponentView.mm +28 -0
  95. package/ios/OneNativeCameraView.swift +163 -0
  96. package/nitrogen/generated/shared/c++/HybridOneCryptoSpec.cpp +2 -0
  97. package/nitrogen/generated/shared/c++/HybridOneCryptoSpec.hpp +4 -1
  98. package/package.json +12 -12
  99. package/src/index.ts +4 -0
  100. package/src/one.ts +3 -0
  101. package/src/platform/camera/index.native.tsx +116 -12
  102. package/src/platform/camera/index.tsx +15 -4
  103. package/src/platform/camera/types.ts +22 -0
  104. package/src/platform/crypto/aes.ts +345 -0
  105. package/src/platform/crypto/bytes.ts +120 -0
  106. package/src/platform/crypto/digest-api.ts +50 -0
  107. package/src/platform/crypto/digest.ts +24 -15
  108. package/src/platform/crypto/index.native.ts +68 -0
  109. package/src/platform/crypto/index.ts +31 -0
  110. package/src/platform/crypto/types.ts +64 -0
  111. package/src/platform/crypto/web.ts +69 -0
  112. package/src/platform/extras.ts +15 -0
  113. package/src/platform/index.native.ts +4 -0
  114. package/src/platform/index.ts +4 -0
  115. package/src/platform/specs/OneCrypto.nitro.ts +19 -0
  116. package/src/platform/specs/OneNativeCameraNativeComponent.ts +35 -2
  117. package/src/router/router.ts +7 -4
  118. package/types/index.d.ts +2 -1
  119. package/types/index.d.ts.map +1 -1
  120. package/types/one.d.ts +2 -1
  121. package/types/one.d.ts.map +1 -1
  122. package/types/platform/camera/index.d.ts +3 -3
  123. package/types/platform/camera/index.d.ts.map +1 -1
  124. package/types/platform/camera/index.native.d.ts +3 -3
  125. package/types/platform/camera/index.native.d.ts.map +1 -1
  126. package/types/platform/camera/types.d.ts +18 -0
  127. package/types/platform/camera/types.d.ts.map +1 -1
  128. package/types/platform/crypto/aes.d.ts +211 -0
  129. package/types/platform/crypto/aes.d.ts.map +1 -0
  130. package/types/platform/crypto/bytes.d.ts +8 -0
  131. package/types/platform/crypto/bytes.d.ts.map +1 -0
  132. package/types/platform/crypto/digest-api.d.ts +9 -0
  133. package/types/platform/crypto/digest-api.d.ts.map +1 -0
  134. package/types/platform/crypto/digest.d.ts +5 -0
  135. package/types/platform/crypto/digest.d.ts.map +1 -1
  136. package/types/platform/crypto/index.d.ts +127 -0
  137. package/types/platform/crypto/index.d.ts.map +1 -1
  138. package/types/platform/crypto/index.native.d.ts +127 -0
  139. package/types/platform/crypto/index.native.d.ts.map +1 -1
  140. package/types/platform/crypto/types.d.ts +43 -0
  141. package/types/platform/crypto/types.d.ts.map +1 -0
  142. package/types/platform/crypto/web.d.ts +5 -0
  143. package/types/platform/crypto/web.d.ts.map +1 -0
  144. package/types/platform/extras.d.ts +2 -0
  145. package/types/platform/extras.d.ts.map +1 -1
  146. package/types/platform/index.d.ts +1 -1
  147. package/types/platform/index.d.ts.map +1 -1
  148. package/types/platform/index.native.d.ts +1 -1
  149. package/types/platform/index.native.d.ts.map +1 -1
  150. package/types/platform/specs/OneCrypto.nitro.d.ts +2 -0
  151. package/types/platform/specs/OneCrypto.nitro.d.ts.map +1 -1
  152. package/types/platform/specs/OneNativeCameraNativeComponent.d.ts +18 -2
  153. package/types/platform/specs/OneNativeCameraNativeComponent.d.ts.map +1 -1
  154. package/types/router/router.d.ts.map +1 -1
@@ -13,9 +13,28 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
13
13
  }
14
14
  }
15
15
 
16
+ // one capture in flight; AVCapturePhotoOutput holds its delegate weakly.
17
+ private final class CameraPhotoDelegate: NSObject, AVCapturePhotoCaptureDelegate {
18
+ let finish: (AVCapturePhoto?, Error?) -> Void
19
+
20
+ init(finish: @escaping (AVCapturePhoto?, Error?) -> Void) {
21
+ self.finish = finish
22
+ }
23
+
24
+ func photoOutput(
25
+ _ output: AVCapturePhotoOutput,
26
+ didFinishProcessingPhoto photo: AVCapturePhoto,
27
+ error: Error?
28
+ ) {
29
+ finish(photo, error)
30
+ }
31
+ }
32
+
16
33
  @objcMembers public final class OneNativeCameraView: UIView {
17
34
  public var onStateChange: ((String) -> Void)?
18
35
  public var onCodeScanned: ((String, String) -> Void)?
36
+ // requestId, uri, width, height, base64, error ("" on success).
37
+ public var onPicture: ((Int, String, Int, Int, String, String) -> Void)?
19
38
 
20
39
  public override class var layerClass: AnyClass { AVCaptureVideoPreviewLayer.self }
21
40
 
@@ -32,6 +51,10 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
32
51
  private let sessionQueue = DispatchQueue(label: "dev.one.camera.capture")
33
52
  private var session: AVCaptureSession?
34
53
  private var metadataOutput: AVCaptureMetadataOutput?
54
+ // owned by sessionQueue.
55
+ private var photoOutput: AVCapturePhotoOutput?
56
+ private var captureDevice: AVCaptureDevice?
57
+ private var photoDelegates: [Int: CameraPhotoDelegate] = [:]
35
58
  private let metadataDelegate = CameraMetadataDelegate()
36
59
  private var sessionObservers: [NSObjectProtocol] = []
37
60
  private var rotationCoordinator: AVCaptureDevice.RotationCoordinator?
@@ -41,6 +64,9 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
41
64
  private var active = false
42
65
  private var facing = "back"
43
66
  private var codeTypes: [String] = []
67
+ private var flash = "off"
68
+ private var torch = false
69
+ private var zoom: Double = 0
44
70
  private var generation = 0
45
71
  private var lastState = ""
46
72
  private var activeObserver: NSObjectProtocol?
@@ -82,13 +108,132 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
82
108
  refresh(reconfigure: true)
83
109
  }
84
110
 
111
+ // flash, torch and zoom change the running device without restarting the session.
112
+ public func configureCapture(_ flash: String, torch: Bool, zoom: Double) {
113
+ guard self.flash != flash || self.torch != torch || self.zoom != zoom else { return }
114
+ self.flash = flash
115
+ self.torch = torch
116
+ self.zoom = zoom
117
+ sessionQueue.async { self.applyDeviceSettings(torch: torch, zoom: zoom) }
118
+ }
119
+
85
120
  public func reset() {
86
121
  active = false
87
122
  facing = "back"
88
123
  codeTypes = []
124
+ flash = "off"
125
+ torch = false
126
+ zoom = 0
89
127
  refresh()
90
128
  }
91
129
 
130
+ private func applyDeviceSettings(torch: Bool, zoom: Double) {
131
+ guard let device = captureDevice, (try? device.lockForConfiguration()) != nil else { return }
132
+ defer { device.unlockForConfiguration() }
133
+ let torchMode: AVCaptureDevice.TorchMode = torch ? .on : .off
134
+ if device.hasTorch, device.isTorchModeSupported(torchMode) { device.torchMode = torchMode }
135
+ let low = device.minAvailableVideoZoomFactor
136
+ let high = device.maxAvailableVideoZoomFactor
137
+ device.videoZoomFactor = low + CGFloat(zoom) * (high - low)
138
+ }
139
+
140
+ public func takePicture(_ requestId: Int, quality: Double, base64: Bool) {
141
+ let fail = { [weak self] (code: String, message: String) in
142
+ self?.onPicture?(requestId, "", 0, 0, "", "\(code): \(message)")
143
+ }
144
+ guard sessionReady, let coordinator = rotationCoordinator else {
145
+ let state = lastState.isEmpty ? "waiting" : lastState
146
+ fail("E_CAMERA_NOT_READY", "CameraView is not ready to take a picture (state: \(state))")
147
+ return
148
+ }
149
+ let angle = coordinator.videoRotationAngleForHorizonLevelCapture
150
+ let flashName = flash
151
+ let current = generation
152
+ sessionQueue.async { [weak self] in
153
+ guard let self, let output = self.photoOutput,
154
+ output.availablePhotoCodecTypes.contains(.jpeg)
155
+ else {
156
+ DispatchQueue.main.async { fail("E_CAMERA_NOT_READY", "CameraView has no photo output") }
157
+ return
158
+ }
159
+ let settings = AVCapturePhotoSettings(format: [
160
+ AVVideoCodecKey: AVVideoCodecType.jpeg,
161
+ AVVideoCompressionPropertiesKey: [AVVideoQualityKey: quality],
162
+ ])
163
+ let flashMode: AVCaptureDevice.FlashMode =
164
+ flashName == "on" ? .on : flashName == "auto" ? .auto : .off
165
+ if output.supportedFlashModes.contains(flashMode) { settings.flashMode = flashMode }
166
+ if let connection = output.connection(with: .video),
167
+ connection.isVideoRotationAngleSupported(angle)
168
+ {
169
+ connection.videoRotationAngle = angle
170
+ }
171
+ let delegate = CameraPhotoDelegate { [weak self] photo, error in
172
+ guard let self else { return }
173
+ // photo callbacks arrive on the output's private queue.
174
+ self.sessionQueue.async { self.photoDelegates[requestId] = nil }
175
+ let result = Self.savePicture(photo: photo, error: error, base64: base64)
176
+ DispatchQueue.main.async {
177
+ guard self.generation == current else {
178
+ fail("E_CAMERA_INACTIVE", "CameraView stopped before the picture was taken")
179
+ return
180
+ }
181
+ switch result {
182
+ case .success(let picture):
183
+ self.onPicture?(requestId, picture.uri, picture.width, picture.height, picture.base64, "")
184
+ case .failure(let failure):
185
+ fail("E_CAMERA_CAPTURE", failure.message)
186
+ }
187
+ }
188
+ }
189
+ self.photoDelegates[requestId] = delegate
190
+ output.capturePhoto(with: settings, delegate: delegate)
191
+ }
192
+ }
193
+
194
+ private struct SavedPicture {
195
+ let uri: String
196
+ let width: Int
197
+ let height: Int
198
+ let base64: String
199
+ }
200
+
201
+ private struct CaptureFailure: Error {
202
+ let message: String
203
+ }
204
+
205
+ // writes the jpeg to caches/Camera and reports its displayed size: exif
206
+ // orientations 5 to 8 are rotated a quarter turn, so their sides swap.
207
+ private static func savePicture(
208
+ photo: AVCapturePhoto?, error: Error?, base64: Bool
209
+ ) -> Result<SavedPicture, CaptureFailure> {
210
+ if let error { return .failure(CaptureFailure(message: error.localizedDescription)) }
211
+ guard let data = photo?.fileDataRepresentation(),
212
+ let source = CGImageSourceCreateWithData(data as CFData, nil),
213
+ let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any],
214
+ let pixelWidth = properties[kCGImagePropertyPixelWidth] as? Int,
215
+ let pixelHeight = properties[kCGImagePropertyPixelHeight] as? Int
216
+ else { return .failure(CaptureFailure(message: "The camera returned no image data")) }
217
+ let orientation = properties[kCGImagePropertyOrientation] as? Int ?? 1
218
+ let quarterTurn = (5...8).contains(orientation)
219
+ do {
220
+ let directory = try FileManager.default
221
+ .url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
222
+ .appendingPathComponent("Camera", isDirectory: true)
223
+ try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
224
+ let file = directory.appendingPathComponent("\(UUID().uuidString).jpg")
225
+ try data.write(to: file, options: .atomic)
226
+ return .success(SavedPicture(
227
+ uri: file.absoluteString,
228
+ width: quarterTurn ? pixelHeight : pixelWidth,
229
+ height: quarterTurn ? pixelWidth : pixelHeight,
230
+ base64: base64 ? data.base64EncodedString() : ""
231
+ ))
232
+ } catch {
233
+ return .failure(CaptureFailure(message: error.localizedDescription))
234
+ }
235
+ }
236
+
92
237
  private func emitState(_ state: String) {
93
238
  guard lastState != state else { return }
94
239
  lastState = state
@@ -115,6 +260,8 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
115
260
  sessionQueue.async {
116
261
  self.session?.stopRunning()
117
262
  self.session = nil
263
+ self.photoOutput = nil
264
+ self.captureDevice = nil
118
265
  }
119
266
  emitState("inactive")
120
267
  return
@@ -125,6 +272,8 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
125
272
  sessionQueue.async {
126
273
  self.session?.stopRunning()
127
274
  self.session = nil
275
+ self.photoOutput = nil
276
+ self.captureDevice = nil
128
277
  }
129
278
  emitState("permission-required")
130
279
  return
@@ -142,6 +291,8 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
142
291
  guard let self else { return }
143
292
  self.session?.stopRunning()
144
293
  self.session = nil
294
+ self.photoOutput = nil
295
+ self.captureDevice = nil
145
296
  guard let device = AVCaptureDevice.default(
146
297
  .builtInWideAngleCamera, for: .video, position: position
147
298
  ) else {
@@ -178,7 +329,16 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
178
329
  metadata.setMetadataObjectsDelegate(self.metadataDelegate, queue: self.sessionQueue)
179
330
  output = metadata
180
331
  }
332
+ let photo = AVCapturePhotoOutput()
333
+ guard next.canAddOutput(photo) else {
334
+ next.commitConfiguration()
335
+ self.emitState("failed", generation: current)
336
+ return
337
+ }
338
+ next.addOutput(photo)
181
339
  next.commitConfiguration()
340
+ self.photoOutput = photo
341
+ self.captureDevice = device
182
342
 
183
343
  var readyState = "ready"
184
344
  if let output {
@@ -211,8 +371,11 @@ private final class CameraMetadataDelegate: NSObject, AVCaptureMetadataOutputObj
211
371
  }
212
372
  self.observeSession(next)
213
373
  let queue = self.sessionQueue
374
+ let torch = self.torch
375
+ let zoom = self.zoom
214
376
  queue.async { [weak self] in
215
377
  next.startRunning()
378
+ self?.applyDeviceSettings(torch: torch, zoom: zoom)
216
379
  let running = next.isRunning
217
380
  DispatchQueue.main.async { [weak self] in
218
381
  guard let self, self.generation == current else { return }
@@ -17,6 +17,8 @@ namespace margelo::nitro::one {
17
17
  prototype.registerHybridMethod("fillRandomBytes", &HybridOneCryptoSpec::fillRandomBytes);
18
18
  prototype.registerHybridMethod("randomUUID", &HybridOneCryptoSpec::randomUUID);
19
19
  prototype.registerHybridMethod("digest", &HybridOneCryptoSpec::digest);
20
+ prototype.registerHybridMethod("aesEncrypt", &HybridOneCryptoSpec::aesEncrypt);
21
+ prototype.registerHybridMethod("aesDecrypt", &HybridOneCryptoSpec::aesDecrypt);
20
22
  });
21
23
  }
22
24
 
@@ -18,6 +18,7 @@
18
18
  #include <NitroModules/ArrayBuffer.hpp>
19
19
  #include <string>
20
20
  #include <NitroModules/Promise.hpp>
21
+ #include <optional>
21
22
 
22
23
  namespace margelo::nitro::one {
23
24
 
@@ -46,13 +47,15 @@ namespace margelo::nitro::one {
46
47
 
47
48
  public:
48
49
  // Properties
49
-
50
+
50
51
 
51
52
  public:
52
53
  // Methods
53
54
  virtual void fillRandomBytes(const std::shared_ptr<ArrayBuffer>& buffer, double offset, double length) = 0;
54
55
  virtual std::string randomUUID() = 0;
55
56
  virtual std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> digest(const std::string& algorithm, const std::shared_ptr<ArrayBuffer>& buffer, double offset, double length) = 0;
57
+ virtual std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> aesEncrypt(const std::shared_ptr<ArrayBuffer>& key, const std::shared_ptr<ArrayBuffer>& iv, const std::shared_ptr<ArrayBuffer>& plaintext, const std::optional<std::shared_ptr<ArrayBuffer>>& additionalData, double tagLength) = 0;
58
+ virtual std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> aesDecrypt(const std::shared_ptr<ArrayBuffer>& key, const std::shared_ptr<ArrayBuffer>& iv, const std::shared_ptr<ArrayBuffer>& ciphertext, const std::optional<std::shared_ptr<ArrayBuffer>>& additionalData, double tagLength) = 0;
56
59
 
57
60
  protected:
58
61
  // Hybrid Setup
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one",
3
- "version": "2.6.20",
3
+ "version": "2.6.22",
4
4
  "description": "One is a React framework for web, iOS, and Android apps.",
5
5
  "license": "BSD-3-Clause",
6
6
  "sideEffects": [
@@ -236,18 +236,18 @@
236
236
  "@react-navigation/native-stack": "8.0.0-alpha.58",
237
237
  "@react-navigation/routers": "8.0.0-alpha.20",
238
238
  "@ungap/structured-clone": "^1.2.0",
239
- "@vxrn/color-scheme": "2.6.20",
240
- "@vxrn/compiler": "2.6.20",
241
- "@vxrn/resolve": "2.6.20",
242
- "@vxrn/tslib-lite": "2.6.20",
243
- "@vxrn/use-isomorphic-layout-effect": "2.6.20",
244
- "@vxrn/utils": "2.6.20",
245
- "@vxrn/vite-plugin-metro": "2.6.20",
239
+ "@vxrn/color-scheme": "2.6.22",
240
+ "@vxrn/compiler": "2.6.22",
241
+ "@vxrn/resolve": "2.6.22",
242
+ "@vxrn/tslib-lite": "2.6.22",
243
+ "@vxrn/use-isomorphic-layout-effect": "2.6.22",
244
+ "@vxrn/utils": "2.6.22",
245
+ "@vxrn/vite-plugin-metro": "2.6.22",
246
246
  "babel-dead-code-elimination": "1.0.10",
247
247
  "babel-plugin-module-resolver": "^5.0.2",
248
248
  "citty": "^0.1.6",
249
249
  "core-js": "^3.38.1",
250
- "create-vxrn": "2.6.20",
250
+ "create-vxrn": "2.6.22",
251
251
  "escape-string-regexp": "^5.0.0",
252
252
  "fast-deep-equal": "^3.1.3",
253
253
  "fast-glob": "^3.3.3",
@@ -269,7 +269,7 @@
269
269
  "tsconfig-paths": "^4",
270
270
  "use-latest-callback": "^0.2.3",
271
271
  "vite": "^8.2.2",
272
- "vxrn": "2.6.20",
272
+ "vxrn": "2.6.22",
273
273
  "web-streams-polyfill": "^4.3.0",
274
274
  "ws": "^8.21.0",
275
275
  "xxhashjs": "^0.2.2"
@@ -337,7 +337,7 @@
337
337
  "@types/react-dom": "^19.2.2",
338
338
  "@types/react-test-renderer": "^19.1.0",
339
339
  "@types/xxhashjs": "^0.2.4",
340
- "@vxrn/emitter": "2.6.20",
340
+ "@vxrn/emitter": "2.6.22",
341
341
  "depcheck": "^1.4.7",
342
342
  "esbuild": "^0.25.12",
343
343
  "immer": "^10.1.1",
@@ -513,7 +513,7 @@
513
513
  }
514
514
  }
515
515
  },
516
- "releaseSourceCommit": "1b992f990d752550fb6fc0894878f5e86f612d06",
516
+ "releaseSourceCommit": "04df94112b1398e221a0c7771f1d4357e6f70ef2",
517
517
  "repository": {
518
518
  "type": "git",
519
519
  "url": "git+https://github.com/onejs/one.git",
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { createApp } from './createApp'
2
2
 
3
3
  export { One } from './one'
4
+ export { AESEncryptionKey, AESSealedData } from './platform'
4
5
  // native hooks and adaptive reads are named exports for react-hooks lint.
5
6
  export {
6
7
  useFonts,
@@ -64,6 +65,9 @@ export type {
64
65
  ComposeSnackbarShowOptions,
65
66
  ComposeSnackbarResult,
66
67
  ComposeModalBottomSheetRef,
68
+ CameraCapturedPicture,
69
+ CameraPictureOptions,
70
+ CameraViewRef,
67
71
  } from './platform'
68
72
  export type { OneRouter } from './interfaces/router'
69
73
 
package/src/one.ts CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  Purchases,
28
28
  ImageManipulator,
29
29
  Device,
30
+ Crypto as NativeCrypto,
30
31
  Localization,
31
32
  Motion,
32
33
  BackgroundTasks,
@@ -157,6 +158,7 @@ export type OneAPI = {
157
158
  readonly Purchases: typeof Purchases
158
159
  readonly ImageManipulator: typeof ImageManipulator
159
160
  readonly Device: typeof Device
161
+ readonly Crypto: typeof NativeCrypto
160
162
  readonly Localization: typeof Localization
161
163
  readonly Motion: typeof Motion
162
164
  readonly BackgroundTasks: typeof BackgroundTasks
@@ -268,6 +270,7 @@ export const One: OneAPI = Object.freeze({
268
270
  Purchases,
269
271
  ImageManipulator,
270
272
  Device,
273
+ Crypto: NativeCrypto,
271
274
  Localization,
272
275
  Motion,
273
276
  BackgroundTasks,
@@ -1,8 +1,43 @@
1
+ import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'
1
2
  import { Platform } from 'react-native'
2
- import NativeCamera from '../specs/OneNativeCameraNativeComponent'
3
- import type { CameraCodeType, CameraState, CameraViewProps } from './types'
3
+ import NativeCamera, { Commands } from '../specs/OneNativeCameraNativeComponent'
4
+ import type {
5
+ CameraCapturedPicture,
6
+ CameraCodeType,
7
+ CameraState,
8
+ CameraViewProps,
9
+ CameraViewRef,
10
+ } from './types'
4
11
 
5
- export type { CameraCode, CameraCodeType, CameraFacing, CameraState, CameraViewProps } from './types'
12
+ export type {
13
+ CameraCapturedPicture,
14
+ CameraCode,
15
+ CameraCodeType,
16
+ CameraFacing,
17
+ CameraFlashMode,
18
+ CameraPictureOptions,
19
+ CameraState,
20
+ CameraViewProps,
21
+ CameraViewRef,
22
+ } from './types'
23
+
24
+ type PendingPicture = {
25
+ resolve: (picture: CameraCapturedPicture) => void
26
+ reject: (error: Error) => void
27
+ base64: boolean
28
+ }
29
+
30
+ let nextRequestId = 1
31
+
32
+ function codedError(code: string, message: string) {
33
+ return Object.assign(new Error(message), { code })
34
+ }
35
+
36
+ // native failures arrive as "<code>: <message>", the same form nitro rejects with.
37
+ function nativeError(value: string) {
38
+ const coded = /^((?:E|ERR)_[A-Z0-9_]+): ([\s\S]*)$/.exec(value)
39
+ return coded ? codedError(coded[1], coded[2]) : new Error(value)
40
+ }
6
41
 
7
42
  function cameraState(value: string): CameraState {
8
43
  switch (value) {
@@ -41,14 +76,20 @@ function codeType(value: string): CameraCodeType {
41
76
  return value
42
77
  }
43
78
 
44
- export function CameraView({
45
- active = true,
46
- facing = 'back',
47
- codeTypes = ['qr'],
48
- onStateChange,
49
- onCodeScanned,
50
- ...viewProps
51
- }: CameraViewProps) {
79
+ export const CameraView = forwardRef<CameraViewRef, CameraViewProps>(function CameraView(
80
+ {
81
+ active = true,
82
+ facing = 'back',
83
+ codeTypes = ['qr'],
84
+ flash = 'off',
85
+ enableTorch = false,
86
+ zoom = 0,
87
+ onStateChange,
88
+ onCodeScanned,
89
+ ...viewProps
90
+ },
91
+ ref
92
+ ) {
52
93
  if (Platform.OS !== 'ios' && Platform.OS !== 'android')
53
94
  throw new Error('CameraView requires a native build')
54
95
  if (typeof active !== 'boolean') throw new TypeError('CameraView.active must be a boolean')
@@ -58,16 +99,79 @@ export function CameraView({
58
99
  if (!Array.isArray(codeTypes) || codeTypes.some((type) => !isCodeType(type))) {
59
100
  throw new TypeError('CameraView.codeTypes contains an unsupported code type')
60
101
  }
102
+ if (flash !== 'off' && flash !== 'on' && flash !== 'auto') {
103
+ throw new TypeError('CameraView.flash must be off, on or auto')
104
+ }
105
+ if (typeof enableTorch !== 'boolean') {
106
+ throw new TypeError('CameraView.enableTorch must be a boolean')
107
+ }
108
+ if (typeof zoom !== 'number' || !(zoom >= 0 && zoom <= 1)) {
109
+ throw new RangeError('CameraView.zoom must be a number from 0 to 1')
110
+ }
111
+ const nativeRef = useRef<React.ElementRef<typeof NativeCamera>>(null)
112
+ const pending = useRef(new Map<number, PendingPicture>())
113
+
114
+ useEffect(() => {
115
+ const requests = pending.current
116
+ return () => {
117
+ for (const request of requests.values())
118
+ request.reject(codedError('E_CAMERA_UNMOUNTED', 'CameraView unmounted before the picture was taken'))
119
+ requests.clear()
120
+ }
121
+ }, [])
122
+
123
+ useImperativeHandle(
124
+ ref,
125
+ () => ({
126
+ takePicture(options = {}) {
127
+ const { quality = 1, base64 = false } = options
128
+ if (typeof quality !== 'number' || !(quality >= 0 && quality <= 1))
129
+ return Promise.reject(new RangeError('takePicture quality must be a number from 0 to 1'))
130
+ if (typeof base64 !== 'boolean')
131
+ return Promise.reject(new TypeError('takePicture base64 must be a boolean'))
132
+ const view = nativeRef.current
133
+ if (!view)
134
+ return Promise.reject(codedError('E_CAMERA_UNMOUNTED', 'CameraView is not mounted'))
135
+ const requestId = nextRequestId++
136
+ return new Promise<CameraCapturedPicture>((resolve, reject) => {
137
+ pending.current.set(requestId, { resolve, reject, base64 })
138
+ Commands.takePicture(view, requestId, quality, base64)
139
+ })
140
+ },
141
+ }),
142
+ []
143
+ )
144
+
61
145
  return (
62
146
  <NativeCamera
63
147
  {...viewProps}
148
+ ref={nativeRef}
64
149
  active={active}
65
150
  facing={facing}
66
151
  codeTypes={[...new Set(codeTypes)]}
152
+ flash={flash}
153
+ enableTorch={enableTorch}
154
+ zoom={zoom}
67
155
  onNativeCameraState={({ nativeEvent }) => onStateChange?.(cameraState(nativeEvent.state))}
68
156
  onNativeCameraCode={({ nativeEvent }) =>
69
157
  onCodeScanned?.({ type: codeType(nativeEvent.type), data: nativeEvent.data })
70
158
  }
159
+ onNativeCameraPicture={({ nativeEvent }) => {
160
+ const request = pending.current.get(nativeEvent.requestId)
161
+ if (!request) return
162
+ pending.current.delete(nativeEvent.requestId)
163
+ if (nativeEvent.error) {
164
+ request.reject(nativeError(nativeEvent.error))
165
+ return
166
+ }
167
+ request.resolve({
168
+ uri: nativeEvent.uri,
169
+ width: nativeEvent.width,
170
+ height: nativeEvent.height,
171
+ format: 'jpg',
172
+ ...(request.base64 ? { base64: nativeEvent.base64 } : {}),
173
+ })
174
+ }}
71
175
  />
72
176
  )
73
- }
177
+ })
@@ -1,7 +1,18 @@
1
- import type { CameraViewProps } from './types'
1
+ import { forwardRef } from 'react'
2
+ import type { CameraViewProps, CameraViewRef } from './types'
2
3
 
3
- export type { CameraCode, CameraCodeType, CameraFacing, CameraState, CameraViewProps } from './types'
4
+ export type {
5
+ CameraCapturedPicture,
6
+ CameraCode,
7
+ CameraCodeType,
8
+ CameraFacing,
9
+ CameraFlashMode,
10
+ CameraPictureOptions,
11
+ CameraState,
12
+ CameraViewProps,
13
+ CameraViewRef,
14
+ } from './types'
4
15
 
5
- export function CameraView(_props: CameraViewProps): never {
16
+ export const CameraView = forwardRef<CameraViewRef, CameraViewProps>(function CameraView(): never {
6
17
  throw new Error('CameraView requires a native build')
7
- }
18
+ })
@@ -27,10 +27,32 @@ export interface CameraCode {
27
27
  data: string
28
28
  }
29
29
 
30
+ export type CameraFlashMode = 'off' | 'on' | 'auto'
31
+
30
32
  export interface CameraViewProps extends ViewProps {
31
33
  active?: boolean
32
34
  facing?: CameraFacing
33
35
  codeTypes?: readonly CameraCodeType[]
36
+ flash?: CameraFlashMode
37
+ enableTorch?: boolean
38
+ zoom?: number
34
39
  onStateChange?: (state: CameraState) => void
35
40
  onCodeScanned?: (code: CameraCode) => void
36
41
  }
42
+
43
+ export interface CameraPictureOptions {
44
+ quality?: number
45
+ base64?: boolean
46
+ }
47
+
48
+ export interface CameraCapturedPicture {
49
+ uri: string
50
+ width: number
51
+ height: number
52
+ format: 'jpg'
53
+ base64?: string
54
+ }
55
+
56
+ export interface CameraViewRef {
57
+ takePicture(options?: CameraPictureOptions): Promise<CameraCapturedPicture>
58
+ }