@mentra/bluetooth-sdk 0.1.8 → 0.1.10

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 (147) hide show
  1. package/README.md +116 -18
  2. package/android/build.gradle +289 -16
  3. package/android/gradle.properties.example +9 -0
  4. package/android/lc3Lib/build.gradle +95 -6
  5. package/android/settings.gradle +3 -0
  6. package/android/silero/LICENSE.md +17 -0
  7. package/android/silero/build.gradle +42 -0
  8. package/android/silero/proguard-rules.pro +24 -0
  9. package/android/silero/src/androidTest/assets/hello.wav +0 -0
  10. package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
  11. package/android/silero/src/main/assets/LICENSE +21 -0
  12. package/android/silero/src/main/assets/silero_vad.onnx +0 -0
  13. package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
  14. package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
  15. package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
  16. package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
  17. package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
  18. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
  19. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
  20. package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
  21. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +6 -0
  22. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +165 -27
  23. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +89 -21
  24. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +189 -16
  25. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +22 -2
  26. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +787 -85
  27. package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +6 -2
  28. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +123 -10
  29. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +12 -2
  30. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +8 -2
  31. package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +33 -17
  32. package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +101 -0
  33. package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +8 -0
  34. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
  35. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +399 -74
  36. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +100 -17
  37. package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +17 -0
  38. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +2 -2
  39. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +569 -220
  40. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +2 -2
  41. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +271 -66
  42. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +176 -104
  43. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraosBle.java +12753 -27036
  44. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +54 -2
  45. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +16 -4
  46. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +60 -3
  47. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +12 -3
  48. package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +87 -36
  49. package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +13 -4
  50. package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
  51. package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +180 -0
  52. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AvifExifStripper.java +573 -0
  53. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +488 -50
  54. package/android/src/main/java/com/mentra/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
  55. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +18 -1
  56. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +64 -18
  57. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +48 -39
  58. package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +23 -0
  59. package/android/src/test/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
  60. package/android/src/test/java/com/mentra/bluetoothsdk/utils/AvifExifStripperTest.java +30 -0
  61. package/android/src/test/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +98 -0
  62. package/android/src/test/resources/avif_with_exif.avif +0 -0
  63. package/build/BluetoothSdk.types.d.ts +241 -43
  64. package/build/BluetoothSdk.types.d.ts.map +1 -1
  65. package/build/BluetoothSdk.types.js +11 -10
  66. package/build/BluetoothSdk.types.js.map +1 -1
  67. package/build/_private/BluetoothSdkModule.d.ts +36 -23
  68. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  69. package/build/_private/BluetoothSdkModule.js +30 -52
  70. package/build/_private/BluetoothSdkModule.js.map +1 -1
  71. package/build/_private/photoRequestPayload.d.ts +4 -0
  72. package/build/_private/photoRequestPayload.d.ts.map +1 -0
  73. package/build/_private/photoRequestPayload.js +25 -0
  74. package/build/_private/photoRequestPayload.js.map +1 -0
  75. package/build/index.d.ts +1 -1
  76. package/build/index.d.ts.map +1 -1
  77. package/build/index.js +23 -0
  78. package/build/index.js.map +1 -1
  79. package/build/react/useMentraBluetooth.d.ts +2 -2
  80. package/build/react/useMentraBluetooth.d.ts.map +1 -1
  81. package/build/react/useMentraBluetooth.js +3 -3
  82. package/build/react/useMentraBluetooth.js.map +1 -1
  83. package/ios/BluetoothSdkModule.swift +174 -70
  84. package/ios/LocalPhotoUploadServer.swift +370 -105
  85. package/ios/MentraBluetoothSDK.podspec +3 -1
  86. package/ios/MentraPhotoReceiverModule.swift +62 -10
  87. package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
  88. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +668 -26
  89. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +13 -9
  90. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4551 -0
  91. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +1745 -0
  92. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  93. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4551 -0
  94. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +1745 -0
  95. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  96. package/ios/Source/BluetoothSdkDefaults.swift +6 -0
  97. package/ios/Source/Bridge.swift +58 -19
  98. package/ios/Source/DeviceManager.swift +149 -17
  99. package/ios/Source/DeviceStore.swift +16 -6
  100. package/ios/Source/MentraBluetoothSDK.swift +803 -47
  101. package/ios/Source/ObservableStore.swift +8 -2
  102. package/ios/Source/{Audio → audio}/AudioModels.swift +9 -7
  103. package/ios/Source/{Camera → camera}/CameraModels.swift +225 -16
  104. package/ios/Source/controllers/ControllerManager.swift +4 -3
  105. package/ios/Source/controllers/R1.swift +3 -3
  106. package/ios/Source/{Events → events}/BluetoothEvents.swift +183 -1
  107. package/ios/Source/{Internal → internal}/ValueParsing.swift +10 -1
  108. package/ios/Source/{Requests → requests}/DisplayRequests.swift +28 -4
  109. package/ios/Source/services/PhoneMic.swift +1 -0
  110. package/ios/Source/sgcs/G1.swift +2 -2
  111. package/ios/Source/sgcs/G2.swift +645 -403
  112. package/ios/Source/sgcs/Mach1.swift +2 -2
  113. package/ios/Source/sgcs/MentraLive.swift +812 -319
  114. package/ios/Source/sgcs/MentraNex.swift +412 -182
  115. package/ios/Source/sgcs/SGCManager.swift +57 -3
  116. package/ios/Source/sgcs/Simulated.swift +7 -3
  117. package/ios/Source/sgcs/mentraos_ble.pb.swift +3073 -3134
  118. package/ios/Source/status/DeviceStatus.swift +974 -0
  119. package/ios/Source/{Status → status}/WifiHotspotStatus.swift +4 -4
  120. package/ios/Source/{Streaming → streaming}/StreamModels.swift +24 -9
  121. package/ios/Source/stt/STTTools.swift +27 -12
  122. package/ios/Source/stt/SherpaOnnxTranscriber.swift +31 -8
  123. package/ios/Source/tts/TTSTools.swift +164 -0
  124. package/ios/Source/utils/JSCExperiment.swift +7 -7
  125. package/ios/Source/utils/MessageChunkReassembler.swift +20 -1
  126. package/ios/Source/utils/MessageChunker.swift +78 -21
  127. package/ios/Source/utils/TarBz2Extractor.swift +53 -2
  128. package/package.json +1 -1
  129. package/plugin/build/withAndroid.js +68 -4
  130. package/src/BluetoothSdk.types.ts +331 -59
  131. package/src/_private/BluetoothSdkModule.ts +113 -102
  132. package/src/_private/photoRequestPayload.ts +28 -0
  133. package/src/index.ts +58 -15
  134. package/src/react/useMentraBluetooth.ts +6 -4
  135. package/android/.project +0 -28
  136. package/android/lc3Lib/.project +0 -28
  137. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/cargs.h +0 -162
  138. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/c-api.h +0 -1852
  139. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/cxx-api.h +0 -674
  140. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/sherpa-onnx.a +0 -0
  141. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/sherpa-onnx.a +0 -0
  142. package/ios/Source/Status/DeviceStatus.swift +0 -471
  143. /package/ios/Source/{Connection → connection}/ScanSession.swift +0 -0
  144. /package/ios/Source/{Errors → errors}/BluetoothError.swift +0 -0
  145. /package/ios/Source/{Internal → internal}/BluetoothAvailability.swift +0 -0
  146. /package/ios/Source/{Status → status}/RuntimeState.swift +0 -0
  147. /package/ios/Source/{Types → types}/DeviceModels.swift +0 -0
