@nebula-rn/host 0.0.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/NebulaHost.podspec +23 -0
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +27 -0
- package/android/consumer-rules.pro +1 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaActivity.kt +290 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaAppManager.kt +134 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaConfig.kt +324 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaEventHub.kt +49 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaHost.kt +145 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaHostModalActivity.kt +178 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaManifestManager.kt +130 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaNativeModule.kt +604 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaPackage.kt +16 -0
- package/android/src/main/java/com/hectorzhuang/nebula/NebulaRouter.kt +300 -0
- package/ios/Nebula/NebulaAppManager.swift +355 -0
- package/ios/Nebula/NebulaConfig.swift +549 -0
- package/ios/Nebula/NebulaContainerController.swift +580 -0
- package/ios/Nebula/NebulaDevLoading.swift +333 -0
- package/ios/Nebula/NebulaHost.swift +611 -0
- package/ios/Nebula/NebulaManifest.swift +214 -0
- package/ios/Nebula/NebulaNativeModule.swift +682 -0
- package/ios/Nebula/NebulaNativeModuleBridge.m +364 -0
- package/ios/Nebula/NebulaPerformanceMonitor.swift +46 -0
- package/ios/Nebula/NebulaRouter.swift +594 -0
- package/ios/Nebula/NebulaRouterBridge.m +19 -0
- package/ios/Nebula/RNInstanceViewController.swift +52 -0
- package/package.json +41 -0
- package/react-native.config.js +14 -0
- package/src/index.ts +9 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
//
|
|
2
|
+
// NebulaManifest.swift
|
|
3
|
+
// SuperApp - Nebula Mini-App Container
|
|
4
|
+
//
|
|
5
|
+
// Mini-app manifest (app.json) parser and registry
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
struct NebulaPageStyle: Codable {
|
|
11
|
+
let backgroundColor: String?
|
|
12
|
+
let navigationBarBackgroundColor: String?
|
|
13
|
+
let navigationBarTextColor: String?
|
|
14
|
+
let navigationBarTitleText: String?
|
|
15
|
+
let navigationStyle: String?
|
|
16
|
+
let visualEffectInBackground: String?
|
|
17
|
+
|
|
18
|
+
func merged(with override: NebulaPageStyle?) -> NebulaPageStyle {
|
|
19
|
+
guard let override = override else { return self }
|
|
20
|
+
return NebulaPageStyle(
|
|
21
|
+
backgroundColor: override.backgroundColor ?? backgroundColor,
|
|
22
|
+
navigationBarBackgroundColor: override.navigationBarBackgroundColor ?? navigationBarBackgroundColor,
|
|
23
|
+
navigationBarTextColor: override.navigationBarTextColor ?? navigationBarTextColor,
|
|
24
|
+
navigationBarTitleText: override.navigationBarTitleText ?? navigationBarTitleText,
|
|
25
|
+
navigationStyle: override.navigationStyle ?? navigationStyle,
|
|
26
|
+
visualEffectInBackground: override.visualEffectInBackground ?? visualEffectInBackground
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
func asDictionary() -> [String: Any] {
|
|
31
|
+
var result: [String: Any] = [:]
|
|
32
|
+
if let value = backgroundColor { result["backgroundColor"] = value }
|
|
33
|
+
if let value = navigationBarBackgroundColor { result["navigationBarBackgroundColor"] = value }
|
|
34
|
+
if let value = navigationBarTextColor { result["navigationBarTextColor"] = value }
|
|
35
|
+
if let value = navigationBarTitleText { result["navigationBarTitleText"] = value }
|
|
36
|
+
if let value = navigationStyle { result["navigationStyle"] = value }
|
|
37
|
+
if let value = visualEffectInBackground { result["visualEffectInBackground"] = value }
|
|
38
|
+
return result
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
init(backgroundColor: String? = nil,
|
|
42
|
+
navigationBarBackgroundColor: String? = nil,
|
|
43
|
+
navigationBarTextColor: String? = nil,
|
|
44
|
+
navigationBarTitleText: String? = nil,
|
|
45
|
+
navigationStyle: String? = nil,
|
|
46
|
+
visualEffectInBackground: String? = nil) {
|
|
47
|
+
self.backgroundColor = backgroundColor
|
|
48
|
+
self.navigationBarBackgroundColor = navigationBarBackgroundColor
|
|
49
|
+
self.navigationBarTextColor = navigationBarTextColor
|
|
50
|
+
self.navigationBarTitleText = navigationBarTitleText
|
|
51
|
+
self.navigationStyle = navigationStyle
|
|
52
|
+
self.visualEffectInBackground = visualEffectInBackground
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
struct NebulaManifest: Codable {
|
|
57
|
+
let entryPagePath: String?
|
|
58
|
+
let pages: [String: String] // path -> componentName
|
|
59
|
+
let pageConfigs: [String: NebulaPageStyle]?
|
|
60
|
+
let updateStrategy: String?
|
|
61
|
+
let version: String?
|
|
62
|
+
let window: NebulaPageStyle?
|
|
63
|
+
|
|
64
|
+
/// Get the component name for a given route path
|
|
65
|
+
func getComponentName(forPath path: String) -> String? {
|
|
66
|
+
let normalizedPath = normalizePath(path)
|
|
67
|
+
|
|
68
|
+
// Exact match first
|
|
69
|
+
if let component = pages[normalizedPath] {
|
|
70
|
+
return component
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Try without leading slash
|
|
74
|
+
let withoutSlash = normalizedPath.hasPrefix("/")
|
|
75
|
+
? String(normalizedPath.dropFirst())
|
|
76
|
+
: normalizedPath
|
|
77
|
+
if let component = pages[withoutSlash] {
|
|
78
|
+
return component
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Try with leading slash
|
|
82
|
+
let withSlash = normalizedPath.hasPrefix("/")
|
|
83
|
+
? normalizedPath
|
|
84
|
+
: "/\(normalizedPath)"
|
|
85
|
+
if let component = pages[withSlash] {
|
|
86
|
+
return component
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return nil
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private func normalizePath(_ path: String) -> String {
|
|
93
|
+
return path.trimmingCharacters(in: .whitespaces).lowercased()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func getPageStyle(forPath path: String?) -> NebulaPageStyle? {
|
|
97
|
+
guard let path = path else {
|
|
98
|
+
return mergedPageStyle(forPath: entryPagePath ?? "/")
|
|
99
|
+
}
|
|
100
|
+
return mergedPageStyle(forPath: path)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private func mergedPageStyle(forPath path: String) -> NebulaPageStyle? {
|
|
104
|
+
let normalizedPath = normalizePath(path)
|
|
105
|
+
let withoutSlash = normalizedPath.hasPrefix("/")
|
|
106
|
+
? String(normalizedPath.dropFirst())
|
|
107
|
+
: normalizedPath
|
|
108
|
+
let withSlash = normalizedPath.hasPrefix("/")
|
|
109
|
+
? normalizedPath
|
|
110
|
+
: "/\(normalizedPath)"
|
|
111
|
+
let pageStyle = pageConfigs?[normalizedPath]
|
|
112
|
+
?? pageConfigs?[withoutSlash]
|
|
113
|
+
?? pageConfigs?[withSlash]
|
|
114
|
+
if let window {
|
|
115
|
+
return window.merged(with: pageStyle)
|
|
116
|
+
}
|
|
117
|
+
return pageStyle
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Manager for mini-app manifests
|
|
122
|
+
@objc public final class NebulaManifestManager: NSObject {
|
|
123
|
+
|
|
124
|
+
// MARK: - Singleton
|
|
125
|
+
|
|
126
|
+
@objc public static let shared = NebulaManifestManager()
|
|
127
|
+
|
|
128
|
+
// MARK: - Properties
|
|
129
|
+
|
|
130
|
+
private var manifests: [String: NebulaManifest] = [:]
|
|
131
|
+
private let queue = DispatchQueue(label: "com.nebula.manifest", attributes: .concurrent)
|
|
132
|
+
|
|
133
|
+
private override init() {
|
|
134
|
+
super.init()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// MARK: - Public Methods
|
|
138
|
+
|
|
139
|
+
/// Load manifest for an app from its sandbox directory
|
|
140
|
+
@objc public func loadManifest(forAppId appId: String, sandboxPath: String) -> Bool {
|
|
141
|
+
let manifestPath = (sandboxPath as NSString).appendingPathComponent("app.json")
|
|
142
|
+
|
|
143
|
+
guard FileManager.default.fileExists(atPath: manifestPath) else {
|
|
144
|
+
print("[Nebula] No app.json found for \(appId) at \(manifestPath)")
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
do {
|
|
149
|
+
let data = try Data(contentsOf: URL(fileURLWithPath: manifestPath))
|
|
150
|
+
let decoder = JSONDecoder()
|
|
151
|
+
let manifest = try decoder.decode(NebulaManifest.self, from: data)
|
|
152
|
+
|
|
153
|
+
queue.async(flags: .barrier) { [weak self] in
|
|
154
|
+
self?.manifests[appId] = manifest
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
print("[Nebula] Loaded manifest for \(appId): \(manifest.pages.count) pages")
|
|
158
|
+
return true
|
|
159
|
+
} catch {
|
|
160
|
+
print("[Nebula] Failed to load manifest for \(appId): \(error)")
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Get component name for a route path in a specific app
|
|
166
|
+
@objc public func getComponentName(forAppId appId: String, path: String) -> String? {
|
|
167
|
+
var result: String?
|
|
168
|
+
queue.sync {
|
|
169
|
+
result = manifests[appId]?.getComponentName(forPath: path)
|
|
170
|
+
}
|
|
171
|
+
return result
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// Register manifest directly from a dictionary (called by mini-app JS at startup)
|
|
175
|
+
@objc public func registerManifest(forAppId appId: String, pages: [String: String]) {
|
|
176
|
+
let manifest = NebulaManifest(entryPagePath: "/", pages: pages, pageConfigs: nil, updateStrategy: "manual", version: nil, window: nil)
|
|
177
|
+
queue.async(flags: .barrier) { [weak self] in
|
|
178
|
+
self?.manifests[appId] = manifest
|
|
179
|
+
}
|
|
180
|
+
print("[Nebula] Registered manifest for \(appId): \(pages.count) routes")
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
func registerManifest(forAppId appId: String, manifest: NebulaManifest) {
|
|
184
|
+
queue.async(flags: .barrier) { [weak self] in
|
|
185
|
+
self?.manifests[appId] = manifest
|
|
186
|
+
}
|
|
187
|
+
print("[Nebula] Registered manifest for \(appId): \(manifest.pages.count) routes")
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@objc public func getEntryPagePath(forAppId appId: String) -> String? {
|
|
191
|
+
var result: String?
|
|
192
|
+
queue.sync {
|
|
193
|
+
result = manifests[appId]?.entryPagePath
|
|
194
|
+
}
|
|
195
|
+
return result
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@objc public func getPageConfig(forAppId appId: String, path: String?) -> NSDictionary? {
|
|
199
|
+
var result: [String: Any]?
|
|
200
|
+
queue.sync {
|
|
201
|
+
result = manifests[appId]?.getPageStyle(forPath: path)?.asDictionary()
|
|
202
|
+
}
|
|
203
|
+
guard let result else { return nil }
|
|
204
|
+
return result as NSDictionary
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Remove manifest when app is uninstalled
|
|
208
|
+
@objc public func removeManifest(forAppId appId: String) {
|
|
209
|
+
queue.async(flags: .barrier) { [weak self] in
|
|
210
|
+
self?.manifests.removeValue(forKey: appId)
|
|
211
|
+
}
|
|
212
|
+
print("[Nebula] Removed manifest for \(appId)")
|
|
213
|
+
}
|
|
214
|
+
}
|