@velocitycareerlabs/vcl-react-native 2.9.0 → 2.9.1
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/VclReactNative.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/io/velocitycareerlabs/reactnative/VclReactNativeModule.kt +58 -74
- package/android/src/main/java/io/velocitycareerlabs/reactnative/extensions/VCLErrorExtensions.kt +62 -2
- package/ios/VCLErrorBridge.swift +89 -0
- package/ios/VclReactNativeImpl.swift +29 -23
- package/lib/module/api/VCLApi.js +44 -18
- package/lib/module/api/VCLApi.js.map +1 -1
- package/lib/module/api/entities/error/VCLError.js +9 -13
- package/lib/module/api/entities/error/VCLError.js.map +1 -1
- package/lib/module/api/entities/error/VCLErrorDeserializer.js +98 -0
- package/lib/module/api/entities/error/VCLErrorDeserializer.js.map +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/api/VCLApi.d.ts.map +1 -1
- package/lib/typescript/src/api/entities/error/VCLError.d.ts +22 -1
- package/lib/typescript/src/api/entities/error/VCLError.d.ts.map +1 -1
- package/lib/typescript/src/api/entities/error/VCLErrorDeserializer.d.ts +19 -0
- package/lib/typescript/src/api/entities/error/VCLErrorDeserializer.d.ts.map +1 -0
- package/lib/typescript/src/api/entities/initialization/VCLInitializationDescriptor.d.ts +6 -0
- package/lib/typescript/src/api/entities/initialization/VCLInitializationDescriptor.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api/VCLApi.ts +58 -18
- package/src/api/entities/error/VCLError.ts +33 -13
- package/src/api/entities/error/VCLErrorDeserializer.ts +151 -0
- package/src/api/entities/initialization/VCLInitializationDescriptor.ts +6 -0
- package/src/index.tsx +8 -0
package/VclReactNative.podspec
CHANGED
package/android/build.gradle
CHANGED
|
@@ -76,7 +76,7 @@ dependencies {
|
|
|
76
76
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
77
77
|
|
|
78
78
|
//--------- Must: be added:
|
|
79
|
-
implementation "io.velocitycareerlabs:vcl:2.9.
|
|
79
|
+
implementation "io.velocitycareerlabs:vcl:2.9.2"
|
|
80
80
|
implementation 'com.nimbusds:nimbus-jose-jwt:10.5'
|
|
81
81
|
implementation "androidx.security:security-crypto:1.1.0"
|
|
82
82
|
//-------------------------
|
|
@@ -38,7 +38,9 @@ import io.velocitycareerlabs.reactnative.utlis.Converter.verifiedProfileToMap
|
|
|
38
38
|
import io.velocitycareerlabs.api.VCLProvider
|
|
39
39
|
import io.velocitycareerlabs.api.entities.error.VCLError
|
|
40
40
|
import io.velocitycareerlabs.api.entities.initialization.VCLInitializationDescriptor
|
|
41
|
-
import io.velocitycareerlabs.reactnative.extensions.
|
|
41
|
+
import io.velocitycareerlabs.reactnative.extensions.VCLErrorBridgeConfig
|
|
42
|
+
import io.velocitycareerlabs.reactnative.extensions.getIntOpt
|
|
43
|
+
import io.velocitycareerlabs.reactnative.extensions.rejectBridgedVCLError
|
|
42
44
|
import io.velocitycareerlabs.reactnative.utlis.Converter.authTokenToMap
|
|
43
45
|
import io.velocitycareerlabs.reactnative.utlis.Converter.didJwkToMap
|
|
44
46
|
import io.velocitycareerlabs.reactnative.utlis.Converter.mapToAuthToken
|
|
@@ -72,11 +74,25 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
72
74
|
GlobalConfig.IsDebugOn = initializationDescriptor.isDebugOn
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
private inline fun withBridgedLocalExceptions(
|
|
78
|
+
promise: Promise,
|
|
79
|
+
block: () -> Unit
|
|
80
|
+
) {
|
|
81
|
+
try {
|
|
82
|
+
block()
|
|
83
|
+
} catch (ex: Exception) {
|
|
84
|
+
promise.rejectBridgedVCLError(VCLError(ex))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
override fun initialize(
|
|
76
89
|
initializationDescriptorMap: ReadableMap,
|
|
77
90
|
promise: Promise
|
|
78
91
|
) {
|
|
79
|
-
|
|
92
|
+
withBridgedLocalExceptions(promise) {
|
|
93
|
+
VCLErrorBridgeConfig.updateNativeErrorStackFrameLimit(
|
|
94
|
+
initializationDescriptorMap.getIntOpt("nativeErrorStackFrameLimit")
|
|
95
|
+
)
|
|
80
96
|
val initializationDescriptor = mapToInitializationDescriptor(
|
|
81
97
|
initializationDescriptorMap
|
|
82
98
|
)
|
|
@@ -88,37 +104,29 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
88
104
|
promise.resolve("VCL initialization succeed!")
|
|
89
105
|
},
|
|
90
106
|
errorHandler = {
|
|
91
|
-
promise.
|
|
107
|
+
promise.rejectBridgedVCLError(it)
|
|
92
108
|
})
|
|
93
|
-
} catch (ex: Exception) {
|
|
94
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
95
109
|
}
|
|
96
110
|
}
|
|
97
111
|
|
|
98
112
|
override fun getCountries(promise: Promise) {
|
|
99
|
-
|
|
113
|
+
withBridgedLocalExceptions(promise) {
|
|
100
114
|
vcl.countries?.let { promise.resolve(countriesToMap(it)) }
|
|
101
|
-
?: promise.
|
|
102
|
-
} catch (ex: Exception) {
|
|
103
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
115
|
+
?: promise.rejectBridgedVCLError(VCLError(message = "Countries not found"))
|
|
104
116
|
}
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
override fun getCredentialTypeSchemas(promise: Promise) {
|
|
108
|
-
|
|
120
|
+
withBridgedLocalExceptions(promise) {
|
|
109
121
|
vcl.credentialTypeSchemas?.let { promise.resolve(credentialTypeSchemasToMap(it)) }
|
|
110
|
-
?: promise.
|
|
111
|
-
} catch (ex: Exception) {
|
|
112
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
122
|
+
?: promise.rejectBridgedVCLError(VCLError(message = "Credential Type Schemas not found"))
|
|
113
123
|
}
|
|
114
124
|
}
|
|
115
125
|
|
|
116
126
|
override fun getCredentialTypes(promise: Promise) {
|
|
117
|
-
|
|
127
|
+
withBridgedLocalExceptions(promise) {
|
|
118
128
|
vcl.credentialTypes?.let { promise.resolve(credentialTypesToMap(it)) }
|
|
119
|
-
?: promise.
|
|
120
|
-
} catch (ex: Exception) {
|
|
121
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
129
|
+
?: promise.rejectBridgedVCLError(VCLError(message = "Credential Types not found"))
|
|
122
130
|
}
|
|
123
131
|
}
|
|
124
132
|
|
|
@@ -126,7 +134,7 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
126
134
|
presentationRequestDescriptorMap: ReadableMap,
|
|
127
135
|
promise: Promise
|
|
128
136
|
) {
|
|
129
|
-
|
|
137
|
+
withBridgedLocalExceptions(promise) {
|
|
130
138
|
vcl.getPresentationRequest(
|
|
131
139
|
presentationRequestDescriptor = mapTopPresentationRequestDescriptor(
|
|
132
140
|
presentationRequestDescriptorMap
|
|
@@ -135,10 +143,8 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
135
143
|
promise.resolve(presentationRequestToMap(it))
|
|
136
144
|
},
|
|
137
145
|
errorHandler = {
|
|
138
|
-
promise.
|
|
146
|
+
promise.rejectBridgedVCLError(it)
|
|
139
147
|
})
|
|
140
|
-
} catch (ex: Exception) {
|
|
141
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
142
148
|
}
|
|
143
149
|
}
|
|
144
150
|
|
|
@@ -147,7 +153,7 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
147
153
|
authTokenMap: ReadableMap?,
|
|
148
154
|
promise: Promise
|
|
149
155
|
) {
|
|
150
|
-
|
|
156
|
+
withBridgedLocalExceptions(promise) {
|
|
151
157
|
vcl.submitPresentation(
|
|
152
158
|
presentationSubmission = mapToPresentationSubmission(presentationSubmissionMap),
|
|
153
159
|
authToken = mapToAuthToken(authTokenMap),
|
|
@@ -155,10 +161,8 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
155
161
|
promise.resolve(presentationSubmissionResultToMap(it))
|
|
156
162
|
},
|
|
157
163
|
errorHandler = {
|
|
158
|
-
promise.
|
|
164
|
+
promise.rejectBridgedVCLError(it)
|
|
159
165
|
})
|
|
160
|
-
} catch (ex: Exception) {
|
|
161
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
162
166
|
}
|
|
163
167
|
}
|
|
164
168
|
|
|
@@ -166,16 +170,14 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
166
170
|
exchangeDescriptorMap: ReadableMap,
|
|
167
171
|
promise: Promise
|
|
168
172
|
) {
|
|
169
|
-
|
|
173
|
+
withBridgedLocalExceptions(promise) {
|
|
170
174
|
vcl.getExchangeProgress(mapToExchangeDescriptor(exchangeDescriptorMap),
|
|
171
175
|
{
|
|
172
176
|
promise.resolve(exchangeToMap(it))
|
|
173
177
|
},
|
|
174
178
|
{
|
|
175
|
-
promise.
|
|
179
|
+
promise.rejectBridgedVCLError(it)
|
|
176
180
|
})
|
|
177
|
-
} catch (ex: Exception) {
|
|
178
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
|
|
@@ -183,17 +185,15 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
183
185
|
organizationsSearchDescriptorMap: ReadableMap,
|
|
184
186
|
promise: Promise
|
|
185
187
|
) {
|
|
186
|
-
|
|
188
|
+
withBridgedLocalExceptions(promise) {
|
|
187
189
|
vcl.searchForOrganizations(
|
|
188
190
|
mapToOrganizationsSearchDescriptor(organizationsSearchDescriptorMap),
|
|
189
191
|
{
|
|
190
192
|
promise.resolve(organizationsToMap(it))
|
|
191
193
|
},
|
|
192
194
|
{
|
|
193
|
-
promise.
|
|
195
|
+
promise.rejectBridgedVCLError(it)
|
|
194
196
|
})
|
|
195
|
-
} catch (ex: Exception) {
|
|
196
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
@@ -201,7 +201,7 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
201
201
|
credentialManifestDescriptorMap: ReadableMap,
|
|
202
202
|
promise: Promise
|
|
203
203
|
) {
|
|
204
|
-
|
|
204
|
+
withBridgedLocalExceptions(promise) {
|
|
205
205
|
VCLLog.d(TAG, "credentialManifestDescriptorMap map: $credentialManifestDescriptorMap")
|
|
206
206
|
mapToCredentialManifestDescriptor(credentialManifestDescriptorMap)?.let { credentialManifestDescriptor ->
|
|
207
207
|
VCLLog.d(
|
|
@@ -214,13 +214,15 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
214
214
|
promise.resolve(credentialManifestToMap(it))
|
|
215
215
|
},
|
|
216
216
|
errorHandler = {
|
|
217
|
-
promise.
|
|
217
|
+
promise.rejectBridgedVCLError(it)
|
|
218
218
|
})
|
|
219
219
|
} ?: run {
|
|
220
|
-
promise.
|
|
220
|
+
promise.rejectBridgedVCLError(
|
|
221
|
+
VCLError(
|
|
222
|
+
message = "Unexpected Credential Manifest Descriptor"
|
|
223
|
+
)
|
|
224
|
+
)
|
|
221
225
|
}
|
|
222
|
-
} catch (ex: Exception) {
|
|
223
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
224
226
|
}
|
|
225
227
|
}
|
|
226
228
|
|
|
@@ -228,17 +230,15 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
228
230
|
generateOffersDescriptorMap: ReadableMap,
|
|
229
231
|
promise: Promise
|
|
230
232
|
) {
|
|
231
|
-
|
|
233
|
+
withBridgedLocalExceptions(promise) {
|
|
232
234
|
vcl.generateOffers(
|
|
233
235
|
generateOffersDescriptor = mapToGenerateOffersDescriptor(generateOffersDescriptorMap),
|
|
234
236
|
successHandler = {
|
|
235
237
|
promise.resolve(offersToMap(it))
|
|
236
238
|
},
|
|
237
239
|
errorHandler = {
|
|
238
|
-
promise.
|
|
240
|
+
promise.rejectBridgedVCLError(it)
|
|
239
241
|
})
|
|
240
|
-
} catch (ex: Exception) {
|
|
241
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
@@ -247,7 +247,7 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
247
247
|
sessionTokenMap: ReadableMap,
|
|
248
248
|
promise: Promise
|
|
249
249
|
) {
|
|
250
|
-
|
|
250
|
+
withBridgedLocalExceptions(promise) {
|
|
251
251
|
vcl.checkForOffers(
|
|
252
252
|
mapToGenerateOffersDescriptor(generateOffersDescriptorMap),
|
|
253
253
|
mapToToken(sessionTokenMap),
|
|
@@ -255,10 +255,8 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
255
255
|
promise.resolve(offersToMap(it))
|
|
256
256
|
},
|
|
257
257
|
{
|
|
258
|
-
promise.
|
|
258
|
+
promise.rejectBridgedVCLError(it)
|
|
259
259
|
})
|
|
260
|
-
} catch (ex: Exception) {
|
|
261
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
262
260
|
}
|
|
263
261
|
}
|
|
264
262
|
|
|
@@ -267,7 +265,7 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
267
265
|
sessionTokenMap: ReadableMap,
|
|
268
266
|
promise: Promise
|
|
269
267
|
) {
|
|
270
|
-
|
|
268
|
+
withBridgedLocalExceptions(promise) {
|
|
271
269
|
vcl.finalizeOffers(
|
|
272
270
|
finalizeOffersDescriptor = mapToFinalizedOffersDescriptor(finalizeOffersDescriptorMap),
|
|
273
271
|
sessionToken = mapToToken(sessionTokenMap),
|
|
@@ -275,10 +273,8 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
275
273
|
promise.resolve(jwtVerifiableCredentialsToMap(it))
|
|
276
274
|
},
|
|
277
275
|
errorHandler = {
|
|
278
|
-
promise.
|
|
276
|
+
promise.rejectBridgedVCLError(it)
|
|
279
277
|
})
|
|
280
|
-
} catch (ex: Exception) {
|
|
281
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
282
278
|
}
|
|
283
279
|
}
|
|
284
280
|
|
|
@@ -286,17 +282,15 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
286
282
|
authTokenDescriptorMap: ReadableMap,
|
|
287
283
|
promise: Promise
|
|
288
284
|
) {
|
|
289
|
-
|
|
285
|
+
withBridgedLocalExceptions(promise) {
|
|
290
286
|
vcl.getAuthToken(
|
|
291
287
|
authTokenDescriptor = mapToAuthTokenDescriptor(authTokenDescriptorMap),
|
|
292
288
|
successHandler = {
|
|
293
289
|
promise.resolve(authTokenToMap(it))
|
|
294
290
|
},
|
|
295
291
|
errorHandler = {
|
|
296
|
-
promise.
|
|
292
|
+
promise.rejectBridgedVCLError(it)
|
|
297
293
|
})
|
|
298
|
-
} catch (ex: Exception) {
|
|
299
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
300
294
|
}
|
|
301
295
|
}
|
|
302
296
|
|
|
@@ -304,18 +298,16 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
304
298
|
credentialTypesUIFormSchemaDescriptorMap: ReadableMap,
|
|
305
299
|
promise: Promise
|
|
306
300
|
) {
|
|
307
|
-
|
|
301
|
+
withBridgedLocalExceptions(promise) {
|
|
308
302
|
vcl.getCredentialTypesUIFormSchema(
|
|
309
303
|
mapToCredentialTypesUIFormSchemaDescriptor(credentialTypesUIFormSchemaDescriptorMap),
|
|
310
304
|
{
|
|
311
305
|
promise.resolve(credentialTypesFormSchemaToMap(it))
|
|
312
306
|
},
|
|
313
307
|
{
|
|
314
|
-
promise.
|
|
308
|
+
promise.rejectBridgedVCLError(it)
|
|
315
309
|
}
|
|
316
310
|
)
|
|
317
|
-
} catch (ex: Exception) {
|
|
318
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
319
311
|
}
|
|
320
312
|
}
|
|
321
313
|
|
|
@@ -323,16 +315,14 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
323
315
|
verifiedProfileDescriptorMap: ReadableMap,
|
|
324
316
|
promise: Promise
|
|
325
317
|
) {
|
|
326
|
-
|
|
318
|
+
withBridgedLocalExceptions(promise) {
|
|
327
319
|
vcl.getVerifiedProfile(mapToVerifiedProfileDescriptor(verifiedProfileDescriptorMap),
|
|
328
320
|
{
|
|
329
321
|
promise.resolve(verifiedProfileToMap(it))
|
|
330
322
|
},
|
|
331
323
|
{
|
|
332
|
-
promise.
|
|
324
|
+
promise.rejectBridgedVCLError(it)
|
|
333
325
|
})
|
|
334
|
-
} catch (ex: Exception) {
|
|
335
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
336
326
|
}
|
|
337
327
|
}
|
|
338
328
|
|
|
@@ -342,7 +332,7 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
342
332
|
remoteCryptoServicesTokenMap: ReadableMap?,
|
|
343
333
|
promise: Promise
|
|
344
334
|
) {
|
|
345
|
-
|
|
335
|
+
withBridgedLocalExceptions(promise) {
|
|
346
336
|
vcl.verifyJwt(
|
|
347
337
|
jwt = mapToJwt(jwtMap),
|
|
348
338
|
publicJwk = mapToPublicJwk(publicJwkMap),
|
|
@@ -351,10 +341,8 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
351
341
|
promise.resolve(it)
|
|
352
342
|
},
|
|
353
343
|
errorHandler = {
|
|
354
|
-
promise.
|
|
344
|
+
promise.rejectBridgedVCLError(it)
|
|
355
345
|
})
|
|
356
|
-
} catch (ex: Exception) {
|
|
357
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
358
346
|
}
|
|
359
347
|
}
|
|
360
348
|
|
|
@@ -364,7 +352,7 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
364
352
|
remoteCryptoServicesTokenMap: ReadableMap?,
|
|
365
353
|
promise: Promise
|
|
366
354
|
) {
|
|
367
|
-
|
|
355
|
+
withBridgedLocalExceptions(promise) {
|
|
368
356
|
vcl.generateSignedJwt(
|
|
369
357
|
jwtDescriptor = mapToJwtDescriptor(jwtDescriptorMap),
|
|
370
358
|
didJwk = mapToDidJwk(didJwkMap),
|
|
@@ -373,10 +361,8 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
373
361
|
promise.resolve(Converter.jwtToMap(it))
|
|
374
362
|
},
|
|
375
363
|
errorHandler = {
|
|
376
|
-
promise.
|
|
364
|
+
promise.rejectBridgedVCLError(it)
|
|
377
365
|
})
|
|
378
|
-
} catch (ex: Exception) {
|
|
379
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
380
366
|
}
|
|
381
367
|
}
|
|
382
368
|
|
|
@@ -384,17 +370,15 @@ class VclReactNativeModule(val reactContext: ReactApplicationContext) :
|
|
|
384
370
|
didJwkDescriptorMap: ReadableMap?,
|
|
385
371
|
promise: Promise
|
|
386
372
|
) {
|
|
387
|
-
|
|
373
|
+
withBridgedLocalExceptions(promise) {
|
|
388
374
|
vcl.generateDidJwk(
|
|
389
375
|
didJwkDescriptor = mapToDidJwkDescriptor(didJwkDescriptorMap),
|
|
390
376
|
successHandler = {
|
|
391
377
|
promise.resolve(didJwkToMap(it))
|
|
392
378
|
},
|
|
393
379
|
errorHandler = {
|
|
394
|
-
promise.
|
|
380
|
+
promise.rejectBridgedVCLError(it)
|
|
395
381
|
})
|
|
396
|
-
} catch (ex: Exception) {
|
|
397
|
-
promise.reject(VCLError(ex).toThrowable())
|
|
398
382
|
}
|
|
399
383
|
}
|
|
400
384
|
}
|
package/android/src/main/java/io/velocitycareerlabs/reactnative/extensions/VCLErrorExtensions.kt
CHANGED
|
@@ -7,7 +7,67 @@
|
|
|
7
7
|
|
|
8
8
|
package io.velocitycareerlabs.reactnative.extensions
|
|
9
9
|
|
|
10
|
+
import com.facebook.react.bridge.Promise
|
|
10
11
|
import io.velocitycareerlabs.api.entities.error.VCLError
|
|
12
|
+
import org.json.JSONArray
|
|
13
|
+
import org.json.JSONObject
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
const val BRIDGED_VCL_ERROR_CODE = "VCL_BRIDGED_ERROR_V1"
|
|
16
|
+
private const val DEFAULT_NATIVE_ERROR_STACK_FRAME_LIMIT = 5
|
|
17
|
+
private const val MAX_NATIVE_ERROR_STACK_FRAME_LIMIT = 20
|
|
18
|
+
|
|
19
|
+
object VCLErrorBridgeConfig {
|
|
20
|
+
var nativeErrorStackFrameLimit = DEFAULT_NATIVE_ERROR_STACK_FRAME_LIMIT
|
|
21
|
+
private set
|
|
22
|
+
|
|
23
|
+
fun updateNativeErrorStackFrameLimit(nativeErrorStackFrameLimit: Int?) {
|
|
24
|
+
this.nativeErrorStackFrameLimit =
|
|
25
|
+
nativeErrorStackFrameLimit
|
|
26
|
+
?.coerceIn(0, MAX_NATIVE_ERROR_STACK_FRAME_LIMIT)
|
|
27
|
+
?: DEFAULT_NATIVE_ERROR_STACK_FRAME_LIMIT
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fun Promise.rejectBridgedVCLError(error: VCLError) {
|
|
32
|
+
val serializedError =
|
|
33
|
+
error.toBridgedJsonObject(VCLErrorBridgeConfig.nativeErrorStackFrameLimit).toString()
|
|
34
|
+
reject(BRIDGED_VCL_ERROR_CODE, serializedError, error)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private fun VCLError.toBridgedJsonObject(
|
|
38
|
+
nativeErrorStackFrameLimit: Int
|
|
39
|
+
) = JSONObject().apply {
|
|
40
|
+
putOpt("payload", payload)
|
|
41
|
+
putOpt("error", error)
|
|
42
|
+
putOpt("errorCode", errorCode)
|
|
43
|
+
putOpt("requestId", requestId)
|
|
44
|
+
putOpt("message", message)
|
|
45
|
+
putOpt("statusCode", statusCode)
|
|
46
|
+
put("diagnostics", toDiagnosticsJsonObject(nativeErrorStackFrameLimit))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private fun VCLError.toDiagnosticsJsonObject(
|
|
50
|
+
nativeErrorStackFrameLimit: Int
|
|
51
|
+
) = JSONObject().apply {
|
|
52
|
+
put("nativePlatform", "android")
|
|
53
|
+
put("nativeType", this@toDiagnosticsJsonObject::class.java.name)
|
|
54
|
+
putOpt("nativeStackFrames", stackTrace.toStackFramesJsonArray(nativeErrorStackFrameLimit))
|
|
55
|
+
cause?.let { put("nativeCause", it.toNativeCauseJsonObject(nativeErrorStackFrameLimit)) }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private fun Throwable.toNativeCauseJsonObject(
|
|
59
|
+
nativeErrorStackFrameLimit: Int
|
|
60
|
+
) = JSONObject().apply {
|
|
61
|
+
put("type", javaClass.name)
|
|
62
|
+
put("message", message ?: toString())
|
|
63
|
+
putOpt("stackFrames", stackTrace.toStackFramesJsonArray(nativeErrorStackFrameLimit))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private fun Array<StackTraceElement>.toStackFramesJsonArray(
|
|
67
|
+
nativeErrorStackFrameLimit: Int
|
|
68
|
+
): JSONArray? =
|
|
69
|
+
takeIf { nativeErrorStackFrameLimit > 0 }
|
|
70
|
+
?.take(nativeErrorStackFrameLimit)
|
|
71
|
+
?.map { it.toString() }
|
|
72
|
+
?.takeIf { it.isNotEmpty() }
|
|
73
|
+
?.let { JSONArray(it) }
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
//
|
|
2
|
+
// VCLErrorBridge.swift
|
|
3
|
+
// vcl-react-native
|
|
4
|
+
//
|
|
5
|
+
// Copyright 2022 Velocity Career Labs inc.
|
|
6
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
import React
|
|
11
|
+
import VCL
|
|
12
|
+
|
|
13
|
+
final class VCLErrorBridge {
|
|
14
|
+
|
|
15
|
+
private static let bridgedVCLErrorCode = "VCL_BRIDGED_ERROR_V1"
|
|
16
|
+
private static let defaultNativeErrorStackFrameLimit = 5
|
|
17
|
+
private static let maxNativeErrorStackFrameLimit = 20
|
|
18
|
+
private static let nativeStackFramesDictionaryKey = "callStackSymbols"
|
|
19
|
+
|
|
20
|
+
private var nativeErrorStackFrameLimit = VCLErrorBridge.defaultNativeErrorStackFrameLimit
|
|
21
|
+
|
|
22
|
+
func updateConfiguration(_ initializationDescriptorDictionary: [String: Any]) {
|
|
23
|
+
let stackFrameLimit = initializationDescriptorDictionary["nativeErrorStackFrameLimit"] as? Int
|
|
24
|
+
nativeErrorStackFrameLimit =
|
|
25
|
+
stackFrameLimit.map { min(max($0, 0), Self.maxNativeErrorStackFrameLimit) }
|
|
26
|
+
?? Self.defaultNativeErrorStackFrameLimit
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func rejectBridgedVCLError(
|
|
30
|
+
_ reject: @escaping RCTPromiseRejectBlock,
|
|
31
|
+
error: VCLError
|
|
32
|
+
) {
|
|
33
|
+
let serializedError =
|
|
34
|
+
bridgedVCLErrorPayload(error).toJsonString()
|
|
35
|
+
?? error.message
|
|
36
|
+
?? "Unexpected VCLError"
|
|
37
|
+
reject(Self.bridgedVCLErrorCode, serializedError, error)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private func bridgedVCLErrorPayload(_ error: VCLError) -> [String: Any] {
|
|
41
|
+
var payload = error.toDictionary().compactMapValues { $0 }
|
|
42
|
+
payload.removeValue(forKey: Self.nativeStackFramesDictionaryKey)
|
|
43
|
+
payload["diagnostics"] = bridgedDiagnostics(error)
|
|
44
|
+
return payload
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private func bridgedDiagnostics(_ error: VCLError) -> [String: Any] {
|
|
48
|
+
var diagnostics: [String: Any] = [
|
|
49
|
+
"nativePlatform": "ios",
|
|
50
|
+
"nativeType": String(describing: type(of: error)),
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
if let nativeStackFrames =
|
|
54
|
+
limitedStackFrames(
|
|
55
|
+
error.toDictionary()[Self.nativeStackFramesDictionaryKey] as? [String]
|
|
56
|
+
)
|
|
57
|
+
{
|
|
58
|
+
diagnostics["nativeStackFrames"] = nativeStackFrames
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if let cause = error.cause {
|
|
62
|
+
var nativeCause: [String: Any] = [
|
|
63
|
+
"type": String(describing: type(of: cause)),
|
|
64
|
+
"message": String(describing: cause),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
if let stackFrames =
|
|
68
|
+
limitedStackFrames(
|
|
69
|
+
(cause as? VCLError)?.toDictionary()[Self.nativeStackFramesDictionaryKey] as? [String]
|
|
70
|
+
)
|
|
71
|
+
{
|
|
72
|
+
nativeCause["stackFrames"] = stackFrames
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
diagnostics["nativeCause"] = nativeCause
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return diagnostics
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private func limitedStackFrames(_ stackFrames: [String]?) -> [String]? {
|
|
82
|
+
guard nativeErrorStackFrameLimit > 0, let stackFrames else {
|
|
83
|
+
return nil
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let trimmedStackFrames = Array(stackFrames.prefix(nativeErrorStackFrameLimit))
|
|
87
|
+
return trimmedStackFrames.isEmpty ? nil : trimmedStackFrames
|
|
88
|
+
}
|
|
89
|
+
}
|