@@ -6,6 +6,7 @@ import SWCompression
6
6
  @objc(TarBz2Extractor)
7
7
  class TarBz2Extractor: NSObject {
8
8
  private static let chunkSize = 1 << 16 // 64 KB
9
+ private static let progressEmitIntervalMs: Int64 = 200
9
10
 
10
11
  @objc
11
12
  static func extractTarBz2From(
@@ -42,14 +43,19 @@ class TarBz2Extractor: NSObject {
42
43
  attributes: nil
43
44
  )
44
45
 
45
- let tempTarURL = try decompressBzipArchive(at: sourcePath)
46
+ let sourceSize = fileSize(atPath: sourcePath)
47
+ Bridge.sendExtractionProgress(percentage: 0, bytesRead: 0, totalBytes: sourceSize)
48
+
49
+ let tempTarURL = try decompressBzipArchive(at: sourcePath, sourceSize: sourceSize)
46
50
  defer { try? fileManager.removeItem(at: tempTarURL) }
47
51
 
48
52
  try extractTarArchive(at: tempTarURL, to: destinationPath)
49
53
  try flattenNestedDirectory(at: destinationPath)
54
+
55
+ Bridge.sendExtractionProgress(percentage: 100, bytesRead: sourceSize, totalBytes: sourceSize)
50
56
  }
51
57
 
52
- private static func decompressBzipArchive(at sourcePath: String) throws -> URL {
58
+ private static func decompressBzipArchive(at sourcePath: String, sourceSize: Int64) throws -> URL {
53
59
  let tempTarURL = FileManager.default.temporaryDirectory
54
60
  .appendingPathComponent(UUID().uuidString)
55
61
  .appendingPathExtension("tar")
@@ -70,6 +76,7 @@ class TarBz2Extractor: NSObject {
70
76
  }
71
77
 
72
78
  var buffer = [Int8](repeating: 0, count: chunkSize)
79
+ var lastEmittedAt: Int64 = 0
73
80
  while true {
74
81
  let bytesRead = BZ2_bzRead(&bzError, bzFile, &buffer, Int32(buffer.count))
75
82
  if bzError == BZ_OK || bzError == BZ_STREAM_END {
@@ -83,6 +90,20 @@ class TarBz2Extractor: NSObject {
83
90
  throw makeError(code: 1004, message: "Failed to write decompressed data")
84
91
  }
85
92
  }
93
+
94
+ let now = nowMs()
95
+ if now - lastEmittedAt >= progressEmitIntervalMs && sourceSize > 0 {
96
+ let consumed = Int64(ftell(sourceFile))
97
+ // Bzip phase claims 0–80% of total UI progress.
98
+ let pct = max(0, min(80, Int((consumed * 80) / sourceSize)))
99
+ Bridge.sendExtractionProgress(
100
+ percentage: pct,
101
+ bytesRead: consumed,
102
+ totalBytes: sourceSize
103
+ )
104
+ lastEmittedAt = now
105
+ }
106
+
86
107
  if bzError == BZ_STREAM_END {
87
108
  break
88
109
  }
@@ -97,6 +118,8 @@ class TarBz2Extractor: NSObject {
97
118
  throw makeError(code: 1006, message: "bzip2 close failed with code \(bzError)")
98
119
  }
99
120
 
121
+ Bridge.sendExtractionProgress(percentage: 80, bytesRead: sourceSize, totalBytes: sourceSize)
122
+
100
123
  return tempTarURL
101
124
  }
102
125
 
@@ -104,10 +127,12 @@ class TarBz2Extractor: NSObject {
104
127
  let destinationRoot = URL(fileURLWithPath: destinationPath, isDirectory: true)
105
128
  let fileManager = FileManager.default
106
129
 
130
+ let tarSize = fileSize(at: tarURL)
107
131
  let handle = try FileHandle(forReadingFrom: tarURL)
108
132
  defer { try? handle.close() }
109
133
 
110
134
  var reader = TarReader(fileHandle: handle)
135
+ var lastEmittedAt: Int64 = 0
111
136
 
112
137
  while let entry = try reader.read() {
113
138
  let sanitizedName = sanitize(entryName: entry.info.name)
@@ -135,9 +160,35 @@ class TarBz2Extractor: NSObject {
135
160
  default:
136
161
  continue
137
162
  }
163
+
164
+ let now = nowMs()
165
+ if now - lastEmittedAt >= progressEmitIntervalMs && tarSize > 0 {
166
+ let consumed = Int64(handle.offsetInFile)
167
+ // Tar phase claims 80–99% of total UI progress.
168
+ let pct = 80 + max(0, min(19, Int((consumed * 19) / tarSize)))
169
+ Bridge.sendExtractionProgress(
170
+ percentage: pct,
171
+ bytesRead: consumed,
172
+ totalBytes: tarSize
173
+ )
174
+ lastEmittedAt = now
175
+ }
138
176
  }
139
177
  }
140
178
 
179
+ private static func fileSize(atPath path: String) -> Int64 {
180
+ let attrs = try? FileManager.default.attributesOfItem(atPath: path)
181
+ return (attrs?[.size] as? NSNumber)?.int64Value ?? 0
182
+ }
183
+
184
+ private static func fileSize(at url: URL) -> Int64 {
185
+ return fileSize(atPath: url.path)
186
+ }
187
+
188
+ private static func nowMs() -> Int64 {
189
+ return Int64(Date().timeIntervalSince1970 * 1000)
190
+ }
191
+
141
192
  private static func flattenNestedDirectory(at destinationPath: String) throws {
142
193
  let fileManager = FileManager.default
143
194
  let destinationURL = URL(fileURLWithPath: destinationPath, isDirectory: true)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/bluetooth-sdk",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "SDK for communicating with smart glasses",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -14,17 +14,42 @@ function toGroovyString(value) {
14
14
  return JSON.stringify(value);
15
15
  }
16
16
  /**
17
- * Modify settings.gradle to include lc3Lib module
17
+ * Modify settings.gradle to include lc3Lib and silero modules
18
18
  */
19
19
  function withSettingsGradleModifications(config) {
20
20
  return (0, config_plugins_1.withSettingsGradle)(config, (config) => {
21
21
  let settingsGradle = config.modResults.contents;
22
- // Add lc3Lib module if not present
22
+ const bluetoothSdkRoot = getBluetoothSdkRoot();
23
+ const bluetoothSdkRootExpression = `System.getenv("MENTRA_BLUETOOTH_SDK_PACKAGE_PATH") ?: ${toGroovyString(bluetoothSdkRoot)}`;
24
+ if (!settingsGradle.includes("project(':mentra-bluetooth-sdk').projectDir")) {
25
+ const bluetoothSdkProjectBlock = `
26
+ if (findProject(':mentra-bluetooth-sdk') != null) {
27
+ project(':mentra-bluetooth-sdk').projectDir = new File(${bluetoothSdkRootExpression}, 'android')
28
+ }
29
+ `;
30
+ const lc3Include = " include ':lc3Lib'";
31
+ settingsGradle = settingsGradle.includes(lc3Include)
32
+ ? settingsGradle.replace(lc3Include, `${bluetoothSdkProjectBlock}\n${lc3Include}`)
33
+ : `${settingsGradle}${bluetoothSdkProjectBlock}`;
34
+ }
23
35
  if (!settingsGradle.includes("include ':lc3Lib'")) {
24
- const bluetoothSdkRoot = getBluetoothSdkRoot();
25
36
  settingsGradle += `
26
37
  include ':lc3Lib'
27
- project(':lc3Lib').projectDir = new File(${toGroovyString(bluetoothSdkRoot)}, 'android/lc3Lib')
38
+ `;
39
+ }
40
+ if (!settingsGradle.includes("project(':lc3Lib').projectDir")) {
41
+ settingsGradle += `
42
+ project(':lc3Lib').projectDir = new File(${bluetoothSdkRootExpression}, 'android/lc3Lib')
43
+ `;
44
+ }
45
+ if (!settingsGradle.includes("include ':silero'")) {
46
+ settingsGradle += `
47
+ include ':silero'
48
+ `;
49
+ }
50
+ if (!settingsGradle.includes("project(':silero').projectDir")) {
51
+ settingsGradle += `
52
+ project(':silero').projectDir = new File(${bluetoothSdkRootExpression}, 'android/silero')
28
53
  `;
29
54
  }
30
55
  config.modResults.contents = settingsGradle;
@@ -70,8 +95,47 @@ function withGradlePropertiesModifications(config) {
70
95
  return config;
71
96
  });
72
97
  }
98
+ /**
99
+ * Inject a local Maven repository into the root project's allprojects block so
100
+ * that `:app` (and any other module that transitively pulls our deps) can
101
+ * resolve `com.k2fsa.sherpa.onnx:sherpa-onnx`. The AAR is downloaded at
102
+ * configure-time by bluetooth-sdk's own build.gradle into
103
+ * `android/libs/maven/`; we just need that directory exposed as a maven repo
104
+ * to consumers as well. AGP rejects raw local-.aar deps from library modules,
105
+ * so the Maven layout is the supported workaround.
106
+ */
107
+ function withSherpaOnnxLocalMavenRepo(config) {
108
+ return (0, config_plugins_1.withProjectBuildGradle)(config, (config) => {
109
+ if (config.modResults.language !== "groovy") {
110
+ return config;
111
+ }
112
+ let contents = config.modResults.contents;
113
+ const fallbackRoot = toGroovyString(getBluetoothSdkRoot());
114
+ const repoDirExpression = `new File(System.getenv("MENTRA_BLUETOOTH_SDK_PACKAGE_PATH") ?: ${fallbackRoot}, "android/libs/maven")`;
115
+ const marker = "// bluetooth-sdk: sherpa-onnx local maven repo";
116
+ if (contents.includes(marker)) {
117
+ return config;
118
+ }
119
+ const repoBlock = ` maven {\n ${marker}\n url = uri(${repoDirExpression})\n }`;
120
+ const allprojectsMatch = contents.match(/allprojects\s*\{[\s\S]*?repositories\s*\{/);
121
+ if (allprojectsMatch) {
122
+ const insertIdx = allprojectsMatch.index + allprojectsMatch[0].length;
123
+ contents = contents.slice(0, insertIdx) + "\n" + repoBlock + contents.slice(insertIdx);
124
+ }
125
+ else {
126
+ // Fallback: append an allprojects block. Older Expo templates that don't
127
+ // emit allprojects {} put repositories in settings.gradle instead — but
128
+ // this codebase's prebuild has historically emitted allprojects, so this
129
+ // branch is just a safety net.
130
+ contents += `\nallprojects {\n repositories {\n${repoBlock}\n }\n}\n`;
131
+ }
132
+ config.modResults.contents = contents;
133
+ return config;
134
+ });
135
+ }
73
136
  const withAndroidConfiguration = (config, props) => {
74
137
  config = withSettingsGradleModifications(config);
138
+ config = withSherpaOnnxLocalMavenRepo(config);
75
139
  if (props?.node) {
76
140
  config = withGradlePropertiesModifications(config);
77
141
  }