@nebula-rn/sdk 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.
@@ -0,0 +1,691 @@
1
+ import React from 'react';
2
+ import { Platform } from 'react-native';
3
+ import type { NebulaHostFeature } from './hostApi';
4
+ import {
5
+ configureMiniappLoadingComponent,
6
+ ensureInternalMiniappLoadingComponentRegistered,
7
+ getMiniappLoadingDelayMs,
8
+ hideMiniappLoading,
9
+ setMiniappLoadingState,
10
+ } from './miniappLoading';
11
+ import { Miniapp } from './miniappRuntime';
12
+ import { getNebulaNativeModule } from './nebulaNative';
13
+ import {
14
+ registerHostApiHandler,
15
+ resolveHostApiDescriptions,
16
+ resolveHostCapabilities,
17
+ startHostApiServer,
18
+ stopHostApiServer,
19
+ unregisterHostApiHandler,
20
+ } from './hostProtocol';
21
+ import type {
22
+ HostVisibilityResult,
23
+ InstalledMiniAppInfoResult,
24
+ InstalledMiniAppsResult,
25
+ MiniAppResult,
26
+ MiniAppUpdateInfo,
27
+ MiniAppVersionType,
28
+ MiniappLoadingDelayResult,
29
+ MiniappLoadingProps,
30
+ MiniappLoadingResolveContext,
31
+ MiniappLoadingResolvedProps,
32
+ NavigationResult,
33
+ NebulaHostApiDescriptionMap,
34
+ NebulaHostApiHandler,
35
+ NebulaHostCapabilityMap,
36
+ RegisteredMiniAppManifest,
37
+ ServerBaseURLResult,
38
+ } from './nebulaTypes';
39
+
40
+ export * from './nebulaTypes';
41
+ export { Miniapp } from './miniappRuntime';
42
+ export {
43
+ createMiniAppPage,
44
+ usePageOnHide,
45
+ usePageOnLoad,
46
+ usePageOnReady,
47
+ usePageOnShow,
48
+ usePageOnUnload,
49
+ } from './miniappPage';
50
+
51
+ type NebulaHostOptions = {
52
+ hostApis?: NebulaHostFeature[];
53
+ miniappLoading?: {
54
+ component: React.ComponentType<MiniappLoadingProps>;
55
+ delayMs?: number;
56
+ enterContentDelayMs?: number;
57
+ resolveProps?: (
58
+ context: MiniappLoadingResolveContext,
59
+ ) => MiniappLoadingResolvedProps;
60
+ };
61
+ serverBaseURL?: string | null;
62
+ };
63
+
64
+ type MiniAppInstallPayload = {
65
+ appId: string;
66
+ appName?: string;
67
+ versionId?: string;
68
+ version?: string;
69
+ bundleUrl?: string | null;
70
+ bundles?: {
71
+ ios?: string;
72
+ android?: string;
73
+ } | null;
74
+ assetsUrl: {
75
+ ios: string;
76
+ android: string;
77
+ };
78
+ manifestUrl: string;
79
+ };
80
+
81
+ function compareMiniAppVersions(
82
+ left?: string | null,
83
+ right?: string | null,
84
+ ): number {
85
+ if (!left && !right) {
86
+ return 0;
87
+ }
88
+ if (!left) {
89
+ return -1;
90
+ }
91
+ if (!right) {
92
+ return 1;
93
+ }
94
+ return compareCapabilityVersions(left, right);
95
+ }
96
+
97
+ function compareCapabilityVersions(left: string, right: string): number {
98
+ const leftParts = left.split('.').map(part => Number(part) || 0);
99
+ const rightParts = right.split('.').map(part => Number(part) || 0);
100
+ const maxLength = Math.max(leftParts.length, rightParts.length);
101
+
102
+ for (let index = 0; index < maxLength; index += 1) {
103
+ const leftPart = leftParts[index] ?? 0;
104
+ const rightPart = rightParts[index] ?? 0;
105
+ if (leftPart > rightPart) {
106
+ return 1;
107
+ }
108
+ if (leftPart < rightPart) {
109
+ return -1;
110
+ }
111
+ }
112
+
113
+ return 0;
114
+ }
115
+
116
+ function normalizeUpdateStrategy(strategy?: string | null): 'auto' | 'manual' {
117
+ return strategy === 'auto' ? 'auto' : 'manual';
118
+ }
119
+
120
+ function isDevelopmentMiniAppMode(mode?: string | null): boolean {
121
+ return mode?.toLowerCase() === 'development';
122
+ }
123
+
124
+ function canCheckForRemoteUpdate(
125
+ sourceUrl?: string | null,
126
+ mode?: string | null,
127
+ ) {
128
+ if (!sourceUrl) {
129
+ return false;
130
+ }
131
+ if (isDevelopmentMiniAppMode(mode)) {
132
+ return false;
133
+ }
134
+ return /^https?:\/\//i.test(sourceUrl);
135
+ }
136
+
137
+ function deriveMiniAppManifestUrl(sourceUrl: string): string | null {
138
+ try {
139
+ const match = sourceUrl.match(
140
+ /^(https?:\/\/[^/?#]+)(\/[^?#]*)?(\?[^#]*)?(#.*)?$/i,
141
+ );
142
+ if (!match) {
143
+ return null;
144
+ }
145
+ const origin = match[1];
146
+ const path = match[2] || '';
147
+ const parentPath = path.includes('/')
148
+ ? path.slice(0, path.lastIndexOf('/'))
149
+ : '';
150
+ const manifestPath = `${parentPath}/app.json`.replace(/\/{2,}/g, '/');
151
+ return `${origin}${manifestPath.startsWith('/') ? manifestPath : `/${manifestPath}`}`;
152
+ } catch {
153
+ return null;
154
+ }
155
+ }
156
+
157
+ function joinApiUrl(baseUrl: string, path: string): string {
158
+ return `${baseUrl.replace(/\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;
159
+ }
160
+
161
+ function normalizeDevelopmentBundleURL(inputURL: string): string {
162
+ const trimmedURL = inputURL.trim();
163
+ if (!trimmedURL) {
164
+ return trimmedURL;
165
+ }
166
+
167
+ if (trimmedURL.match(/\.nebula\/generated/)) {
168
+ return trimmedURL;
169
+ }
170
+
171
+ try {
172
+ const parsed = new URL(trimmedURL);
173
+
174
+ if (parsed.pathname === '/') {
175
+ return `${trimmedURL}/.nebula/generated/index.bundle?platform=${Platform.OS}&dev=true&minify=false`;
176
+ }
177
+
178
+ return trimmedURL;
179
+ } catch {
180
+ return trimmedURL;
181
+ }
182
+ }
183
+
184
+ function resolveBundleUrlForPlatform(
185
+ payload: MiniAppInstallPayload,
186
+ ): string | null {
187
+ if (Platform.OS === 'android') {
188
+ return payload.bundles?.android ?? payload.bundleUrl ?? null;
189
+ }
190
+ return payload.bundles?.ios ?? payload.bundleUrl ?? null;
191
+ }
192
+
193
+ function resolveAssetsUrlForPlatform(payload: MiniAppInstallPayload): string {
194
+ if (Platform.OS === 'android') {
195
+ return payload.assetsUrl.android;
196
+ }
197
+ return payload.assetsUrl.ios;
198
+ }
199
+
200
+ export class NebulaAPI {
201
+ static wrap(options: NebulaHostOptions) {
202
+ configureMiniappLoadingComponent(
203
+ options.miniappLoading?.component ?? null,
204
+ options.miniappLoading?.resolveProps ?? null,
205
+ options.miniappLoading?.delayMs ?? 0,
206
+ );
207
+ ensureInternalMiniappLoadingComponentRegistered();
208
+
209
+ return function wrapNebulaHost<P extends object>(
210
+ AppComponent: React.ComponentType<P>,
211
+ ) {
212
+ function NebulaWrappedApp(props: P) {
213
+ React.useEffect(() => {
214
+ configureMiniappLoadingComponent(
215
+ options.miniappLoading?.component ?? null,
216
+ options.miniappLoading?.resolveProps ?? null,
217
+ options.miniappLoading?.delayMs ?? 0,
218
+ );
219
+ NebulaAPI.startApiServer();
220
+ const unregisterCallbacks =
221
+ options.hostApis
222
+ ?.map(feature => feature.register())
223
+ .filter(Boolean) ?? [];
224
+ const unsubscribePageLifecycle = Miniapp.onPageLifecycle(event => {
225
+ if (event.type === 'ready') {
226
+ const delayMs = getMiniappLoadingDelayMs();
227
+ if (delayMs > 0) {
228
+ setTimeout(() => hideMiniappLoading(event.appId), delayMs);
229
+ } else {
230
+ hideMiniappLoading(event.appId);
231
+ }
232
+ }
233
+ });
234
+
235
+ NebulaAPI.setMiniappLoadingDelay(
236
+ options.miniappLoading?.delayMs ?? 0,
237
+ ).catch(error => {
238
+ console.error(
239
+ '[Nebula] Failed to apply miniapp loading delay',
240
+ error,
241
+ );
242
+ });
243
+ NebulaAPI.setMiniappLoadingEnterContentDelay(
244
+ options.miniappLoading?.enterContentDelayMs ?? 0,
245
+ ).catch(error => {
246
+ console.error(
247
+ '[Nebula] Failed to apply miniapp content enter delay',
248
+ error,
249
+ );
250
+ });
251
+
252
+ if (typeof options.serverBaseURL !== 'undefined') {
253
+ NebulaAPI.setServerBaseURL(options.serverBaseURL).catch(error => {
254
+ console.error(
255
+ '[Nebula] Failed to apply host configuration',
256
+ error,
257
+ );
258
+ });
259
+ }
260
+
261
+ return () => {
262
+ unsubscribePageLifecycle();
263
+ unregisterCallbacks.forEach(unregister => {
264
+ if (typeof unregister === 'function') {
265
+ unregister();
266
+ }
267
+ });
268
+ };
269
+ }, []);
270
+
271
+ return React.createElement(AppComponent, props);
272
+ }
273
+
274
+ NebulaWrappedApp.displayName = `NebulaWrapped(${AppComponent.displayName ?? AppComponent.name ?? 'App'})`;
275
+ return NebulaWrappedApp;
276
+ };
277
+ }
278
+
279
+ static async registerRoutes(
280
+ appId: string,
281
+ routes: Record<string, string>,
282
+ ): Promise<void> {
283
+ return this.registerManifest(appId, { pages: routes });
284
+ }
285
+
286
+ static async registerManifest(
287
+ appId: string,
288
+ manifest: RegisteredMiniAppManifest,
289
+ ): Promise<void> {
290
+ const nativeModule = getNebulaNativeModule();
291
+ try {
292
+ await nativeModule.registerManifest(appId, manifest);
293
+ } catch (e) {
294
+ console.warn(`[Nebula] registerManifest failed for ${appId}:`, e);
295
+ }
296
+ }
297
+
298
+ static async openMiniApp(
299
+ appId: string,
300
+ initialProps: Record<string, unknown> = {},
301
+ versionType: MiniAppVersionType = 'release',
302
+ ): Promise<MiniAppResult> {
303
+ const nativeModule = getNebulaNativeModule();
304
+ try {
305
+ let installedInfo = await this.getInstalledMiniAppInfo(appId);
306
+ if (!installedInfo.installed) {
307
+ const serverBaseURLResult = await this.getServerBaseURL();
308
+ const serverBaseURL = serverBaseURLResult.serverBaseURL?.trim();
309
+ if (!serverBaseURL) {
310
+ throw new Error(
311
+ `Miniapp ${appId} is not installed and no serverBaseURL is configured for auto-install.`,
312
+ );
313
+ }
314
+
315
+ setMiniappLoadingState({
316
+ appId,
317
+ mode: 'production',
318
+ status: 'installing',
319
+ title: appId,
320
+ icon: null,
321
+ extraData: null,
322
+ installedInfo: null,
323
+ errorMessage: null,
324
+ visible: true,
325
+ });
326
+
327
+ const installPayloadResponse = await fetch(
328
+ joinApiUrl(
329
+ serverBaseURL,
330
+ `/mini-apps/access/apps/${encodeURIComponent(appId)}/${encodeURIComponent(versionType)}/install`,
331
+ ),
332
+ );
333
+ if (!installPayloadResponse.ok) {
334
+ throw new Error(
335
+ `Failed to fetch ${versionType} install payload for ${appId}: ${installPayloadResponse.status}`,
336
+ );
337
+ }
338
+ const installPayload =
339
+ (await installPayloadResponse.json()) as MiniAppInstallPayload;
340
+ const bundleURL = resolveBundleUrlForPlatform(installPayload);
341
+ if (!bundleURL) {
342
+ throw new Error(
343
+ `No bundle URL available for platform ${Platform.OS}`,
344
+ );
345
+ }
346
+
347
+ const assetsURL = resolveAssetsUrlForPlatform(installPayload);
348
+
349
+ await nativeModule.installMiniAppFromURLs(
350
+ appId,
351
+ bundleURL,
352
+ installPayload.manifestUrl,
353
+ assetsURL,
354
+ false,
355
+ );
356
+ installedInfo = await this.getInstalledMiniAppInfo(appId);
357
+ }
358
+ const nextMode =
359
+ installedInfo.app?.mode?.toLowerCase() === 'development'
360
+ ? 'development'
361
+ : 'production';
362
+ if (!isDevelopmentMiniAppMode(installedInfo.app?.mode)) {
363
+ setMiniappLoadingState({
364
+ appId,
365
+ mode: nextMode,
366
+ status: 'loading',
367
+ title: appId,
368
+ icon: null,
369
+ extraData: null,
370
+ installedInfo: installedInfo.app ?? null,
371
+ errorMessage: null,
372
+ visible: true,
373
+ });
374
+ }
375
+ if (
376
+ installedInfo.installed &&
377
+ !isDevelopmentMiniAppMode(installedInfo.app?.mode) &&
378
+ normalizeUpdateStrategy(installedInfo.app?.updateStrategy) === 'auto'
379
+ ) {
380
+ await this.applyMiniAppUpdate(appId).catch(error => {
381
+ console.warn(
382
+ `[Nebula] Failed to auto-update mini-app before open: ${appId}`,
383
+ error,
384
+ );
385
+ });
386
+ }
387
+ const result = await nativeModule.openMiniApp(appId, initialProps);
388
+ console.log(`[Nebula] Opened mini-app: ${appId}`, result);
389
+ return result;
390
+ } catch (error) {
391
+ hideMiniappLoading(appId);
392
+ console.error(`[Nebula] Failed to open mini-app: ${appId}`, error);
393
+ throw error;
394
+ }
395
+ }
396
+
397
+ static async preloadMiniApp(appId: string): Promise<MiniAppResult> {
398
+ const result = await getNebulaNativeModule().preloadMiniApp(appId);
399
+ console.log(`[Nebula] Preloaded mini-app: ${appId}`, result);
400
+ return result;
401
+ }
402
+
403
+ static async preloadMiniAppWithBundleURL(
404
+ appId: string,
405
+ bundleURL: string,
406
+ connectToURLMetroServer = false,
407
+ ): Promise<MiniAppResult> {
408
+ const resolvedBundleURL = connectToURLMetroServer
409
+ ? normalizeDevelopmentBundleURL(bundleURL)
410
+ : bundleURL;
411
+ const result = await getNebulaNativeModule().preloadMiniAppWithBundleURL(
412
+ appId,
413
+ resolvedBundleURL,
414
+ connectToURLMetroServer,
415
+ );
416
+ console.log(
417
+ `[Nebula] Preloaded mini-app: ${appId}, metro=${connectToURLMetroServer}`,
418
+ result,
419
+ );
420
+ return result;
421
+ }
422
+
423
+ static async installMiniApp(
424
+ appId: string,
425
+ bundleURL: string,
426
+ ): Promise<MiniAppResult> {
427
+ const result = await getNebulaNativeModule().installMiniApp(
428
+ appId,
429
+ bundleURL,
430
+ );
431
+ console.log(`[Nebula] Installed mini-app: ${appId}`, result);
432
+ return result;
433
+ }
434
+
435
+ static async installMiniAppWithBundleURL(
436
+ appId: string,
437
+ bundleURL: string,
438
+ connectToURLMetroServer = false,
439
+ ): Promise<MiniAppResult> {
440
+ const resolvedBundleURL = connectToURLMetroServer
441
+ ? normalizeDevelopmentBundleURL(bundleURL)
442
+ : bundleURL;
443
+ const result = await getNebulaNativeModule().installMiniAppWithBundleURL(
444
+ appId,
445
+ resolvedBundleURL,
446
+ connectToURLMetroServer,
447
+ );
448
+ console.log(
449
+ `[Nebula] Installed mini-app: ${appId}, metro=${connectToURLMetroServer}`,
450
+ result,
451
+ );
452
+ return result;
453
+ }
454
+
455
+ static async uninstallMiniApp(appId: string): Promise<MiniAppResult> {
456
+ const result = await getNebulaNativeModule().uninstallMiniApp(appId);
457
+ console.log(`[Nebula] Uninstalled mini-app: ${appId}`, result);
458
+ return result;
459
+ }
460
+
461
+ static async closeMiniApp(appId: string): Promise<MiniAppResult> {
462
+ hideMiniappLoading(appId);
463
+ const result = await getNebulaNativeModule().closeMiniApp(appId);
464
+ console.log(`[Nebula] Closed mini-app: ${appId}`, result);
465
+ return result;
466
+ }
467
+
468
+ static async openMiniAppWithBundleURL(
469
+ appId: string,
470
+ bundleURL: string,
471
+ initialProps: Record<string, unknown> = {},
472
+ connectToURLMetroServer = false,
473
+ ): Promise<MiniAppResult> {
474
+ const resolvedBundleURL = connectToURLMetroServer
475
+ ? normalizeDevelopmentBundleURL(bundleURL)
476
+ : bundleURL;
477
+ if (connectToURLMetroServer) {
478
+ hideMiniappLoading(appId);
479
+ } else {
480
+ setMiniappLoadingState({
481
+ appId,
482
+ mode: 'production',
483
+ status: 'installing',
484
+ title: appId,
485
+ icon: null,
486
+ extraData: null,
487
+ installedInfo: null,
488
+ errorMessage: null,
489
+ visible: true,
490
+ });
491
+ }
492
+ await this.installMiniAppWithBundleURL(
493
+ appId,
494
+ resolvedBundleURL,
495
+ connectToURLMetroServer,
496
+ );
497
+ return this.openMiniApp(appId, initialProps);
498
+ }
499
+
500
+ static async getInstalledMiniApps(): Promise<InstalledMiniAppsResult> {
501
+ const result = await getNebulaNativeModule().getInstalledMiniApps();
502
+ console.log('[Nebula] Installed mini-apps:', result);
503
+ return result;
504
+ }
505
+
506
+ static async getInstalledMiniAppInfo(
507
+ appId: string,
508
+ ): Promise<InstalledMiniAppInfoResult> {
509
+ return getNebulaNativeModule().getInstalledMiniAppInfo(appId);
510
+ }
511
+
512
+ static async checkMiniAppUpdate(appId: string): Promise<MiniAppUpdateInfo> {
513
+ const installedInfo = await this.getInstalledMiniAppInfo(appId);
514
+ const app = installedInfo.app;
515
+ const isDevelopmentMode = isDevelopmentMiniAppMode(app?.mode);
516
+ const updateStrategy = isDevelopmentMode
517
+ ? 'manual'
518
+ : normalizeUpdateStrategy(app?.updateStrategy);
519
+
520
+ if (!installedInfo.installed || !app) {
521
+ return {
522
+ appId,
523
+ currentVersion: null,
524
+ latestVersion: null,
525
+ hasUpdate: false,
526
+ updateStrategy,
527
+ };
528
+ }
529
+
530
+ if (!canCheckForRemoteUpdate(app.sourceUrl, app.mode)) {
531
+ return {
532
+ appId,
533
+ currentVersion: isDevelopmentMode ? null : (app.version ?? null),
534
+ latestVersion: isDevelopmentMode ? null : (app.version ?? null),
535
+ hasUpdate: false,
536
+ updateStrategy,
537
+ mode: app.mode ?? null,
538
+ sourceUrl: app.sourceUrl ?? null,
539
+ };
540
+ }
541
+
542
+ const manifestUrl = deriveMiniAppManifestUrl(app.sourceUrl as string);
543
+ if (!manifestUrl) {
544
+ return {
545
+ appId,
546
+ currentVersion: app.version ?? null,
547
+ latestVersion: null,
548
+ hasUpdate: false,
549
+ updateStrategy,
550
+ mode: app.mode ?? null,
551
+ sourceUrl: app.sourceUrl ?? null,
552
+ };
553
+ }
554
+
555
+ try {
556
+ const response = await fetch(manifestUrl);
557
+ if (!response.ok) {
558
+ throw new Error(`Failed to fetch update manifest: ${response.status}`);
559
+ }
560
+ const remoteManifest =
561
+ (await response.json()) as RegisteredMiniAppManifest & {
562
+ version?: string;
563
+ };
564
+ const latestVersion = remoteManifest.version ?? null;
565
+ const hasUpdate =
566
+ latestVersion != null &&
567
+ compareMiniAppVersions(latestVersion, app.version ?? null) > 0;
568
+
569
+ return {
570
+ appId,
571
+ currentVersion: app.version ?? null,
572
+ latestVersion,
573
+ hasUpdate,
574
+ updateStrategy,
575
+ mode: app.mode ?? null,
576
+ sourceUrl: app.sourceUrl ?? null,
577
+ };
578
+ } catch (error) {
579
+ console.warn(`[Nebula] Failed to check mini-app update: ${appId}`, error);
580
+ return {
581
+ appId,
582
+ currentVersion: app.version ?? null,
583
+ latestVersion: null,
584
+ hasUpdate: false,
585
+ updateStrategy,
586
+ mode: app.mode ?? null,
587
+ sourceUrl: app.sourceUrl ?? null,
588
+ };
589
+ }
590
+ }
591
+
592
+ static async applyMiniAppUpdate(appId: string): Promise<MiniAppUpdateInfo> {
593
+ const updateInfo = await this.checkMiniAppUpdate(appId);
594
+ if (!updateInfo.hasUpdate || !updateInfo.sourceUrl) {
595
+ return updateInfo;
596
+ }
597
+
598
+ await this.installMiniAppWithBundleURL(appId, updateInfo.sourceUrl, false);
599
+ return this.checkMiniAppUpdate(appId);
600
+ }
601
+
602
+ static async getServerBaseURL(): Promise<ServerBaseURLResult> {
603
+ return getNebulaNativeModule().getServerBaseURL();
604
+ }
605
+
606
+ static async setServerBaseURL(
607
+ serverBaseURL?: string | null,
608
+ ): Promise<ServerBaseURLResult> {
609
+ return getNebulaNativeModule().setServerBaseURL(serverBaseURL);
610
+ }
611
+
612
+ static async setMiniappLoadingDelay(
613
+ delayMs?: number | null,
614
+ ): Promise<MiniappLoadingDelayResult> {
615
+ return getNebulaNativeModule().setMiniappLoadingDelay(delayMs);
616
+ }
617
+
618
+ static async setMiniappLoadingEnterContentDelay(
619
+ delayMs?: number | null,
620
+ ): Promise<MiniappLoadingDelayResult> {
621
+ return getNebulaNativeModule().setMiniappLoadingEnterContentDelay(delayMs);
622
+ }
623
+
624
+ static async postMessageToMiniApp(
625
+ appId: string,
626
+ message: Record<string, unknown>,
627
+ ): Promise<NavigationResult> {
628
+ return getNebulaNativeModule().postMessageToMiniApp(appId, message);
629
+ }
630
+
631
+ static async bringHostToFront(): Promise<HostVisibilityResult> {
632
+ return getNebulaNativeModule().bringHostToFront();
633
+ }
634
+
635
+ static async restoreMiniApp(
636
+ token?: string | null,
637
+ ): Promise<HostVisibilityResult> {
638
+ return getNebulaNativeModule().restoreMiniApp(token);
639
+ }
640
+
641
+ static async presentHostModal(
642
+ moduleName: string,
643
+ props: Record<string, unknown> = {},
644
+ ): Promise<NavigationResult> {
645
+ return getNebulaNativeModule().presentHostModal(moduleName, props);
646
+ }
647
+
648
+ static async dismissHostModal(): Promise<NavigationResult> {
649
+ return getNebulaNativeModule().dismissHostModal();
650
+ }
651
+
652
+ static addMiniAppMessageListener(
653
+ listener: (event: {
654
+ appId: string;
655
+ message: Record<string, unknown>;
656
+ timestamp?: number;
657
+ }) => void,
658
+ ): () => void {
659
+ return Miniapp.onHostMessage(listener);
660
+ }
661
+
662
+ static startApiServer(): void {
663
+ startHostApiServer((appId, message) =>
664
+ this.postMessageToMiniApp(appId, message),
665
+ );
666
+ }
667
+
668
+ static stopApiServer(): void {
669
+ stopHostApiServer();
670
+ }
671
+
672
+ static registerApiHandler(
673
+ apiName: string,
674
+ handler: NebulaHostApiHandler,
675
+ ): void {
676
+ registerHostApiHandler(apiName, handler);
677
+ this.startApiServer();
678
+ }
679
+
680
+ static unregisterApiHandler(apiName: string): void {
681
+ unregisterHostApiHandler(apiName);
682
+ }
683
+
684
+ static async getRegisteredCapabilities(): Promise<NebulaHostCapabilityMap> {
685
+ return resolveHostCapabilities();
686
+ }
687
+
688
+ static getRegisteredApiDescriptions(): NebulaHostApiDescriptionMap {
689
+ return resolveHostApiDescriptions();
690
+ }
691
+ }