@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.
Files changed (37) hide show
  1. package/NebulaHost.podspec +23 -0
  2. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  4. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  5. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  6. package/android/.gradle/8.9/gc.properties +0 -0
  7. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  8. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  9. package/android/.gradle/vcs-1/gc.properties +0 -0
  10. package/android/build.gradle +27 -0
  11. package/android/consumer-rules.pro +1 -0
  12. package/android/src/main/AndroidManifest.xml +1 -0
  13. package/android/src/main/java/com/hectorzhuang/nebula/NebulaActivity.kt +290 -0
  14. package/android/src/main/java/com/hectorzhuang/nebula/NebulaAppManager.kt +134 -0
  15. package/android/src/main/java/com/hectorzhuang/nebula/NebulaConfig.kt +324 -0
  16. package/android/src/main/java/com/hectorzhuang/nebula/NebulaEventHub.kt +49 -0
  17. package/android/src/main/java/com/hectorzhuang/nebula/NebulaHost.kt +145 -0
  18. package/android/src/main/java/com/hectorzhuang/nebula/NebulaHostModalActivity.kt +178 -0
  19. package/android/src/main/java/com/hectorzhuang/nebula/NebulaManifestManager.kt +130 -0
  20. package/android/src/main/java/com/hectorzhuang/nebula/NebulaNativeModule.kt +604 -0
  21. package/android/src/main/java/com/hectorzhuang/nebula/NebulaPackage.kt +16 -0
  22. package/android/src/main/java/com/hectorzhuang/nebula/NebulaRouter.kt +300 -0
  23. package/ios/Nebula/NebulaAppManager.swift +355 -0
  24. package/ios/Nebula/NebulaConfig.swift +549 -0
  25. package/ios/Nebula/NebulaContainerController.swift +580 -0
  26. package/ios/Nebula/NebulaDevLoading.swift +333 -0
  27. package/ios/Nebula/NebulaHost.swift +611 -0
  28. package/ios/Nebula/NebulaManifest.swift +214 -0
  29. package/ios/Nebula/NebulaNativeModule.swift +682 -0
  30. package/ios/Nebula/NebulaNativeModuleBridge.m +364 -0
  31. package/ios/Nebula/NebulaPerformanceMonitor.swift +46 -0
  32. package/ios/Nebula/NebulaRouter.swift +594 -0
  33. package/ios/Nebula/NebulaRouterBridge.m +19 -0
  34. package/ios/Nebula/RNInstanceViewController.swift +52 -0
  35. package/package.json +41 -0
  36. package/react-native.config.js +14 -0
  37. 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
+ }