brick-module 0.3.1 → 0.4.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/BrickModule.podspec +1 -1
- package/android/brick_modules.gradle +59 -15
- package/android/src/main/java/com/brickmodule/BrickModuleBase.kt +39 -2
- package/android/src/main/java/com/brickmodule/EventDataConverter.kt +45 -32
- package/android/src/main/java/com/brickmodule/Null.kt +7 -0
- package/dist/BrickError.d.ts +29 -0
- package/dist/BrickError.js +49 -0
- package/dist/BrickModule.js +9 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/ios/BrickModule/BrickCoreModule.swift +59 -25
- package/package.json +2 -2
- package/podfile_helper.rb +26 -3
- package/src/BrickError.ts +78 -0
- package/src/BrickModule.ts +22 -7
- package/src/index.ts +3 -0
package/BrickModule.podspec
CHANGED
|
@@ -68,6 +68,29 @@ ext.getBrickAndroidPath = { projectRoot ->
|
|
|
68
68
|
return new File(projectRoot, 'android/.brick').canonicalPath
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
ext.getBrickAndroidAppPath = { projectRoot ->
|
|
72
|
+
// Read brick.json from explicit project root
|
|
73
|
+
try {
|
|
74
|
+
def brickConfigFile = new File(projectRoot, 'brick.json')
|
|
75
|
+
if (brickConfigFile.exists()) {
|
|
76
|
+
def config = new JsonSlurper().parse(brickConfigFile)
|
|
77
|
+
def androidAppPath = config?.output?.androidAppPath
|
|
78
|
+
if (androidAppPath && !androidAppPath.isEmpty()) {
|
|
79
|
+
if (new File(androidAppPath).isAbsolute()) {
|
|
80
|
+
println("[Brick] Warning: Using absolute path for Android app path is not recommended: ${androidAppPath}")
|
|
81
|
+
return androidAppPath
|
|
82
|
+
}
|
|
83
|
+
return new File(projectRoot, androidAppPath).canonicalPath
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch (Exception e) {
|
|
87
|
+
println("[Brick] Warning: Failed to read brick.json from ${projectRoot}: ${e.message}")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Default: android/app in project root
|
|
91
|
+
return new File(projectRoot, 'android/app').canonicalPath
|
|
92
|
+
}
|
|
93
|
+
|
|
71
94
|
ext.getAllDependencies = { packageJson ->
|
|
72
95
|
def deps = []
|
|
73
96
|
deps.addAll(packageJson.dependencies?.keySet() ?: [])
|
|
@@ -79,7 +102,7 @@ ext.isBrickModule = { packageJson ->
|
|
|
79
102
|
return packageJson?.brickModule != null
|
|
80
103
|
}
|
|
81
104
|
|
|
82
|
-
ext.runBrickCodegen = { workingDir ->
|
|
105
|
+
ext.runBrickCodegen = { workingDir, options = [:] ->
|
|
83
106
|
def brickModuleDir = resolveModule(workingDir, "brick-module")
|
|
84
107
|
if (brickModuleDir == null) {
|
|
85
108
|
brickLog("brick-module not found, skipping code generation")
|
|
@@ -94,7 +117,18 @@ ext.runBrickCodegen = { workingDir ->
|
|
|
94
117
|
|
|
95
118
|
try {
|
|
96
119
|
def wdPath = (workingDir instanceof File) ? workingDir.absolutePath : workingDir
|
|
97
|
-
def
|
|
120
|
+
def codegenArgs = [
|
|
121
|
+
'node',
|
|
122
|
+
codegenPath.absolutePath,
|
|
123
|
+
'--platform',
|
|
124
|
+
'android',
|
|
125
|
+
'--projectRoot',
|
|
126
|
+
wdPath
|
|
127
|
+
]
|
|
128
|
+
if (options.noClean == true) {
|
|
129
|
+
codegenArgs.add('--no-clean')
|
|
130
|
+
}
|
|
131
|
+
def proc = codegenArgs.execute(null, workingDir)
|
|
98
132
|
proc.waitFor()
|
|
99
133
|
return proc.exitValue() == 0
|
|
100
134
|
} catch (Exception e) {
|
|
@@ -194,10 +228,12 @@ ext.applyBrickModules = { settings, args ->
|
|
|
194
228
|
def androidBrickPath = getBrickAndroidPath(projectRoot)
|
|
195
229
|
def brickDir = new File(androidBrickPath)
|
|
196
230
|
def needsCodegen = !brickDir.exists() || !new File(brickDir, "src/main/kotlin/BrickModule.kt").exists()
|
|
197
|
-
|
|
231
|
+
def codegenAttempted = false
|
|
198
232
|
if (needsCodegen) {
|
|
199
|
-
|
|
233
|
+
codegenAttempted = true
|
|
234
|
+
runBrickCodegen(projectRoot, [noClean: true])
|
|
200
235
|
}
|
|
236
|
+
settings.ext.brickCodegenRan = codegenAttempted
|
|
201
237
|
|
|
202
238
|
// Include generated module (use configured path)
|
|
203
239
|
if (brickDir.exists()) {
|
|
@@ -216,6 +252,8 @@ ext.applyBrickModules = { settings, args ->
|
|
|
216
252
|
gradle.rootProject.ext.brickModulesList = foundModules
|
|
217
253
|
gradle.rootProject.ext.brickProjectRoot = projectRoot
|
|
218
254
|
gradle.rootProject.ext.brickAppProjectName = appProjectName
|
|
255
|
+
gradle.rootProject.ext.brickCodegenRanInBuild =
|
|
256
|
+
settings.ext.has("brickCodegenRan") ? settings.ext.brickCodegenRan : false
|
|
219
257
|
configureAppProject(gradle)
|
|
220
258
|
// Inline: ensure RN autolinking doesn't fail due to missing codegen/jni for brick-module
|
|
221
259
|
gradle.rootProject.subprojects { subproj ->
|
|
@@ -395,19 +433,16 @@ ${moduleNames}
|
|
|
395
433
|
project.preBuild.dependsOn('generateBrickModulesList')
|
|
396
434
|
project.preBuild.doFirst {
|
|
397
435
|
def workingDir = projectRoot
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
def brickOutputDir = getBrickAndroidPath(workingDir)
|
|
404
|
-
def generatedMarker = new File(brickOutputDir, "src/main/kotlin/BrickModule.kt")
|
|
405
|
-
if (generatedMarker.exists()) {
|
|
406
|
-
// Already generated, skip to preserve build outputs
|
|
436
|
+
|
|
437
|
+
def alreadyRan = rootProject.ext.has("brickCodegenRanInBuild") ?
|
|
438
|
+
rootProject.ext.brickCodegenRanInBuild : false
|
|
439
|
+
if (alreadyRan) {
|
|
440
|
+
// Avoid double-running if settings already executed codegen.
|
|
407
441
|
return
|
|
408
442
|
}
|
|
443
|
+
rootProject.ext.brickCodegenRanInBuild = true
|
|
409
444
|
|
|
410
|
-
// Run codegen
|
|
445
|
+
// Run codegen once per build without cleaning outputs.
|
|
411
446
|
try {
|
|
412
447
|
def proc = ['node', '-p', "require.resolve('brick-module/package.json')"].execute(null, workingDir)
|
|
413
448
|
proc.waitFor()
|
|
@@ -419,7 +454,16 @@ ${moduleNames}
|
|
|
419
454
|
def codegenPath = new File(brickModuleDir, "bin/brick-codegen.js")
|
|
420
455
|
|
|
421
456
|
if (codegenPath.exists()) {
|
|
422
|
-
def
|
|
457
|
+
def codegenArgs = [
|
|
458
|
+
'node',
|
|
459
|
+
codegenPath.absolutePath,
|
|
460
|
+
'--platform',
|
|
461
|
+
'android',
|
|
462
|
+
'--projectRoot',
|
|
463
|
+
workingDir.absolutePath,
|
|
464
|
+
'--no-clean'
|
|
465
|
+
]
|
|
466
|
+
def codegenProc = codegenArgs.execute(null, workingDir)
|
|
423
467
|
codegenProc.waitFor()
|
|
424
468
|
}
|
|
425
469
|
}
|
|
@@ -58,8 +58,45 @@ abstract class BrickModuleSpec(private val reactContext: ReactContext) : BrickMo
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
// MARK: - BrickError Interface
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Error interface for Brick modules.
|
|
65
|
+
* Implement this interface to propagate all error properties to JavaScript.
|
|
66
|
+
* Follows the same pattern as iOS BrickError protocol.
|
|
67
|
+
*
|
|
68
|
+
* Note: message is inherited from Throwable, so it's not defined in this interface.
|
|
69
|
+
* Classes implementing BrickError must extend Exception/Throwable.
|
|
70
|
+
*/
|
|
71
|
+
interface BrickError {
|
|
72
|
+
/** Error code (accessed as error.name in JS) */
|
|
73
|
+
val code: String
|
|
74
|
+
|
|
75
|
+
/** Additional properties (accessed as error.userInfo.xxx in JS) */
|
|
76
|
+
val userInfo: Map<String, Any>?
|
|
77
|
+
get() = null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Default implementation of BrickError.
|
|
82
|
+
* Extend this class to create custom errors.
|
|
83
|
+
*/
|
|
84
|
+
open class BrickException(
|
|
85
|
+
override val code: String,
|
|
86
|
+
message: String,
|
|
87
|
+
override val userInfo: Map<String, Any>? = null,
|
|
88
|
+
cause: Throwable? = null
|
|
89
|
+
) : Exception(message, cause), BrickError
|
|
90
|
+
|
|
91
|
+
/** Error types for Brick modules - implements BrickError interface */
|
|
92
|
+
open class BrickModuleError(
|
|
93
|
+
message: String,
|
|
94
|
+
override val code: String
|
|
95
|
+
) : Exception(message), BrickError {
|
|
96
|
+
|
|
97
|
+
override val userInfo: Map<String, Any>?
|
|
98
|
+
get() = null
|
|
99
|
+
|
|
63
100
|
class TypeMismatch(message: String) : BrickModuleError("Type mismatch: $message", "TYPE_ERROR")
|
|
64
101
|
class ExecutionError(message: String) :
|
|
65
102
|
BrickModuleError("Execution error: $message", "EXECUTION_ERROR")
|
|
@@ -3,33 +3,45 @@ package com.brickmodule
|
|
|
3
3
|
import com.facebook.react.bridge.*
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Helper class for converting data to React Native compatible formats
|
|
7
|
-
*
|
|
6
|
+
* Helper class for converting data to React Native compatible formats Ensures type safety when
|
|
7
|
+
* sending events to JavaScript
|
|
8
8
|
*/
|
|
9
9
|
object EventDataConverter {
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
private fun isNullSentinel(value: Any?): Boolean {
|
|
12
|
+
return value is Null ||
|
|
13
|
+
(value != null && value.javaClass.name == "com.brickmodule.Null") ||
|
|
14
|
+
(value is String && value.startsWith("com.brickmodule.Null@"))
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
/**
|
|
12
18
|
* Convert any data to React Native compatible format
|
|
13
|
-
*
|
|
19
|
+
*
|
|
14
20
|
* @param data The data to convert
|
|
15
21
|
* @return React Native compatible data or null if conversion fails
|
|
16
22
|
*/
|
|
17
23
|
fun convert(data: Any?): Any? {
|
|
18
24
|
return try {
|
|
19
|
-
|
|
20
|
-
null
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
data.
|
|
25
|
+
if (isNullSentinel(data)) {
|
|
26
|
+
null
|
|
27
|
+
} else {
|
|
28
|
+
when (data) {
|
|
29
|
+
null -> null
|
|
30
|
+
is String -> data
|
|
31
|
+
is Boolean -> data
|
|
32
|
+
is Int -> data
|
|
33
|
+
is Double -> data
|
|
34
|
+
is Float -> data.toDouble()
|
|
35
|
+
is Long -> data.toDouble()
|
|
36
|
+
is Map<*, *> -> convertMap(data)
|
|
37
|
+
is List<*> -> convertList(data)
|
|
38
|
+
is Array<*> -> convertList(data.toList())
|
|
39
|
+
else -> {
|
|
40
|
+
println(
|
|
41
|
+
"⚠️ EventDataConverter: Unsupported data type ${data.javaClass.simpleName}, converting to string"
|
|
42
|
+
)
|
|
43
|
+
data.toString()
|
|
44
|
+
}
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
} catch (e: Exception) {
|
|
@@ -37,17 +49,17 @@ object EventDataConverter {
|
|
|
37
49
|
null
|
|
38
50
|
}
|
|
39
51
|
}
|
|
40
|
-
|
|
52
|
+
|
|
41
53
|
/**
|
|
42
54
|
* Convert a map to WritableMap for React Native
|
|
43
|
-
*
|
|
55
|
+
*
|
|
44
56
|
* @param map The map to convert
|
|
45
57
|
* @return WritableMap or null if conversion fails
|
|
46
58
|
*/
|
|
47
59
|
private fun convertMap(map: Map<*, *>): WritableMap? {
|
|
48
60
|
return try {
|
|
49
61
|
val writableMap = Arguments.createMap()
|
|
50
|
-
|
|
62
|
+
|
|
51
63
|
map.forEach { (key, value) ->
|
|
52
64
|
val keyString = key?.toString()
|
|
53
65
|
if (keyString != null) {
|
|
@@ -63,24 +75,24 @@ object EventDataConverter {
|
|
|
63
75
|
}
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
|
-
|
|
78
|
+
|
|
67
79
|
writableMap
|
|
68
80
|
} catch (e: Exception) {
|
|
69
81
|
println("❌ EventDataConverter: Failed to convert map: ${e.message}")
|
|
70
82
|
null
|
|
71
83
|
}
|
|
72
84
|
}
|
|
73
|
-
|
|
85
|
+
|
|
74
86
|
/**
|
|
75
87
|
* Convert a list to WritableArray for React Native
|
|
76
|
-
*
|
|
88
|
+
*
|
|
77
89
|
* @param list The list to convert
|
|
78
90
|
* @return WritableArray or null if conversion fails
|
|
79
91
|
*/
|
|
80
92
|
private fun convertList(list: List<*>): WritableArray? {
|
|
81
93
|
return try {
|
|
82
94
|
val writableArray = Arguments.createArray()
|
|
83
|
-
|
|
95
|
+
|
|
84
96
|
list.forEach { item ->
|
|
85
97
|
when (val convertedValue = convert(item)) {
|
|
86
98
|
null -> writableArray.pushNull()
|
|
@@ -93,30 +105,31 @@ object EventDataConverter {
|
|
|
93
105
|
else -> writableArray.pushString(convertedValue.toString())
|
|
94
106
|
}
|
|
95
107
|
}
|
|
96
|
-
|
|
108
|
+
|
|
97
109
|
writableArray
|
|
98
110
|
} catch (e: Exception) {
|
|
99
111
|
println("❌ EventDataConverter: Failed to convert list: ${e.message}")
|
|
100
112
|
null
|
|
101
113
|
}
|
|
102
114
|
}
|
|
103
|
-
|
|
115
|
+
|
|
104
116
|
/**
|
|
105
117
|
* Validate if data can be safely converted to React Native format
|
|
106
|
-
*
|
|
118
|
+
*
|
|
107
119
|
* @param data The data to validate
|
|
108
120
|
* @return true if data can be safely converted, false otherwise
|
|
109
121
|
*/
|
|
110
122
|
fun isConvertible(data: Any?): Boolean {
|
|
123
|
+
if (isNullSentinel(data)) {
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
111
126
|
return when (data) {
|
|
112
127
|
null -> true
|
|
113
128
|
is String, is Boolean, is Number -> true
|
|
114
|
-
is Map<*, *> -> data.all { (key, value) ->
|
|
115
|
-
key is String && isConvertible(value)
|
|
116
|
-
}
|
|
129
|
+
is Map<*, *> -> data.all { (key, value) -> key is String && isConvertible(value) }
|
|
117
130
|
is List<*> -> data.all { isConvertible(it) }
|
|
118
131
|
is Array<*> -> data.all { isConvertible(it) }
|
|
119
132
|
else -> false
|
|
120
133
|
}
|
|
121
134
|
}
|
|
122
|
-
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/BrickError.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* BrickError - Type-safe class for custom errors from native modules.
|
|
4
|
+
* Matches the structure of iOS/Android BrickError.
|
|
5
|
+
*/
|
|
6
|
+
declare class BrickError extends Error {
|
|
7
|
+
/** Error code (same as error.name) */
|
|
8
|
+
readonly code: string;
|
|
9
|
+
/** Additional properties (e.g., amount, currency) */
|
|
10
|
+
readonly userInfo: Record<string, unknown>;
|
|
11
|
+
/** Name of the module where the error occurred */
|
|
12
|
+
readonly moduleName?: string;
|
|
13
|
+
constructor(message: string, code: string, userInfo?: Record<string, unknown>, moduleName?: string);
|
|
14
|
+
/**
|
|
15
|
+
* Type guard to check if an error object is a BrickError.
|
|
16
|
+
* @param error - The error object to check
|
|
17
|
+
* @returns true if the error is a BrickError
|
|
18
|
+
*/
|
|
19
|
+
static isBrickError(error: unknown): error is BrickError;
|
|
20
|
+
/**
|
|
21
|
+
* Converts a general error object to a BrickError.
|
|
22
|
+
* @param error - The error object to convert
|
|
23
|
+
* @param moduleName - Name of the module where the error occurred
|
|
24
|
+
* @returns A BrickError instance
|
|
25
|
+
*/
|
|
26
|
+
static from(error: unknown, moduleName?: string): BrickError;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { BrickError };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/BrickError.ts
|
|
2
|
+
/**
|
|
3
|
+
* BrickError - Type-safe class for custom errors from native modules.
|
|
4
|
+
* Matches the structure of iOS/Android BrickError.
|
|
5
|
+
*/
|
|
6
|
+
var BrickError = class BrickError extends Error {
|
|
7
|
+
/** Error code (same as error.name) */
|
|
8
|
+
code;
|
|
9
|
+
/** Additional properties (e.g., amount, currency) */
|
|
10
|
+
userInfo;
|
|
11
|
+
/** Name of the module where the error occurred */
|
|
12
|
+
moduleName;
|
|
13
|
+
constructor(message, code, userInfo = {}, moduleName) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = code;
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.userInfo = userInfo;
|
|
18
|
+
this.moduleName = moduleName;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Type guard to check if an error object is a BrickError.
|
|
22
|
+
* @param error - The error object to check
|
|
23
|
+
* @returns true if the error is a BrickError
|
|
24
|
+
*/
|
|
25
|
+
static isBrickError(error) {
|
|
26
|
+
return error instanceof Error && typeof error.code === "string" && typeof error.userInfo === "object" && error.userInfo !== null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Converts a general error object to a BrickError.
|
|
30
|
+
* @param error - The error object to convert
|
|
31
|
+
* @param moduleName - Name of the module where the error occurred
|
|
32
|
+
* @returns A BrickError instance
|
|
33
|
+
*/
|
|
34
|
+
static from(error, moduleName) {
|
|
35
|
+
if (error instanceof BrickError) {
|
|
36
|
+
if (moduleName && !error.moduleName) return new BrickError(error.message, error.code, error.userInfo, moduleName);
|
|
37
|
+
return error;
|
|
38
|
+
}
|
|
39
|
+
if (BrickError.isBrickError(error)) return new BrickError(error.message, error.code, error.userInfo, moduleName || error.moduleName);
|
|
40
|
+
if (error instanceof Error) {
|
|
41
|
+
const anyError = error;
|
|
42
|
+
return new BrickError(error.message, anyError.code || anyError.name || "UNKNOWN_ERROR", anyError.userInfo || {}, moduleName || anyError.moduleName);
|
|
43
|
+
}
|
|
44
|
+
return new BrickError(String(error), "UNKNOWN_ERROR", {}, moduleName);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { BrickError };
|
package/dist/BrickModule.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BrickError } from "./BrickError.js";
|
|
1
2
|
import { TurboModuleRegistry } from "react-native";
|
|
2
3
|
|
|
3
4
|
//#region src/BrickModule.ts
|
|
@@ -49,11 +50,15 @@ function get(moduleName) {
|
|
|
49
50
|
if (result && typeof result === "object" && result["~sync"] === true) if (result.success === true) return result.value;
|
|
50
51
|
else {
|
|
51
52
|
const errorInfo = result.error || {};
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const message = errorInfo.message || errorInfo.errorMessage || "Unknown error";
|
|
54
|
+
const code = errorInfo.code || errorInfo.errorCode || "BRICK_ERROR";
|
|
55
|
+
const userInfo = { code };
|
|
56
|
+
for (const key of Object.keys(errorInfo)) if (key !== "code" && key !== "message" && key !== "errorCode" && key !== "errorMessage") userInfo[key] = errorInfo[key];
|
|
57
|
+
throw new BrickError(message, code, userInfo, moduleName);
|
|
56
58
|
}
|
|
59
|
+
if (result && typeof result.then === "function") return result.catch((error) => {
|
|
60
|
+
throw BrickError.from(error, moduleName);
|
|
61
|
+
});
|
|
57
62
|
return result;
|
|
58
63
|
}
|
|
59
64
|
throw new Error(`Method ${methodKey} not found`);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { BrickModule, BrickModuleInterface, BrickModuleSpec } from "./BrickModule.js";
|
|
2
|
+
import { BrickError } from "./BrickError.js";
|
|
2
3
|
import { Any, AnyObject } from "./types.js";
|
|
3
4
|
|
|
4
5
|
//#region src/index.d.ts
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Error types for Brick modules
|
|
9
|
+
* @deprecated Use BrickError instead
|
|
8
10
|
*/
|
|
9
11
|
declare class BrickModuleError extends Error {
|
|
10
12
|
code: string;
|
|
@@ -29,4 +31,4 @@ interface BrickCodegenConfig {
|
|
|
29
31
|
dev?: boolean;
|
|
30
32
|
}
|
|
31
33
|
//#endregion
|
|
32
|
-
export { Any, AnyObject, BrickCodegenConfig, BrickModule, BrickModuleError, type BrickModuleInterface, type BrickModuleSpec };
|
|
34
|
+
export { Any, AnyObject, BrickCodegenConfig, BrickError, BrickModule, BrickModuleError, type BrickModuleInterface, type BrickModuleSpec };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { BrickError } from "./BrickError.js";
|
|
1
2
|
import BrickModule_default from "./BrickModule.js";
|
|
2
3
|
|
|
3
4
|
//#region src/index.ts
|
|
4
5
|
/**
|
|
5
6
|
* Error types for Brick modules
|
|
7
|
+
* @deprecated Use BrickError instead
|
|
6
8
|
*/
|
|
7
9
|
var BrickModuleError = class extends Error {
|
|
8
10
|
constructor(message, code = "BRICK_ERROR", moduleName, methodName) {
|
|
@@ -15,4 +17,4 @@ var BrickModuleError = class extends Error {
|
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
//#endregion
|
|
18
|
-
export { BrickModule_default as BrickModule, BrickModuleError };
|
|
20
|
+
export { BrickError, BrickModule_default as BrickModule, BrickModuleError };
|
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import React
|
|
3
3
|
|
|
4
|
+
// MARK: - BrickError Protocol
|
|
5
|
+
|
|
6
|
+
/// Error protocol for Brick modules.
|
|
7
|
+
/// Conforming to this protocol propagates all error properties to JavaScript.
|
|
8
|
+
public protocol BrickError: Error {
|
|
9
|
+
/// Error message (accessed as error.message in JS)
|
|
10
|
+
var message: String { get }
|
|
11
|
+
|
|
12
|
+
/// Error code (accessed as error.code in JS, optional)
|
|
13
|
+
var code: String? { get }
|
|
14
|
+
|
|
15
|
+
/// Additional properties (accessed as error.userInfo.xxx in JS)
|
|
16
|
+
var userInfo: [String: Any]? { get }
|
|
17
|
+
|
|
18
|
+
/// Convert to NSError (for React Native reject)
|
|
19
|
+
func asNSError() -> NSError
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public extension BrickError {
|
|
23
|
+
/// Default code implementation
|
|
24
|
+
var code: String? { nil }
|
|
25
|
+
|
|
26
|
+
/// Default userInfo implementation
|
|
27
|
+
var userInfo: [String: Any]? { nil }
|
|
28
|
+
|
|
29
|
+
/// Default NSError conversion implementation
|
|
30
|
+
func asNSError() -> NSError {
|
|
31
|
+
var info: [String: Any] = userInfo ?? [:]
|
|
32
|
+
info["code"] = code ?? "EXECUTION_ERROR"
|
|
33
|
+
info["message"] = message
|
|
34
|
+
info[NSLocalizedDescriptionKey] = message
|
|
35
|
+
return NSError(domain: "BrickModule", code: 0, userInfo: info)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// Error code to use when calling reject
|
|
39
|
+
var rejectCode: String {
|
|
40
|
+
code ?? "EXECUTION_ERROR"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// MARK: - BrickModuleBase
|
|
45
|
+
|
|
4
46
|
/**
|
|
5
47
|
* Base class for all Brick modules
|
|
6
48
|
* Provides common functionality including event emission
|
|
@@ -30,43 +72,35 @@ open class BrickModuleBase: NSObject {
|
|
|
30
72
|
}
|
|
31
73
|
}
|
|
32
74
|
|
|
75
|
+
// MARK: - BrickModuleError
|
|
76
|
+
|
|
33
77
|
/**
|
|
34
78
|
* Error types for Brick modules
|
|
35
79
|
*/
|
|
36
|
-
public enum BrickModuleError:
|
|
80
|
+
public enum BrickModuleError: BrickError {
|
|
37
81
|
case typeMismatch(String)
|
|
38
82
|
case executionError(String)
|
|
39
83
|
case invalidDefinition(String)
|
|
40
84
|
case methodNotFound(String)
|
|
41
85
|
case moduleNotFound(String)
|
|
42
|
-
|
|
43
|
-
public var
|
|
86
|
+
|
|
87
|
+
public var code: String? {
|
|
44
88
|
switch self {
|
|
45
|
-
case .typeMismatch
|
|
46
|
-
|
|
47
|
-
case .
|
|
48
|
-
|
|
49
|
-
case .
|
|
50
|
-
return "Invalid definition: \(message)"
|
|
51
|
-
case .methodNotFound(let message):
|
|
52
|
-
return "Method not found: \(message)"
|
|
53
|
-
case .moduleNotFound(let message):
|
|
54
|
-
return "Module not found: \(message)"
|
|
89
|
+
case .typeMismatch: return "TYPE_ERROR"
|
|
90
|
+
case .executionError: return "EXECUTION_ERROR"
|
|
91
|
+
case .invalidDefinition: return "DEFINITION_ERROR"
|
|
92
|
+
case .methodNotFound: return "METHOD_NOT_FOUND"
|
|
93
|
+
case .moduleNotFound: return "MODULE_NOT_FOUND"
|
|
55
94
|
}
|
|
56
95
|
}
|
|
57
|
-
|
|
58
|
-
public var
|
|
96
|
+
|
|
97
|
+
public var message: String {
|
|
59
98
|
switch self {
|
|
60
|
-
case .typeMismatch:
|
|
61
|
-
|
|
62
|
-
case .
|
|
63
|
-
|
|
64
|
-
case .
|
|
65
|
-
return "DEFINITION_ERROR"
|
|
66
|
-
case .methodNotFound:
|
|
67
|
-
return "METHOD_NOT_FOUND"
|
|
68
|
-
case .moduleNotFound:
|
|
69
|
-
return "MODULE_NOT_FOUND"
|
|
99
|
+
case .typeMismatch(let msg): return "Type mismatch: \(msg)"
|
|
100
|
+
case .executionError(let msg): return "Execution error: \(msg)"
|
|
101
|
+
case .invalidDefinition(let msg): return "Invalid definition: \(msg)"
|
|
102
|
+
case .methodNotFound(let msg): return "Method not found: \(msg)"
|
|
103
|
+
case .moduleNotFound(let msg): return "Module not found: \(msg)"
|
|
70
104
|
}
|
|
71
105
|
}
|
|
72
106
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brick-module",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Better React Native native module development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"brick-codegen": "./bin/brick-codegen.js"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"brick-codegen": "0.
|
|
61
|
+
"brick-codegen": "0.4.1"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"react": ">=18.2.0",
|
package/podfile_helper.rb
CHANGED
|
@@ -27,7 +27,14 @@ def use_brick_modules!(app_path: nil)
|
|
|
27
27
|
else
|
|
28
28
|
File.expand_path(File.join(brick_root, ios_brick_path))
|
|
29
29
|
end
|
|
30
|
-
brick_codegen_podspec_path = File.join(
|
|
30
|
+
brick_codegen_podspec_path = File.join(
|
|
31
|
+
brick_codegen_pod_path,
|
|
32
|
+
'BrickCodegen.podspec'
|
|
33
|
+
)
|
|
34
|
+
brick_codegen_pod_relative_path = relative_pod_path(
|
|
35
|
+
brick_codegen_pod_path,
|
|
36
|
+
podfile_dir
|
|
37
|
+
)
|
|
31
38
|
|
|
32
39
|
# Run brick-codegen with real-time output and colors (iOS only)
|
|
33
40
|
exit_status = system("cd #{brick_root} && FORCE_COLOR=1 npx brick-codegen --platform ios --projectRoot \"#{brick_root}\"")
|
|
@@ -42,8 +49,8 @@ def use_brick_modules!(app_path: nil)
|
|
|
42
49
|
end
|
|
43
50
|
|
|
44
51
|
# Link generated BrickCodegen pod
|
|
45
|
-
pod 'BrickCodegen', :path =>
|
|
46
|
-
Pod::UI.puts "[Brick] Linked BrickCodegen from #{
|
|
52
|
+
pod 'BrickCodegen', :path => brick_codegen_pod_relative_path
|
|
53
|
+
Pod::UI.puts "[Brick] Linked BrickCodegen from #{brick_codegen_pod_relative_path}"
|
|
47
54
|
rescue => e
|
|
48
55
|
# Re-raise so CocoaPods fails the install
|
|
49
56
|
raise e
|
|
@@ -74,3 +81,19 @@ def get_brick_ios_path(project_root)
|
|
|
74
81
|
|
|
75
82
|
return File.expand_path(File.join(project_root, 'ios/.brick'))
|
|
76
83
|
end
|
|
84
|
+
|
|
85
|
+
def podfile_dir
|
|
86
|
+
podfile_path = Pod::Config.instance.podfile_path
|
|
87
|
+
return Pathname.new(Dir.pwd).expand_path if podfile_path.nil?
|
|
88
|
+
|
|
89
|
+
podfile_path.dirname.expand_path
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def relative_pod_path(target_path, base_dir)
|
|
93
|
+
Pathname.new(target_path)
|
|
94
|
+
.expand_path
|
|
95
|
+
.relative_path_from(base_dir)
|
|
96
|
+
.to_s
|
|
97
|
+
rescue ArgumentError
|
|
98
|
+
target_path
|
|
99
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrickError - Type-safe class for custom errors from native modules.
|
|
3
|
+
* Matches the structure of iOS/Android BrickError.
|
|
4
|
+
*/
|
|
5
|
+
export class BrickError extends Error {
|
|
6
|
+
/** Error code (same as error.name) */
|
|
7
|
+
readonly code: string;
|
|
8
|
+
|
|
9
|
+
/** Additional properties (e.g., amount, currency) */
|
|
10
|
+
readonly userInfo: Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
/** Name of the module where the error occurred */
|
|
13
|
+
readonly moduleName?: string;
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
message: string,
|
|
17
|
+
code: string,
|
|
18
|
+
userInfo: Record<string, unknown> = {},
|
|
19
|
+
moduleName?: string
|
|
20
|
+
) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = code;
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.userInfo = userInfo;
|
|
25
|
+
this.moduleName = moduleName;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Type guard to check if an error object is a BrickError.
|
|
30
|
+
* @param error - The error object to check
|
|
31
|
+
* @returns true if the error is a BrickError
|
|
32
|
+
*/
|
|
33
|
+
static isBrickError(error: unknown): error is BrickError {
|
|
34
|
+
return (
|
|
35
|
+
error instanceof Error &&
|
|
36
|
+
typeof (error as any).code === "string" &&
|
|
37
|
+
typeof (error as any).userInfo === "object" &&
|
|
38
|
+
(error as any).userInfo !== null
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Converts a general error object to a BrickError.
|
|
44
|
+
* @param error - The error object to convert
|
|
45
|
+
* @param moduleName - Name of the module where the error occurred
|
|
46
|
+
* @returns A BrickError instance
|
|
47
|
+
*/
|
|
48
|
+
static from(error: unknown, moduleName?: string): BrickError {
|
|
49
|
+
if (error instanceof BrickError) {
|
|
50
|
+
// Update moduleName if provided
|
|
51
|
+
if (moduleName && !error.moduleName) {
|
|
52
|
+
return new BrickError(error.message, error.code, error.userInfo, moduleName);
|
|
53
|
+
}
|
|
54
|
+
return error;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (BrickError.isBrickError(error)) {
|
|
58
|
+
return new BrickError(
|
|
59
|
+
error.message,
|
|
60
|
+
error.code,
|
|
61
|
+
error.userInfo,
|
|
62
|
+
moduleName || (error as any).moduleName
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
const anyError = error as any;
|
|
68
|
+
return new BrickError(
|
|
69
|
+
error.message,
|
|
70
|
+
anyError.code || anyError.name || "UNKNOWN_ERROR",
|
|
71
|
+
anyError.userInfo || {},
|
|
72
|
+
moduleName || anyError.moduleName
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return new BrickError(String(error), "UNKNOWN_ERROR", {}, moduleName);
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/BrickModule.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { TurboModule } from "react-native";
|
|
7
7
|
import { TurboModuleRegistry } from "react-native";
|
|
8
|
+
import { BrickError } from "./BrickError";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Base interface that all Brick module specs must extend
|
|
@@ -133,18 +134,32 @@ function get<T extends BrickModuleInterface>(
|
|
|
133
134
|
// Success: return the unwrapped value
|
|
134
135
|
return result.value;
|
|
135
136
|
} else {
|
|
136
|
-
// Failure: throw
|
|
137
|
+
// Failure: throw a BrickError with all properties from error object
|
|
137
138
|
const errorInfo = result.error || {};
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
const message = errorInfo.message || errorInfo.errorMessage || "Unknown error";
|
|
140
|
+
const code = errorInfo.code || errorInfo.errorCode || "BRICK_ERROR";
|
|
141
|
+
|
|
142
|
+
// Build userInfo from additional properties
|
|
143
|
+
const userInfo: Record<string, unknown> = { code };
|
|
144
|
+
for (const key of Object.keys(errorInfo)) {
|
|
145
|
+
if (key !== "code" && key !== "message" && key !== "errorCode" && key !== "errorMessage") {
|
|
146
|
+
userInfo[key] = errorInfo[key];
|
|
147
|
+
}
|
|
142
148
|
}
|
|
143
|
-
|
|
149
|
+
|
|
150
|
+
throw new BrickError(message, code, userInfo, moduleName);
|
|
144
151
|
}
|
|
145
152
|
}
|
|
146
153
|
|
|
147
|
-
//
|
|
154
|
+
// Async method: wrap Promise and convert to BrickError
|
|
155
|
+
if (result && typeof result.then === "function") {
|
|
156
|
+
return result.catch((error: unknown) => {
|
|
157
|
+
// Convert to BrickError with moduleName for type-safe error handling
|
|
158
|
+
throw BrickError.from(error, moduleName);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Not wrapped: return as-is
|
|
148
163
|
return result;
|
|
149
164
|
}
|
|
150
165
|
|
package/src/index.ts
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
export type { BrickModuleInterface, BrickModuleSpec } from "./BrickModule";
|
|
5
5
|
// Main API exports
|
|
6
6
|
export { default as BrickModule } from "./BrickModule";
|
|
7
|
+
export { BrickError } from "./BrickError";
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* Error types for Brick modules
|
|
11
|
+
* @deprecated Use BrickError instead
|
|
9
12
|
*/
|
|
10
13
|
export class BrickModuleError extends Error {
|
|
11
14
|
constructor(
|