From fd2a3e2c5fe805428881a07a2efdf43d11456ef0 Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 13:08:17 +0800 Subject: [PATCH 1/9] docs(openspec): add miniapp context sdk proposal --- .../changes/add-miniapp-context-sdk/design.md | 44 +++++++++++++++++++ .../add-miniapp-context-sdk/proposal.md | 15 +++++++ .../specs/miniapp-runtime/spec.md | 40 +++++++++++++++++ .../changes/add-miniapp-context-sdk/tasks.md | 20 +++++++++ 4 files changed, 119 insertions(+) create mode 100644 openspec/changes/add-miniapp-context-sdk/design.md create mode 100644 openspec/changes/add-miniapp-context-sdk/proposal.md create mode 100644 openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md create mode 100644 openspec/changes/add-miniapp-context-sdk/tasks.md diff --git a/openspec/changes/add-miniapp-context-sdk/design.md b/openspec/changes/add-miniapp-context-sdk/design.md new file mode 100644 index 000000000..e82690d05 --- /dev/null +++ b/openspec/changes/add-miniapp-context-sdk/design.md @@ -0,0 +1,44 @@ +# Design: Miniapp context SDK + +## Context Payload +```ts +export type MiniappContext = { + version: number; + env: { + safeAreaInsets: { + top: number; + right: number; + bottom: number; + left: number; + }; + platform?: "ios" | "android" | "web" | "dweb"; + locale?: string; + }; + host: { + name: "KeyApp"; + version: string; + build?: string; + }; + updatedAt: string; +}; +``` + +## Message Contract +- Request: `{ type: "miniapp:context-request", requestId, sdkVersion }` +- Response: `{ type: "keyapp:context-update", requestId, payload }` +- Errors use `{ type: "keyapp:context-error", requestId, code, message }` + +## SDK Behavior +- `initMiniapp()` creates a single message bridge, subscribes to updates, and triggers refresh if no cached context is available. +- `getContext()` resolves cached context; if missing, it waits for refresh with timeout + retry (configurable defaults). +- `onContextUpdate()` registers handler, replays latest cached context once per subscriber, returns unsubscribe. +- `requestContextRefresh()` sends a request and resolves with the next context update. +- If host does not support the channel (timeout), SDK resolves with default context values and logs a warning. + +## Host Behavior +- Host maintains a context snapshot (safeAreaInsets, env info, version) and updates it on layout changes. +- On `miniapp:context-request`, respond with `keyapp:context-update` (including `requestId`). +- When context changes, broadcast `keyapp:context-update` to active miniapp frames. + +## CSS Variable Hook +- Provide helper `applySafeAreaCssVars(context)` or emit event `contextupdate` for UI to set variables like `--f7-safe-area-top`. diff --git a/openspec/changes/add-miniapp-context-sdk/proposal.md b/openspec/changes/add-miniapp-context-sdk/proposal.md new file mode 100644 index 000000000..2b758453d --- /dev/null +++ b/openspec/changes/add-miniapp-context-sdk/proposal.md @@ -0,0 +1,15 @@ +# Change: Add miniapp context SDK and host channel + +## Why +Miniapps need safe area and host context without directly using postMessage. A normalized SDK plus host-side channel prevents fragile integrations and provides backward-compatible defaults. + +## What Changes +- Define a schema-first miniapp context payload (safeAreaInsets, env info, host version). +- Add a host-side context channel that responds to `miniapp:context-request` with `keyapp:context-update`. +- Provide a miniapp SDK wrapper that handles init, refresh, subscription, retry, timeout, and caching. +- Ensure iframe mode automatically replays context on subscription and on-demand refresh. +- Document the new SDK usage and context structure. + +## Impact +- Affected specs: miniapp-runtime (new) +- Affected code: miniapp runtime messaging, keyapp-sdk, docs/white-book diff --git a/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md b/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md new file mode 100644 index 000000000..ab7b64c28 --- /dev/null +++ b/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md @@ -0,0 +1,40 @@ +## ADDED Requirements + +### Requirement: Miniapp context channel +The host SHALL accept `miniapp:context-request` messages and respond with `keyapp:context-update` containing the latest context payload and the originating `requestId`. + +#### Scenario: Context request success +- **WHEN** a miniapp sends `miniapp:context-request` +- **THEN** the host responds with `keyapp:context-update` within the configured timeout +- **AND** the payload includes safe area insets and host version information + +### Requirement: Context update broadcast +The host SHALL broadcast `keyapp:context-update` whenever the context changes (safe area, environment, host version) so running miniapps can react without polling. + +#### Scenario: Safe area change +- **WHEN** the host safe area insets change +- **THEN** all active miniapps receive a `keyapp:context-update` event + +### Requirement: SDK initialization and caching +The SDK SHALL provide `initMiniapp`, `getContext`, `onContextUpdate`, and `requestContextRefresh` APIs, cache the latest context, and avoid duplicate event bindings. + +#### Scenario: Cached context replay +- **WHEN** a consumer calls `onContextUpdate` +- **THEN** the SDK replays the latest cached context once +- **AND** returns an unsubscribe function + +### Requirement: Automatic refresh on enter +The SDK SHALL request context once when initialized if no cached context is available. + +#### Scenario: First load without cached context +- **WHEN** the miniapp loads with no cached context +- **THEN** the SDK sends a `miniapp:context-request` +- **AND** resolves `initMiniapp()` with the next `keyapp:context-update` + +### Requirement: Compatibility fallback +When the host does not support the context channel, the SDK SHALL resolve with default values (safeAreaInsets = 0) and emit a warning. + +#### Scenario: Unsupported host +- **WHEN** the SDK times out waiting for a `keyapp:context-update` +- **THEN** it resolves with default context values +- **AND** logs a warning about unsupported host capability diff --git a/openspec/changes/add-miniapp-context-sdk/tasks.md b/openspec/changes/add-miniapp-context-sdk/tasks.md new file mode 100644 index 000000000..eb1ce49a6 --- /dev/null +++ b/openspec/changes/add-miniapp-context-sdk/tasks.md @@ -0,0 +1,20 @@ +## 1. Schema & Protocol +- [ ] Define Zod schema for `MiniappContext` and message envelope. +- [ ] Add typed message channel constants and payload types. + +## 2. Host Integration +- [ ] Add context provider in miniapp runtime (safeAreaInsets, env, host version). +- [ ] Handle `miniapp:context-request` and emit `keyapp:context-update`. +- [ ] Broadcast updates on safe area/layout changes. + +## 3. SDK Implementation +- [ ] Add `initMiniapp`, `getContext`, `onContextUpdate`, `requestContextRefresh` APIs. +- [ ] Implement retry/timeout + default fallback when unsupported. +- [ ] Cache context and avoid duplicate event bindings. + +## 4. UI/CSS Bridge +- [ ] Provide helper or event guidance for applying safe area CSS variables. + +## 5. Documentation & Tests +- [ ] Update white-book/SDK docs with usage examples. +- [ ] Add unit tests for SDK message flow and fallback. From 21ecafea0b5ac5dfeb0110fff46a3fb136fb29ed Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 13:18:14 +0800 Subject: [PATCH 2/9] docs(openspec): simplify miniapp context sdk api --- .../changes/add-miniapp-context-sdk/design.md | 21 +++++++++++++++---- .../add-miniapp-context-sdk/proposal.md | 2 +- .../specs/miniapp-runtime/spec.md | 10 ++++----- .../changes/add-miniapp-context-sdk/tasks.md | 2 +- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/openspec/changes/add-miniapp-context-sdk/design.md b/openspec/changes/add-miniapp-context-sdk/design.md index e82690d05..1c56113af 100644 --- a/openspec/changes/add-miniapp-context-sdk/design.md +++ b/openspec/changes/add-miniapp-context-sdk/design.md @@ -28,11 +28,24 @@ export type MiniappContext = { - Response: `{ type: "keyapp:context-update", requestId, payload }` - Errors use `{ type: "keyapp:context-error", requestId, code, message }` +## SDK API (Atomic) +```ts +export async function getMiniappContext(options?: { + forceRefresh?: boolean; + timeoutMs?: number; + retries?: number; +}): Promise; + +export function onMiniappContextUpdate( + handler: (context: MiniappContext) => void, + options?: { emitCached?: boolean }, +): () => void; +``` + ## SDK Behavior -- `initMiniapp()` creates a single message bridge, subscribes to updates, and triggers refresh if no cached context is available. -- `getContext()` resolves cached context; if missing, it waits for refresh with timeout + retry (configurable defaults). -- `onContextUpdate()` registers handler, replays latest cached context once per subscriber, returns unsubscribe. -- `requestContextRefresh()` sends a request and resolves with the next context update. +- `getMiniappContext()` returns cached context when available; if missing (or `forceRefresh`), it sends `miniapp:context-request` and waits for `keyapp:context-update`. +- `onMiniappContextUpdate()` registers a handler, replays cached context once (default), and ensures a refresh if nothing is cached. +- SDK uses a singleton message bridge to avoid duplicate event bindings. - If host does not support the channel (timeout), SDK resolves with default context values and logs a warning. ## Host Behavior diff --git a/openspec/changes/add-miniapp-context-sdk/proposal.md b/openspec/changes/add-miniapp-context-sdk/proposal.md index 2b758453d..88ab315bf 100644 --- a/openspec/changes/add-miniapp-context-sdk/proposal.md +++ b/openspec/changes/add-miniapp-context-sdk/proposal.md @@ -6,7 +6,7 @@ Miniapps need safe area and host context without directly using postMessage. A n ## What Changes - Define a schema-first miniapp context payload (safeAreaInsets, env info, host version). - Add a host-side context channel that responds to `miniapp:context-request` with `keyapp:context-update`. -- Provide a miniapp SDK wrapper that handles init, refresh, subscription, retry, timeout, and caching. +- Provide a miniapp SDK wrapper with atomic APIs (`getMiniappContext`, `onMiniappContextUpdate`) that handle refresh, retry, timeout, and caching. - Ensure iframe mode automatically replays context on subscription and on-demand refresh. - Document the new SDK usage and context structure. diff --git a/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md b/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md index ab7b64c28..19baae681 100644 --- a/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md +++ b/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md @@ -15,21 +15,21 @@ The host SHALL broadcast `keyapp:context-update` whenever the context changes (s - **WHEN** the host safe area insets change - **THEN** all active miniapps receive a `keyapp:context-update` event -### Requirement: SDK initialization and caching -The SDK SHALL provide `initMiniapp`, `getContext`, `onContextUpdate`, and `requestContextRefresh` APIs, cache the latest context, and avoid duplicate event bindings. +### Requirement: SDK access and caching +The SDK SHALL provide `getMiniappContext` and `onMiniappContextUpdate` APIs, cache the latest context, and avoid duplicate event bindings. #### Scenario: Cached context replay -- **WHEN** a consumer calls `onContextUpdate` +- **WHEN** a consumer calls `onMiniappContextUpdate` - **THEN** the SDK replays the latest cached context once - **AND** returns an unsubscribe function ### Requirement: Automatic refresh on enter -The SDK SHALL request context once when initialized if no cached context is available. +The SDK SHALL request context once when accessed if no cached context is available. #### Scenario: First load without cached context - **WHEN** the miniapp loads with no cached context - **THEN** the SDK sends a `miniapp:context-request` -- **AND** resolves `initMiniapp()` with the next `keyapp:context-update` +- **AND** resolves `getMiniappContext()` with the next `keyapp:context-update` ### Requirement: Compatibility fallback When the host does not support the context channel, the SDK SHALL resolve with default values (safeAreaInsets = 0) and emit a warning. diff --git a/openspec/changes/add-miniapp-context-sdk/tasks.md b/openspec/changes/add-miniapp-context-sdk/tasks.md index eb1ce49a6..32411dbea 100644 --- a/openspec/changes/add-miniapp-context-sdk/tasks.md +++ b/openspec/changes/add-miniapp-context-sdk/tasks.md @@ -8,7 +8,7 @@ - [ ] Broadcast updates on safe area/layout changes. ## 3. SDK Implementation -- [ ] Add `initMiniapp`, `getContext`, `onContextUpdate`, `requestContextRefresh` APIs. +- [ ] Add `getMiniappContext` + `onMiniappContextUpdate` APIs with optional `forceRefresh`. - [ ] Implement retry/timeout + default fallback when unsupported. - [ ] Cache context and avoid duplicate event bindings. From dfa293f189bc5bb6968a2eb7916038f41d3610d1 Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 13:28:44 +0800 Subject: [PATCH 3/9] docs(openspec): detail safe area css helper --- .../changes/add-miniapp-context-sdk/design.md | 26 ++++++++++++++++++- .../changes/add-miniapp-context-sdk/tasks.md | 2 +- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/openspec/changes/add-miniapp-context-sdk/design.md b/openspec/changes/add-miniapp-context-sdk/design.md index 1c56113af..542aa17bf 100644 --- a/openspec/changes/add-miniapp-context-sdk/design.md +++ b/openspec/changes/add-miniapp-context-sdk/design.md @@ -54,4 +54,28 @@ export function onMiniappContextUpdate( - When context changes, broadcast `keyapp:context-update` to active miniapp frames. ## CSS Variable Hook -- Provide helper `applySafeAreaCssVars(context)` or emit event `contextupdate` for UI to set variables like `--f7-safe-area-top`. +Provide a small helper in the SDK so UI can apply safe-area CSS variables without manual mapping. + +```ts +export function applyMiniappSafeAreaCssVars( + context: MiniappContext, + options?: { + target?: HTMLElement | Document; + includeFramework7Vars?: boolean; + }, +): void; +``` + +Behavior: +- Default target: `document.documentElement`. +- Always sets: + - `--keyapp-safe-area-top` + - `--keyapp-safe-area-right` + - `--keyapp-safe-area-bottom` + - `--keyapp-safe-area-left` +- When `includeFramework7Vars !== false`, also set: + - `--f7-safe-area-top` + - `--f7-safe-area-right` + - `--f7-safe-area-bottom` + - `--f7-safe-area-left` +- Values are `${number}px`, derived from `context.env.safeAreaInsets`. diff --git a/openspec/changes/add-miniapp-context-sdk/tasks.md b/openspec/changes/add-miniapp-context-sdk/tasks.md index 32411dbea..a6096d3b7 100644 --- a/openspec/changes/add-miniapp-context-sdk/tasks.md +++ b/openspec/changes/add-miniapp-context-sdk/tasks.md @@ -13,7 +13,7 @@ - [ ] Cache context and avoid duplicate event bindings. ## 4. UI/CSS Bridge -- [ ] Provide helper or event guidance for applying safe area CSS variables. +- [ ] Provide `applyMiniappSafeAreaCssVars` helper and usage guidance for safe area CSS variables. ## 5. Documentation & Tests - [ ] Update white-book/SDK docs with usage examples. From 10e4112b4a8e9b4f3503a5197af51ba2591bbb82 Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 13:31:11 +0800 Subject: [PATCH 4/9] docs(openspec): remove framework7 safe area vars --- openspec/changes/add-miniapp-context-sdk/design.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openspec/changes/add-miniapp-context-sdk/design.md b/openspec/changes/add-miniapp-context-sdk/design.md index 542aa17bf..c3e40dcbb 100644 --- a/openspec/changes/add-miniapp-context-sdk/design.md +++ b/openspec/changes/add-miniapp-context-sdk/design.md @@ -61,7 +61,6 @@ export function applyMiniappSafeAreaCssVars( context: MiniappContext, options?: { target?: HTMLElement | Document; - includeFramework7Vars?: boolean; }, ): void; ``` @@ -73,9 +72,4 @@ Behavior: - `--keyapp-safe-area-right` - `--keyapp-safe-area-bottom` - `--keyapp-safe-area-left` -- When `includeFramework7Vars !== false`, also set: - - `--f7-safe-area-top` - - `--f7-safe-area-right` - - `--f7-safe-area-bottom` - - `--f7-safe-area-left` - Values are `${number}px`, derived from `context.env.safeAreaInsets`. From 3b72dab2e25cf3d44b10bbaaef09aad2f741d362 Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 13:34:17 +0800 Subject: [PATCH 5/9] docs(openspec): clarify sdk distribution --- openspec/changes/add-miniapp-context-sdk/design.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openspec/changes/add-miniapp-context-sdk/design.md b/openspec/changes/add-miniapp-context-sdk/design.md index c3e40dcbb..949d9125a 100644 --- a/openspec/changes/add-miniapp-context-sdk/design.md +++ b/openspec/changes/add-miniapp-context-sdk/design.md @@ -42,6 +42,10 @@ export function onMiniappContextUpdate( ): () => void; ``` +## Distribution +- Exposed from `@biochain/bio-sdk` as named exports to keep a single public package. +- APIs are side-effect-free; existing provider auto-init behavior remains unchanged. + ## SDK Behavior - `getMiniappContext()` returns cached context when available; if missing (or `forceRefresh`), it sends `miniapp:context-request` and waits for `keyapp:context-update`. - `onMiniappContextUpdate()` registers a handler, replays cached context once (default), and ensures a refresh if nothing is cached. From 7d16e6d710f50802c191f674dfc5c7acd4dc476e Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 14:17:34 +0800 Subject: [PATCH 6/9] feat(miniapp): add context sdk --- .../01-Bio-SDK-Communication.md | 25 ++ .../changes/add-miniapp-context-sdk/tasks.md | 22 +- packages/bio-sdk/package.json | 3 + packages/bio-sdk/src/index.ts | 1 + packages/bio-sdk/src/miniapp-context.test.ts | 109 +++++++ packages/bio-sdk/src/miniapp-context.ts | 265 ++++++++++++++++++ pnpm-lock.yaml | 6 +- src/services/miniapp-runtime/index.ts | 157 ++++++++--- 8 files changed, 542 insertions(+), 46 deletions(-) create mode 100644 packages/bio-sdk/src/miniapp-context.test.ts create mode 100644 packages/bio-sdk/src/miniapp-context.ts diff --git a/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md b/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md index c2f16816a..08619e145 100644 --- a/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md +++ b/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md @@ -188,6 +188,31 @@ await bio.kv.set('user-preference', { theme: 'dark' }); const pref = await bio.kv.get('user-preference'); ``` +## Miniapp Context SDK + +用于获取 KeyApp 上下文(safe area、宿主版本等),并订阅更新,避免业务侧直接使用 postMessage。 + +```typescript +import { + getMiniappContext, + onMiniappContextUpdate, + applyMiniappSafeAreaCssVars, +} from '@bioforest/bio-sdk'; + +const context = await getMiniappContext(); +applyMiniappSafeAreaCssVars(context); + +const unsubscribe = onMiniappContextUpdate((next) => { + applyMiniappSafeAreaCssVars(next); +}); +``` + +SDK 行为要点: + +- `getMiniappContext()` 无缓存时自动发起一次请求,超时会回退默认值并告警。 +- `onMiniappContextUpdate()` 会回放最近一次 context,并在需要时触发刷新。 +- `applyMiniappSafeAreaCssVars()` 会写入 `--keyapp-safe-area-*` 四个变量。 + ### 内部实现 ```typescript diff --git a/openspec/changes/add-miniapp-context-sdk/tasks.md b/openspec/changes/add-miniapp-context-sdk/tasks.md index a6096d3b7..ec7af99d1 100644 --- a/openspec/changes/add-miniapp-context-sdk/tasks.md +++ b/openspec/changes/add-miniapp-context-sdk/tasks.md @@ -1,20 +1,20 @@ ## 1. Schema & Protocol -- [ ] Define Zod schema for `MiniappContext` and message envelope. -- [ ] Add typed message channel constants and payload types. +- [x] Define Zod schema for `MiniappContext` and message envelope. +- [x] Add typed message channel constants and payload types. ## 2. Host Integration -- [ ] Add context provider in miniapp runtime (safeAreaInsets, env, host version). -- [ ] Handle `miniapp:context-request` and emit `keyapp:context-update`. -- [ ] Broadcast updates on safe area/layout changes. +- [x] Add context provider in miniapp runtime (safeAreaInsets, env, host version). +- [x] Handle `miniapp:context-request` and emit `keyapp:context-update`. +- [x] Broadcast updates on safe area/layout changes. ## 3. SDK Implementation -- [ ] Add `getMiniappContext` + `onMiniappContextUpdate` APIs with optional `forceRefresh`. -- [ ] Implement retry/timeout + default fallback when unsupported. -- [ ] Cache context and avoid duplicate event bindings. +- [x] Add `getMiniappContext` + `onMiniappContextUpdate` APIs with optional `forceRefresh`. +- [x] Implement retry/timeout + default fallback when unsupported. +- [x] Cache context and avoid duplicate event bindings. ## 4. UI/CSS Bridge -- [ ] Provide `applyMiniappSafeAreaCssVars` helper and usage guidance for safe area CSS variables. +- [x] Provide `applyMiniappSafeAreaCssVars` helper and usage guidance for safe area CSS variables. ## 5. Documentation & Tests -- [ ] Update white-book/SDK docs with usage examples. -- [ ] Add unit tests for SDK message flow and fallback. +- [x] Update white-book/SDK docs with usage examples. +- [x] Add unit tests for SDK message flow and fallback. diff --git a/packages/bio-sdk/package.json b/packages/bio-sdk/package.json index b482e4889..6db80076b 100644 --- a/packages/bio-sdk/package.json +++ b/packages/bio-sdk/package.json @@ -34,6 +34,9 @@ "vite-plugin-dts": "^4.5.4", "vitest": "^4.0.0" }, + "dependencies": { + "zod": "^4.1.13" + }, "keywords": [ "bio", "sdk", diff --git a/packages/bio-sdk/src/index.ts b/packages/bio-sdk/src/index.ts index 1696731a3..373ec14e0 100644 --- a/packages/bio-sdk/src/index.ts +++ b/packages/bio-sdk/src/index.ts @@ -29,6 +29,7 @@ import type { BioProvider } from './types' // Re-export types export * from './types' export * from './chain-id' +export * from './miniapp-context' export { EventEmitter } from './events' export { BioProviderImpl } from './provider' export { EthereumProvider, initEthereumProvider } from './ethereum-provider' diff --git a/packages/bio-sdk/src/miniapp-context.test.ts b/packages/bio-sdk/src/miniapp-context.test.ts new file mode 100644 index 000000000..80549f8f3 --- /dev/null +++ b/packages/bio-sdk/src/miniapp-context.test.ts @@ -0,0 +1,109 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { + applyMiniappSafeAreaCssVars, + getMiniappContext, + onMiniappContextUpdate, + type MiniappContext, +} from './miniapp-context' + +const sampleContext: MiniappContext = { + version: 1, + env: { + safeAreaInsets: { top: 12, right: 0, bottom: 8, left: 0 }, + platform: 'web', + locale: 'en-US', + }, + host: { + name: 'KeyApp', + version: '0.1.0', + }, + updatedAt: new Date().toISOString(), +} + +describe('miniapp context sdk', () => { + let mockParent: { postMessage: ReturnType } + let originalParent: Window + + beforeEach(() => { + mockParent = { postMessage: vi.fn() } + originalParent = window.parent + Object.defineProperty(window, 'parent', { + value: mockParent, + writable: true, + }) + }) + + afterEach(() => { + Object.defineProperty(window, 'parent', { + value: originalParent, + writable: true, + }) + vi.useRealTimers() + vi.restoreAllMocks() + }) + + it('requests context and resolves on update', async () => { + const promise = getMiniappContext({ timeoutMs: 100, retries: 0, forceRefresh: true }) + + const posted = mockParent.postMessage.mock.calls[0][0] + expect(posted.type).toBe('miniapp:context-request') + expect(typeof posted.requestId).toBe('string') + + window.dispatchEvent( + new MessageEvent('message', { + data: { + type: 'keyapp:context-update', + requestId: posted.requestId, + payload: sampleContext, + }, + }) + ) + + await expect(promise).resolves.toMatchObject({ + env: { + safeAreaInsets: { top: 12, right: 0, bottom: 8, left: 0 }, + }, + }) + }) + + it('falls back with warning on timeout', async () => { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + vi.useFakeTimers() + + const promise = getMiniappContext({ timeoutMs: 10, retries: 0, forceRefresh: true }) + await vi.advanceTimersByTimeAsync(20) + + const context = await promise + expect(context.env.safeAreaInsets).toEqual({ top: 0, right: 0, bottom: 0, left: 0 }) + expect(warnSpy).toHaveBeenCalled() + }) + + it('replays cached context to subscribers', async () => { + const handler = vi.fn() + + window.dispatchEvent( + new MessageEvent('message', { + data: { + type: 'keyapp:context-update', + payload: sampleContext, + }, + }) + ) + + const unsubscribe = onMiniappContextUpdate(handler) + expect(handler).toHaveBeenCalledWith( + expect.objectContaining({ + host: expect.objectContaining({ name: 'KeyApp' }), + }) + ) + + unsubscribe() + }) + + it('applies safe area css vars', () => { + applyMiniappSafeAreaCssVars(sampleContext) + const styles = getComputedStyle(document.documentElement) + expect(styles.getPropertyValue('--keyapp-safe-area-top').trim()).toBe('12px') + expect(styles.getPropertyValue('--keyapp-safe-area-bottom').trim()).toBe('8px') + }) +}) diff --git a/packages/bio-sdk/src/miniapp-context.ts b/packages/bio-sdk/src/miniapp-context.ts new file mode 100644 index 000000000..f5fb2e8d1 --- /dev/null +++ b/packages/bio-sdk/src/miniapp-context.ts @@ -0,0 +1,265 @@ +import { z } from 'zod' + +export const MiniappSafeAreaInsetsSchema = z.object({ + top: z.number().min(0), + right: z.number().min(0), + bottom: z.number().min(0), + left: z.number().min(0), +}) + +export const MiniappContextEnvSchema = z.object({ + safeAreaInsets: MiniappSafeAreaInsetsSchema, + platform: z.enum(['web', 'dweb', 'ios', 'android']).optional(), + locale: z.string().optional(), +}) + +export const MiniappContextHostSchema = z.object({ + name: z.string().min(1), + version: z.string().min(1), + build: z.string().optional(), +}) + +export const MiniappContextSchema = z + .object({ + version: z.number().min(1), + env: MiniappContextEnvSchema, + host: MiniappContextHostSchema, + updatedAt: z.string(), + theme: z + .object({ + colorMode: z.enum(['light', 'dark']).optional(), + }) + .optional(), + }) + .passthrough() + +export type MiniappContext = z.infer + +export type MiniappContextRequestMessage = { + type: 'miniapp:context-request' + requestId: string + sdkVersion: string + payload?: { + appId?: string + } +} + +export type MiniappContextUpdateMessage = { + type: 'keyapp:context-update' + requestId?: string + payload: MiniappContext +} + +export type MiniappContextOptions = { + forceRefresh?: boolean + timeoutMs?: number + retries?: number +} + +export type MiniappContextSubscriptionOptions = { + emitCached?: boolean +} + +type PendingRequest = { + resolve: (context: MiniappContext) => void + reject: (error: Error) => void + timeoutId: ReturnType | null +} + +const DEFAULT_TIMEOUT_MS = 1200 +const DEFAULT_RETRIES = 1 +const SDK_VERSION = '1.0.0' + +const listeners = new Set<(context: MiniappContext) => void>() +const pendingRequests = new Map() +let cachedContext: MiniappContext | null = null +let bridgeReady = false +let warnedUnsupported = false +let inflightContextPromise: Promise | null = null + +function ensureBridge(): void { + if (bridgeReady || typeof window === 'undefined') return + bridgeReady = true + window.addEventListener('message', handleMessage) +} + +function handleMessage(event: MessageEvent): void { + const data = event.data as MiniappContextUpdateMessage | undefined + if (!data || data.type !== 'keyapp:context-update') return + + const parsed = MiniappContextSchema.safeParse(data.payload) + if (!parsed.success) { + console.warn('[bio-sdk] Invalid miniapp context payload', parsed.error) + return + } + + cachedContext = parsed.data + listeners.forEach((listener) => listener(parsed.data)) + + if (data.requestId) { + const pending = pendingRequests.get(data.requestId) + if (pending) { + clearPendingRequest(data.requestId, pending) + pending.resolve(parsed.data) + return + } + } + + if (!data.requestId && pendingRequests.size > 0) { + pendingRequests.forEach((pending, key) => { + clearPendingRequest(key, pending) + pending.resolve(parsed.data) + }) + } +} + +function clearPendingRequest(requestId: string, pending: PendingRequest): void { + if (pending.timeoutId) clearTimeout(pending.timeoutId) + pendingRequests.delete(requestId) +} + +function createRequestId(): string { + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { + return crypto.randomUUID() + } + return `ctx_${Date.now()}_${Math.random().toString(16).slice(2)}` +} + +function postMessage(message: MiniappContextRequestMessage): void { + if (typeof window === 'undefined') return + if (window.parent === window) { + console.warn('[bio-sdk] Miniapp context requires iframe environment') + return + } + window.parent.postMessage(message, '*') +} + +function buildDefaultContext(): MiniappContext { + return { + version: 1, + env: { + safeAreaInsets: { top: 0, right: 0, bottom: 0, left: 0 }, + platform: 'web', + locale: typeof navigator !== 'undefined' ? navigator.language : undefined, + }, + host: { + name: 'KeyApp', + version: 'unknown', + }, + updatedAt: new Date().toISOString(), + } +} + +async function requestContextOnce(options?: MiniappContextOptions): Promise { + ensureBridge() + + const requestId = createRequestId() + const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS + const sdkVersion = SDK_VERSION + + const promise = new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + pendingRequests.delete(requestId) + reject(new Error('Miniapp context request timed out')) + }, timeoutMs) + + pendingRequests.set(requestId, { resolve, reject, timeoutId }) + }) + + postMessage({ + type: 'miniapp:context-request', + requestId, + sdkVersion, + }) + + return promise +} + +async function requestContextWithRetry(options?: MiniappContextOptions): Promise { + const retries = options?.retries ?? DEFAULT_RETRIES + let attempt = 0 + let lastError: Error | null = null + + while (attempt <= retries) { + try { + return await requestContextOnce(options) + } catch (error) { + lastError = error instanceof Error ? error : new Error('Miniapp context request failed') + } + attempt += 1 + } + + if (lastError) throw lastError + throw new Error('Miniapp context request failed') +} + +export async function getMiniappContext(options?: MiniappContextOptions): Promise { + if (cachedContext && !options?.forceRefresh) { + return cachedContext + } + + if (typeof window === 'undefined') { + return buildDefaultContext() + } + + if (!options?.forceRefresh && inflightContextPromise) { + return inflightContextPromise + } + + const requestPromise = requestContextWithRetry(options) + .then((context) => { + cachedContext = context + return context + }) + .catch((error) => { + if (!warnedUnsupported) { + warnedUnsupported = true + console.warn('[bio-sdk] Miniapp context is not supported by the host', error) + } + const fallback = buildDefaultContext() + cachedContext = fallback + return fallback + }) + .finally(() => { + if (inflightContextPromise === requestPromise) { + inflightContextPromise = null + } + }) + + inflightContextPromise = requestPromise + return requestPromise +} + +export function onMiniappContextUpdate( + handler: (context: MiniappContext) => void, + options?: MiniappContextSubscriptionOptions +): () => void { + ensureBridge() + listeners.add(handler) + + if (options?.emitCached !== false && cachedContext) { + handler(cachedContext) + } else if (!cachedContext) { + void getMiniappContext() + } + + return () => { + listeners.delete(handler) + } +} + +export function applyMiniappSafeAreaCssVars( + context: MiniappContext, + options?: { target?: HTMLElement | Document } +): void { + if (typeof document === 'undefined') return + const target = options?.target ?? document + const element = target instanceof Document ? target.documentElement : target + if (!element) return + + const { top, right, bottom, left } = context.env.safeAreaInsets + element.style.setProperty('--keyapp-safe-area-top', `${top}px`) + element.style.setProperty('--keyapp-safe-area-right', `${right}px`) + element.style.setProperty('--keyapp-safe-area-bottom', `${bottom}px`) + element.style.setProperty('--keyapp-safe-area-left', `${left}px`) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a004b2bfd..e6016a2ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -715,6 +715,10 @@ importers: version: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2) packages/bio-sdk: + dependencies: + zod: + specifier: ^4.1.13 + version: 4.2.1 devDependencies: jsdom: specifier: ^26.1.0 @@ -12116,7 +12120,7 @@ snapshots: '@vitest/mocker': 4.0.16(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) playwright: 1.57.0 tinyrainbow: 3.0.3 - vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2) + vitest: 4.0.16(@types/node@24.10.4)(@vitest/browser-playwright@4.0.16)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(msw@2.12.4(@types/node@24.10.4)(typescript@5.9.3))(yaml@2.8.2) transitivePeerDependencies: - bufferutil - msw diff --git a/src/services/miniapp-runtime/index.ts b/src/services/miniapp-runtime/index.ts index 0779d49bb..1aa2c639f 100644 --- a/src/services/miniapp-runtime/index.ts +++ b/src/services/miniapp-runtime/index.ts @@ -65,6 +65,8 @@ import { toastService } from '../toast'; import { getDesktopAppSlotRect, getIconRef } from './runtime-refs'; import i18n from '@/i18n'; import { windowStackManager } from '@biochain/ecosystem-native'; +import { isDwebEnvironment } from '@/lib/crypto/secure-storage'; +import type { MiniappContext as MiniappContextPayload } from '@biochain/bio-sdk'; const t = i18n.t.bind(i18n); export { @@ -209,48 +211,93 @@ function syncRunningMiniapps(nextApps: MiniappManifest[]): void { }); } -function getCapsuleSafeAreaTop(): number { - const testEl = document.createElement('div'); - testEl.style.cssText = 'position:fixed;top:env(safe-area-inset-top,0px);visibility:hidden;'; - document.body.appendChild(testEl); - const safeAreaTop = testEl.offsetTop; - document.body.removeChild(testEl); +type MiniappContextRequestMessage = { + type: 'miniapp:context-request'; + requestId: string; + sdkVersion: string; + payload?: { + appId?: string; + }; +}; + +type LegacyContextRequestMessage = { + type: 'keyapp:context-request'; + payload?: { + appId?: string; + }; +}; + +type KeyAppContextRequestMessage = MiniappContextRequestMessage | LegacyContextRequestMessage; + +type SafeAreaInsets = { + top: number; + right: number; + bottom: number; + left: number; +}; + +const MINIAPP_CONTEXT_VERSION = 1; + +const DEFAULT_SAFE_AREA: SafeAreaInsets = { top: 0, right: 0, bottom: 0, left: 0 }; + +function readSafeAreaInsets(): SafeAreaInsets { + if (typeof document === 'undefined' || !document.body) return { ...DEFAULT_SAFE_AREA }; + const probe = document.createElement('div'); + probe.style.cssText = + 'position:fixed;inset:0;padding:env(safe-area-inset-top,0px) env(safe-area-inset-right,0px) env(safe-area-inset-bottom,0px) env(safe-area-inset-left,0px);visibility:hidden;pointer-events:none;'; + document.body.appendChild(probe); + const styles = getComputedStyle(probe); + const insets = { + top: Number.parseFloat(styles.paddingTop) || 0, + right: Number.parseFloat(styles.paddingRight) || 0, + bottom: Number.parseFloat(styles.paddingBottom) || 0, + left: Number.parseFloat(styles.paddingLeft) || 0, + }; + document.body.removeChild(probe); + return insets; +} - const capsuleTop = Math.max(safeAreaTop, 8); +function getMiniappSafeAreaInsets(): SafeAreaInsets { + const insets = readSafeAreaInsets(); + const capsuleTop = Math.max(insets.top, 8); const capsuleHeight = 32; const padding = 8; - return capsuleTop + capsuleHeight + padding; + return { + ...insets, + top: capsuleTop + capsuleHeight + padding, + }; } -function sendKeyAppContext(iframe: HTMLIFrameElement): void { - const safeAreaTop = getCapsuleSafeAreaTop(); - const colorMode = document.documentElement.classList.contains('dark') ? 'dark' : 'light'; +function buildMiniappContextPayload(): MiniappContextPayload { + const colorMode = document?.documentElement?.classList.contains('dark') ? 'dark' : 'light'; + const locale = i18n.language || (typeof navigator !== 'undefined' ? navigator.language : 'en'); + const platform = isDwebEnvironment() ? 'dweb' : 'web'; - const context = { - type: 'keyapp:context-update', - payload: { - theme: { colorMode }, - env: { - platform: 'web', - safeAreaInsets: { - top: safeAreaTop, - bottom: 0, - left: 0, - right: 0, - }, - }, + return { + version: MINIAPP_CONTEXT_VERSION, + env: { + safeAreaInsets: getMiniappSafeAreaInsets(), + platform, + locale, + }, + host: { + name: 'KeyApp', + version: __APP_VERSION__, }, + updatedAt: new Date().toISOString(), + theme: { colorMode }, }; - - iframe.contentWindow?.postMessage(context, '*'); } -type KeyAppContextRequestMessage = { - type: 'keyapp:context-request'; - payload?: { - appId?: string; +function sendKeyAppContext(iframe: HTMLIFrameElement, requestId?: string): void { + const context: { type: 'keyapp:context-update'; requestId?: string; payload: MiniappContextPayload } = { + type: 'keyapp:context-update', + requestId, + payload: buildMiniappContextPayload(), }; -}; + + iframe.contentWindow?.postMessage(context, '*'); +} function getIframeByMessageSource(source: MessageEvent['source']): HTMLIFrameElement | null { if (!source) return null; @@ -265,11 +312,14 @@ function getIframeByMessageSource(source: MessageEvent['source']): HTMLIFrameEle function handleKeyAppContextRequest(event: MessageEvent): void { const data = event.data as KeyAppContextRequestMessage | undefined; - if (!data || data.type !== 'keyapp:context-request') return; + if (!data) return; + if (data.type !== 'keyapp:context-request' && data.type !== 'miniapp:context-request') return; + + const requestId = data.type === 'miniapp:context-request' ? data.requestId : undefined; const targetIframe = getIframeByMessageSource(event.source); if (targetIframe) { - sendKeyAppContext(targetIframe); + sendKeyAppContext(targetIframe, requestId); return; } @@ -278,10 +328,48 @@ function handleKeyAppContextRequest(event: MessageEvent): void { const app = miniappRuntimeStore.state.apps.get(appId); const iframe = app?.containerHandle?.getIframe() ?? app?.iframeRef; if (iframe) { - sendKeyAppContext(iframe); + sendKeyAppContext(iframe, requestId); } } +let lastContextFingerprint: string | null = null; + +function getContextFingerprint(context: MiniappContextPayload): string { + return JSON.stringify({ + env: context.env, + host: context.host, + theme: context.theme, + }); +} + +function broadcastMiniappContextUpdate(): void { + const context = buildMiniappContextPayload(); + const fingerprint = getContextFingerprint(context); + if (fingerprint === lastContextFingerprint) return; + lastContextFingerprint = fingerprint; + + miniappRuntimeStore.state.apps.forEach((app) => { + const iframe = app.containerHandle?.getIframe() ?? app.iframeRef; + if (iframe) { + iframe.contentWindow?.postMessage({ type: 'keyapp:context-update', payload: context }, '*'); + } + }); +} + +let miniappContextAutoRefreshReady = false; +function ensureMiniappContextAutoRefresh(): void { + if (miniappContextAutoRefreshReady || typeof window === 'undefined') return; + miniappContextAutoRefreshReady = true; + + const handleRefresh = () => { + broadcastMiniappContextUpdate(); + }; + + window.addEventListener('resize', handleRefresh); + window.addEventListener('orientationchange', handleRefresh); + window.visualViewport?.addEventListener('resize', handleRefresh); +} + let keyAppContextRequestListenerReady = false; function ensureKeyAppContextRequestListener(): void { if (keyAppContextRequestListenerReady) return; @@ -301,6 +389,7 @@ export const miniappRuntimeStore = new Store(initialState); // 注册 context-request 监听器(必须在 store 定义之后) ensureKeyAppContextRequestListener(); +ensureMiniappContextAutoRefresh(); ensureMiniappManifestSyncListener(); export function setMiniappVisualConfig(update: MiniappVisualConfigUpdate): void { From 9cfae4c9ea2356311408b7490a7d99be470389bb Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 14:18:38 +0800 Subject: [PATCH 7/9] chore: update deno lock --- deno.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deno.lock b/deno.lock index 85b63aa3e..8e316d4ba 100644 --- a/deno.lock +++ b/deno.lock @@ -10360,7 +10360,8 @@ "npm:typescript@^5.9.3", "npm:vite-plugin-dts@^4.5.4", "npm:vite@^7.3.0", - "npm:vitest@4" + "npm:vitest@4", + "npm:zod@^4.1.13" ] } }, From d09d543f7f885d337a0eca289d77be087016539b Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 14:42:20 +0800 Subject: [PATCH 8/9] feat(miniapp): refine context updates --- .../01-Bio-SDK-Communication.md | 3 +- miniapps/biobridge/src/main.tsx | 12 ++- miniapps/teleport/src/main.tsx | 12 ++- .../changes/add-miniapp-context-sdk/design.md | 7 +- .../specs/miniapp-runtime/spec.md | 2 +- packages/bio-sdk/src/miniapp-context.test.ts | 8 +- packages/bio-sdk/src/miniapp-context.ts | 77 ++++++++++++++++++- src/services/miniapp-runtime/index.ts | 14 +++- 8 files changed, 123 insertions(+), 12 deletions(-) diff --git a/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md b/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md index 08619e145..8f216e247 100644 --- a/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md +++ b/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md @@ -199,7 +199,7 @@ import { applyMiniappSafeAreaCssVars, } from '@bioforest/bio-sdk'; -const context = await getMiniappContext(); +const context = await getMiniappContext({ appId: window.name || undefined }); applyMiniappSafeAreaCssVars(context); const unsubscribe = onMiniappContextUpdate((next) => { @@ -212,6 +212,7 @@ SDK 行为要点: - `getMiniappContext()` 无缓存时自动发起一次请求,超时会回退默认值并告警。 - `onMiniappContextUpdate()` 会回放最近一次 context,并在需要时触发刷新。 - `applyMiniappSafeAreaCssVars()` 会写入 `--keyapp-safe-area-*` 四个变量。 +- 当宿主不支持时,SDK 会回退到浏览器的 `env(safe-area-inset-*)`、`prefers-color-scheme`、`document.documentElement.lang` 等标准 Web API。 ### 内部实现 diff --git a/miniapps/biobridge/src/main.tsx b/miniapps/biobridge/src/main.tsx index 207d0fb31..3b2a13890 100644 --- a/miniapps/biobridge/src/main.tsx +++ b/miniapps/biobridge/src/main.tsx @@ -1,7 +1,11 @@ import './index.css' import '@fontsource-variable/noto-sans-sc' import './i18n' -import '@biochain/bio-sdk' +import { + applyMiniappSafeAreaCssVars, + getMiniappContext, + onMiniappContextUpdate, +} from '@biochain/bio-sdk' import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import App from './App' @@ -11,6 +15,12 @@ if (!rootElement) { throw new Error('Root element not found') } +const appId = window.name || undefined +void getMiniappContext({ appId }).then(applyMiniappSafeAreaCssVars) +onMiniappContextUpdate((context) => { + applyMiniappSafeAreaCssVars(context) +}, { appId }) + createRoot(rootElement).render( diff --git a/miniapps/teleport/src/main.tsx b/miniapps/teleport/src/main.tsx index 22d81b70d..1f3bfe8fb 100644 --- a/miniapps/teleport/src/main.tsx +++ b/miniapps/teleport/src/main.tsx @@ -1,5 +1,9 @@ import './index.css' -import '@biochain/bio-sdk' +import { + applyMiniappSafeAreaCssVars, + getMiniappContext, + onMiniappContextUpdate, +} from '@biochain/bio-sdk' import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' @@ -14,6 +18,12 @@ const queryClient = new QueryClient({ }, }) +const appId = window.name || undefined +void getMiniappContext({ appId }).then(applyMiniappSafeAreaCssVars) +onMiniappContextUpdate((context) => { + applyMiniappSafeAreaCssVars(context) +}, { appId }) + createRoot(document.getElementById('root') as HTMLElement).render( diff --git a/openspec/changes/add-miniapp-context-sdk/design.md b/openspec/changes/add-miniapp-context-sdk/design.md index 949d9125a..c422e5e34 100644 --- a/openspec/changes/add-miniapp-context-sdk/design.md +++ b/openspec/changes/add-miniapp-context-sdk/design.md @@ -24,7 +24,7 @@ export type MiniappContext = { ``` ## Message Contract -- Request: `{ type: "miniapp:context-request", requestId, sdkVersion }` +- Request: `{ type: "miniapp:context-request", requestId, sdkVersion, payload?: { appId } }` - Response: `{ type: "keyapp:context-update", requestId, payload }` - Errors use `{ type: "keyapp:context-error", requestId, code, message }` @@ -34,11 +34,12 @@ export async function getMiniappContext(options?: { forceRefresh?: boolean; timeoutMs?: number; retries?: number; + appId?: string; }): Promise; export function onMiniappContextUpdate( handler: (context: MiniappContext) => void, - options?: { emitCached?: boolean }, + options?: { emitCached?: boolean; appId?: string }, ): () => void; ``` @@ -50,7 +51,7 @@ export function onMiniappContextUpdate( - `getMiniappContext()` returns cached context when available; if missing (or `forceRefresh`), it sends `miniapp:context-request` and waits for `keyapp:context-update`. - `onMiniappContextUpdate()` registers a handler, replays cached context once (default), and ensures a refresh if nothing is cached. - SDK uses a singleton message bridge to avoid duplicate event bindings. -- If host does not support the channel (timeout), SDK resolves with default context values and logs a warning. +- If host does not support the channel (timeout), SDK resolves with fallback context derived from standard Web APIs (safe-area env vars, prefers-color-scheme, document language) and logs a warning. ## Host Behavior - Host maintains a context snapshot (safeAreaInsets, env info, version) and updates it on layout changes. diff --git a/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md b/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md index 19baae681..c0554ef8c 100644 --- a/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md +++ b/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md @@ -32,7 +32,7 @@ The SDK SHALL request context once when accessed if no cached context is availab - **AND** resolves `getMiniappContext()` with the next `keyapp:context-update` ### Requirement: Compatibility fallback -When the host does not support the context channel, the SDK SHALL resolve with default values (safeAreaInsets = 0) and emit a warning. +When the host does not support the context channel, the SDK SHALL resolve with fallback values derived from standard Web APIs (safe-area env vars, prefers-color-scheme, document language) and emit a warning. #### Scenario: Unsupported host - **WHEN** the SDK times out waiting for a `keyapp:context-update` diff --git a/packages/bio-sdk/src/miniapp-context.test.ts b/packages/bio-sdk/src/miniapp-context.test.ts index 80549f8f3..3d5ca2155 100644 --- a/packages/bio-sdk/src/miniapp-context.test.ts +++ b/packages/bio-sdk/src/miniapp-context.test.ts @@ -43,11 +43,17 @@ describe('miniapp context sdk', () => { }) it('requests context and resolves on update', async () => { - const promise = getMiniappContext({ timeoutMs: 100, retries: 0, forceRefresh: true }) + const promise = getMiniappContext({ + timeoutMs: 100, + retries: 0, + forceRefresh: true, + appId: 'miniapp-test', + }) const posted = mockParent.postMessage.mock.calls[0][0] expect(posted.type).toBe('miniapp:context-request') expect(typeof posted.requestId).toBe('string') + expect(posted.payload).toEqual({ appId: 'miniapp-test' }) window.dispatchEvent( new MessageEvent('message', { diff --git a/packages/bio-sdk/src/miniapp-context.ts b/packages/bio-sdk/src/miniapp-context.ts index f5fb2e8d1..5707446d9 100644 --- a/packages/bio-sdk/src/miniapp-context.ts +++ b/packages/bio-sdk/src/miniapp-context.ts @@ -7,6 +7,8 @@ export const MiniappSafeAreaInsetsSchema = z.object({ left: z.number().min(0), }) +export type MiniappSafeAreaInsets = z.infer + export const MiniappContextEnvSchema = z.object({ safeAreaInsets: MiniappSafeAreaInsetsSchema, platform: z.enum(['web', 'dweb', 'ios', 'android']).optional(), @@ -54,10 +56,12 @@ export type MiniappContextOptions = { forceRefresh?: boolean timeoutMs?: number retries?: number + appId?: string } export type MiniappContextSubscriptionOptions = { emitCached?: boolean + appId?: string } type PendingRequest = { @@ -76,6 +80,7 @@ let cachedContext: MiniappContext | null = null let bridgeReady = false let warnedUnsupported = false let inflightContextPromise: Promise | null = null +let lastRequestAppId: string | undefined function ensureBridge(): void { if (bridgeReady || typeof window === 'undefined') return @@ -134,19 +139,70 @@ function postMessage(message: MiniappContextRequestMessage): void { window.parent.postMessage(message, '*') } +function resolveAppId(options?: MiniappContextOptions): string | undefined { + if (options?.appId) { + lastRequestAppId = options.appId + return options.appId + } + return lastRequestAppId +} + +function readSafeAreaInsets(): MiniappSafeAreaInsets { + if (typeof document === 'undefined' || !document.body) { + return { top: 0, right: 0, bottom: 0, left: 0 } + } + + const probe = document.createElement('div') + probe.style.cssText = + 'position:fixed;inset:0;padding:env(safe-area-inset-top,0px) env(safe-area-inset-right,0px) env(safe-area-inset-bottom,0px) env(safe-area-inset-left,0px);visibility:hidden;pointer-events:none;' + document.body.appendChild(probe) + const styles = getComputedStyle(probe) + const insets = { + top: Number.parseFloat(styles.paddingTop) || 0, + right: Number.parseFloat(styles.paddingRight) || 0, + bottom: Number.parseFloat(styles.paddingBottom) || 0, + left: Number.parseFloat(styles.paddingLeft) || 0, + } + document.body.removeChild(probe) + return insets +} + +function readColorMode(): 'light' | 'dark' | undefined { + if (typeof document !== 'undefined' && document.documentElement.classList.contains('dark')) { + return 'dark' + } + if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') { + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' + } + return undefined +} + +function readLocale(): string | undefined { + if (typeof document !== 'undefined' && document.documentElement.lang) { + return document.documentElement.lang + } + if (typeof navigator !== 'undefined') { + return navigator.language + } + return undefined +} + function buildDefaultContext(): MiniappContext { return { version: 1, env: { - safeAreaInsets: { top: 0, right: 0, bottom: 0, left: 0 }, + safeAreaInsets: readSafeAreaInsets(), platform: 'web', - locale: typeof navigator !== 'undefined' ? navigator.language : undefined, + locale: readLocale(), }, host: { name: 'KeyApp', version: 'unknown', }, updatedAt: new Date().toISOString(), + theme: { + colorMode: readColorMode(), + }, } } @@ -156,6 +212,7 @@ async function requestContextOnce(options?: MiniappContextOptions): Promise((resolve, reject) => { const timeoutId = setTimeout(() => { @@ -170,6 +227,7 @@ async function requestContextOnce(options?: MiniappContextOptions): Promise void { ensureBridge() + if (options?.appId) { + lastRequestAppId = options.appId + } listeners.add(handler) if (options?.emitCached !== false && cachedContext) { handler(cachedContext) } else if (!cachedContext) { - void getMiniappContext() + void getMiniappContext({ appId: options?.appId }) } return () => { diff --git a/src/services/miniapp-runtime/index.ts b/src/services/miniapp-runtime/index.ts index 1aa2c639f..7751039d1 100644 --- a/src/services/miniapp-runtime/index.ts +++ b/src/services/miniapp-runtime/index.ts @@ -270,7 +270,10 @@ function getMiniappSafeAreaInsets(): SafeAreaInsets { function buildMiniappContextPayload(): MiniappContextPayload { const colorMode = document?.documentElement?.classList.contains('dark') ? 'dark' : 'light'; - const locale = i18n.language || (typeof navigator !== 'undefined' ? navigator.language : 'en'); + const locale = + i18n.language || + document?.documentElement?.lang || + (typeof navigator !== 'undefined' ? navigator.language : 'en'); const platform = isDwebEnvironment() ? 'dweb' : 'web'; return { @@ -368,6 +371,15 @@ function ensureMiniappContextAutoRefresh(): void { window.addEventListener('resize', handleRefresh); window.addEventListener('orientationchange', handleRefresh); window.visualViewport?.addEventListener('resize', handleRefresh); + i18n.on('languageChanged', handleRefresh); + + if (typeof document !== 'undefined') { + const observer = new MutationObserver(handleRefresh); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class', 'data-theme'], + }); + } } let keyAppContextRequestListenerReady = false; From 3b94eeaa4ef4e080f4460c8a0a683475063e9fc0 Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 5 Feb 2026 14:59:29 +0800 Subject: [PATCH 9/9] feat(miniapp): cssify context vars --- .../01-Bio-SDK-Communication.md | 4 +- .../changes/add-miniapp-context-sdk/design.md | 5 + .../specs/miniapp-runtime/spec.md | 2 +- packages/bio-sdk/dist/index.cjs | 624 ----------------- packages/bio-sdk/dist/index.cjs.map | 1 - packages/bio-sdk/dist/index.d.ts | 445 ------------- packages/bio-sdk/dist/index.js | 624 ----------------- packages/bio-sdk/dist/index.js.map | 1 - packages/bio-sdk/dist/index.umd.js | 628 ------------------ packages/bio-sdk/dist/index.umd.js.map | 1 - packages/bio-sdk/package.json | 16 +- packages/bio-sdk/src/miniapp-context.test.ts | 7 + packages/bio-sdk/src/miniapp-context.ts | 20 + src/services/miniapp-runtime/index.ts | 4 + 14 files changed, 45 insertions(+), 2337 deletions(-) delete mode 100644 packages/bio-sdk/dist/index.cjs delete mode 100644 packages/bio-sdk/dist/index.cjs.map delete mode 100644 packages/bio-sdk/dist/index.d.ts delete mode 100644 packages/bio-sdk/dist/index.js delete mode 100644 packages/bio-sdk/dist/index.js.map delete mode 100644 packages/bio-sdk/dist/index.umd.js delete mode 100644 packages/bio-sdk/dist/index.umd.js.map diff --git a/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md b/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md index 8f216e247..07f0fc7e6 100644 --- a/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md +++ b/docs/white-book/11-DApp-Guide/02-Connectivity/01-Bio-SDK-Communication.md @@ -211,8 +211,8 @@ SDK 行为要点: - `getMiniappContext()` 无缓存时自动发起一次请求,超时会回退默认值并告警。 - `onMiniappContextUpdate()` 会回放最近一次 context,并在需要时触发刷新。 -- `applyMiniappSafeAreaCssVars()` 会写入 `--keyapp-safe-area-*` 四个变量。 -- 当宿主不支持时,SDK 会回退到浏览器的 `env(safe-area-inset-*)`、`prefers-color-scheme`、`document.documentElement.lang` 等标准 Web API。 +- `applyMiniappSafeAreaCssVars()` 会写入 `--keyapp-safe-area-*`、`--keyapp-lang`、`--keyapp-direction`、`--keyapp-color-mode`。 +- 当宿主不支持时,SDK 会回退到浏览器的 `env(safe-area-inset-*)`、`prefers-color-scheme`、`document.documentElement.lang/dir` 等标准 Web API。 ### 内部实现 diff --git a/openspec/changes/add-miniapp-context-sdk/design.md b/openspec/changes/add-miniapp-context-sdk/design.md index c422e5e34..32a0423ff 100644 --- a/openspec/changes/add-miniapp-context-sdk/design.md +++ b/openspec/changes/add-miniapp-context-sdk/design.md @@ -13,6 +13,7 @@ export type MiniappContext = { }; platform?: "ios" | "android" | "web" | "dweb"; locale?: string; + direction?: "ltr" | "rtl"; }; host: { name: "KeyApp"; @@ -77,4 +78,8 @@ Behavior: - `--keyapp-safe-area-right` - `--keyapp-safe-area-bottom` - `--keyapp-safe-area-left` +- Also sets when available: + - `--keyapp-lang` + - `--keyapp-direction` + - `--keyapp-color-mode` - Values are `${number}px`, derived from `context.env.safeAreaInsets`. diff --git a/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md b/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md index c0554ef8c..c430906ce 100644 --- a/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md +++ b/openspec/changes/add-miniapp-context-sdk/specs/miniapp-runtime/spec.md @@ -32,7 +32,7 @@ The SDK SHALL request context once when accessed if no cached context is availab - **AND** resolves `getMiniappContext()` with the next `keyapp:context-update` ### Requirement: Compatibility fallback -When the host does not support the context channel, the SDK SHALL resolve with fallback values derived from standard Web APIs (safe-area env vars, prefers-color-scheme, document language) and emit a warning. +When the host does not support the context channel, the SDK SHALL resolve with fallback values derived from standard Web APIs (safe-area env vars, prefers-color-scheme, document language/dir) and emit a warning. #### Scenario: Unsupported host - **WHEN** the SDK times out waiting for a `keyapp:context-update` diff --git a/packages/bio-sdk/dist/index.cjs b/packages/bio-sdk/dist/index.cjs deleted file mode 100644 index a7b01f361..000000000 --- a/packages/bio-sdk/dist/index.cjs +++ /dev/null @@ -1,624 +0,0 @@ -"use strict"; -Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); -const BioErrorCodes = { - USER_REJECTED: 4001, - UNAUTHORIZED: 4100, - UNSUPPORTED_METHOD: 4200, - DISCONNECTED: 4900, - CHAIN_DISCONNECTED: 4901, - INTERNAL_ERROR: -32603, - INVALID_PARAMS: -32602, - METHOD_NOT_FOUND: -32601 -}; -function createProviderError(code, message, data) { - const error = new Error(message); - error.code = code; - error.data = data; - return error; -} -class EventEmitter { - handlers = /* @__PURE__ */ new Map(); - on(event, handler) { - let handlers = this.handlers.get(event); - if (!handlers) { - handlers = /* @__PURE__ */ new Set(); - this.handlers.set(event, handlers); - } - handlers.add(handler); - } - off(event, handler) { - const handlers = this.handlers.get(event); - if (handlers) { - handlers.delete(handler); - if (handlers.size === 0) { - this.handlers.delete(event); - } - } - } - emit(event, ...args) { - const handlers = this.handlers.get(event); - if (handlers) { - handlers.forEach((handler) => { - try { - handler(...args); - } catch (error) { - console.error(`[BioSDK] Error in event handler for "${event}":`, error); - } - }); - } - } - removeAllListeners(event) { - if (event) { - this.handlers.delete(event); - } else { - this.handlers.clear(); - } - } -} -class BioProviderImpl { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - connected = false; - targetOrigin; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - this.connect(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "bio_response") { - this.handleResponse(data); - } else if (data.type === "bio_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - if (message.event === "connect") { - this.connected = true; - } else if (message.event === "disconnect") { - this.connected = false; - } - } - connect() { - this.postMessage({ - type: "bio_request", - id: this.generateId(), - method: "bio_connect", - params: [] - }); - } - generateId() { - return `bio_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[BioSDK] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - async request(args) { - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "bio_request", - id, - method: args.method, - params: args.params - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - on(event, handler) { - this.events.on(event, handler); - } - off(event, handler) { - this.events.off(event, handler); - } - isConnected() { - return this.connected; - } -} -class EthereumProvider { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - connected = false; - currentChainId = null; - accounts = []; - targetOrigin; - // EIP-1193 required properties - isMetaMask = false; - isKeyApp = true; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "eth_response") { - this.handleResponse(data); - } else if (data.type === "eth_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - if (message.event === "connect") { - this.connected = true; - const info = message.args[0]; - this.currentChainId = info?.chainId ?? null; - } else if (message.event === "disconnect") { - this.connected = false; - this.accounts = []; - } else if (message.event === "chainChanged") { - this.currentChainId = message.args[0]; - } else if (message.event === "accountsChanged") { - this.accounts = message.args[0]; - } - } - generateId() { - return `eth_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[EthereumProvider] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - /** - * EIP-1193 request method - */ - async request(args) { - const { method, params } = args; - const paramsArray = Array.isArray(params) ? params : params ? [params] : []; - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "eth_request", - id, - method, - params: paramsArray - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - /** - * Subscribe to an event - */ - on(event, handler) { - this.events.on(event, handler); - return this; - } - /** - * Unsubscribe from an event - */ - off(event, handler) { - this.events.off(event, handler); - return this; - } - /** - * Alias for off (Node.js EventEmitter compatibility) - */ - removeListener(event, handler) { - return this.off(event, handler); - } - /** - * Add listener that fires only once - */ - once(event, handler) { - const wrapper = (...args) => { - this.off(event, wrapper); - handler(...args); - }; - this.on(event, wrapper); - return this; - } - /** - * EIP-1193 isConnected method - */ - isConnected() { - return this.connected; - } - /** - * Get current chain ID (cached) - */ - get chainId() { - return this.currentChainId; - } - /** - * Get selected address (first account) - */ - get selectedAddress() { - return this.accounts[0] ?? null; - } - // ============================================ - // Legacy methods (for backwards compatibility) - // ============================================ - /** - * @deprecated Use request({ method: 'eth_requestAccounts' }) - */ - async enable() { - return this.request({ method: "eth_requestAccounts" }); - } - /** - * @deprecated Use request() - */ - send(method, params) { - return this.request({ method, params }); - } - /** - * @deprecated Use request() - */ - sendAsync(payload, callback) { - this.request({ method: payload.method, params: payload.params }).then((result) => callback(null, { result })).catch((error) => callback(error)); - } -} -function initEthereumProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[EthereumProvider] Cannot initialize: window is not defined"); - } - if (window.ethereum) { - console.warn("[EthereumProvider] Provider already exists, returning existing instance"); - return window.ethereum; - } - const provider = new EthereumProvider(targetOrigin); - window.ethereum = provider; - console.log("[EthereumProvider] Provider initialized"); - return provider; -} -class TronLinkProvider { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - targetOrigin; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "tron_response") { - this.handleResponse(data); - } else if (data.type === "tron_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - } - generateId() { - return `tron_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[TronLinkProvider] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - /** - * TronLink request method (EIP-1193 style) - */ - async request(args) { - const { method, params } = args; - const paramsArray = Array.isArray(params) ? params : params !== void 0 ? [params] : []; - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "tron_request", - id, - method, - params: paramsArray - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - on(event, handler) { - this.events.on(event, handler); - return this; - } - off(event, handler) { - this.events.off(event, handler); - return this; - } -} -class TronWebProvider { - tronLink; - _ready = false; - _defaultAddress = { base58: "", hex: "" }; - /** TRX operations */ - trx; - constructor(tronLink) { - this.tronLink = tronLink; - this.trx = new TronWebTrx(tronLink); - tronLink.on("accountsChanged", (accounts) => { - if (Array.isArray(accounts) && accounts.length > 0) { - const addr = accounts[0]; - this._defaultAddress = addr; - this._ready = true; - } else { - this._defaultAddress = { base58: "", hex: "" }; - this._ready = false; - } - }); - } - /** Whether TronWeb is ready (connected) */ - get ready() { - return this._ready; - } - /** Current default address */ - get defaultAddress() { - return this._defaultAddress; - } - /** - * Set default address (called by host after connection) - */ - setAddress(address) { - this._defaultAddress = address; - this._ready = true; - } - /** - * Check if an address is valid - */ - isAddress(address) { - if (address.startsWith("T")) { - return address.length === 34; - } - if (address.startsWith("41")) { - return address.length === 42; - } - return false; - } - /** - * Convert address to hex format - */ - address = { - toHex: (base58) => { - return base58; - }, - fromHex: (hex) => { - return hex; - } - }; -} -class TronWebTrx { - tronLink; - constructor(tronLink) { - this.tronLink = tronLink; - } - /** - * Sign a transaction - */ - async sign(transaction) { - return this.tronLink.request({ - method: "tron_signTransaction", - params: transaction - }); - } - /** - * Send raw transaction (broadcast) - */ - async sendRawTransaction(signedTransaction) { - return this.tronLink.request({ - method: "tron_sendRawTransaction", - params: signedTransaction - }); - } - /** - * Get account balance - */ - async getBalance(address) { - return this.tronLink.request({ - method: "tron_getBalance", - params: address - }); - } - /** - * Get account info - */ - async getAccount(address) { - return this.tronLink.request({ - method: "tron_getAccount", - params: address - }); - } -} -function initTronProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[TronProvider] Cannot initialize: window is not defined"); - } - if (window.tronLink && window.tronWeb) { - console.warn("[TronProvider] Providers already exist, returning existing instances"); - return { tronLink: window.tronLink, tronWeb: window.tronWeb }; - } - const tronLink = new TronLinkProvider(targetOrigin); - const tronWeb = new TronWebProvider(tronLink); - window.tronLink = tronLink; - window.tronWeb = tronWeb; - console.log("[TronProvider] Providers initialized"); - return { tronLink, tronWeb }; -} -const EVM_CHAIN_IDS = { - ethereum: 1, - binance: 56 - // Future chains - // polygon: 137, - // arbitrum: 42161, - // optimism: 10, -}; -const EVM_CHAIN_ID_TO_KEYAPP = Object.fromEntries( - Object.entries(EVM_CHAIN_IDS).map(([key, value]) => [value, key]) -); -const API_CHAIN_TO_KEYAPP = { - ETH: "ethereum", - BSC: "binance", - TRON: "tron", - BFMCHAIN: "bfmeta", - BFCHAIN: "bfchain", - // Lowercase variants - eth: "ethereum", - bsc: "binance", - tron: "tron", - bfmchain: "bfmeta", - bfchain: "bfchain" -}; -const CHAIN_DISPLAY_NAMES = { - ethereum: "Ethereum", - binance: "BNB Smart Chain", - tron: "Tron", - bfmeta: "BFMeta", - bfchain: "BFChain" -}; -function toHexChainId(chainId) { - return `0x${chainId.toString(16)}`; -} -function parseHexChainId(hexChainId) { - if (!hexChainId.startsWith("0x")) { - throw new Error(`Invalid hex chain ID: ${hexChainId}`); - } - return parseInt(hexChainId, 16); -} -function getKeyAppChainId(hexChainId) { - const decimal = parseHexChainId(hexChainId); - return EVM_CHAIN_ID_TO_KEYAPP[decimal] ?? null; -} -function getEvmChainId(keyAppChainId) { - const decimal = EVM_CHAIN_IDS[keyAppChainId]; - return decimal ? toHexChainId(decimal) : null; -} -function isEvmChain(chainId) { - return chainId in EVM_CHAIN_IDS; -} -function normalizeChainId(chainName) { - return API_CHAIN_TO_KEYAPP[chainName] ?? chainName.toLowerCase(); -} -function initBioProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[BioSDK] Cannot initialize: window is not defined"); - } - if (window.bio) { - console.warn("[BioSDK] Provider already exists, returning existing instance"); - return window.bio; - } - const provider = new BioProviderImpl(targetOrigin); - window.bio = provider; - console.log("[BioSDK] Provider initialized"); - return provider; -} -function initAllProviders(targetOrigin = "*") { - const bio = initBioProvider(targetOrigin); - const ethereum = initEthereumProvider(targetOrigin); - const { tronLink, tronWeb } = initTronProvider(targetOrigin); - return { bio, ethereum, tronLink, tronWeb }; -} -if (typeof window !== "undefined") { - const init = () => { - initBioProvider(); - initEthereumProvider(); - initTronProvider(); - }; - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", init); - } else { - init(); - } -} -exports.API_CHAIN_TO_KEYAPP = API_CHAIN_TO_KEYAPP; -exports.BioErrorCodes = BioErrorCodes; -exports.BioProviderImpl = BioProviderImpl; -exports.CHAIN_DISPLAY_NAMES = CHAIN_DISPLAY_NAMES; -exports.EVM_CHAIN_IDS = EVM_CHAIN_IDS; -exports.EVM_CHAIN_ID_TO_KEYAPP = EVM_CHAIN_ID_TO_KEYAPP; -exports.EthereumProvider = EthereumProvider; -exports.EventEmitter = EventEmitter; -exports.TronLinkProvider = TronLinkProvider; -exports.TronWebProvider = TronWebProvider; -exports.createProviderError = createProviderError; -exports.getEvmChainId = getEvmChainId; -exports.getKeyAppChainId = getKeyAppChainId; -exports.initAllProviders = initAllProviders; -exports.initBioProvider = initBioProvider; -exports.initEthereumProvider = initEthereumProvider; -exports.initTronProvider = initTronProvider; -exports.isEvmChain = isEvmChain; -exports.normalizeChainId = normalizeChainId; -exports.parseHexChainId = parseHexChainId; -exports.toHexChainId = toHexChainId; -//# sourceMappingURL=index.cjs.map diff --git a/packages/bio-sdk/dist/index.cjs.map b/packages/bio-sdk/dist/index.cjs.map deleted file mode 100644 index 75fe55311..000000000 --- a/packages/bio-sdk/dist/index.cjs.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.cjs","sources":["../src/types.ts","../src/events.ts","../src/provider.ts","../src/ethereum-provider.ts","../src/tron-provider.ts","../src/chain-id.ts","../src/index.ts"],"sourcesContent":["/**\n * Bio SDK Types\n * EIP-1193 style provider interface for Bio ecosystem\n */\n\n/** Account information */\nexport interface BioAccount {\n address: string\n chain: string\n name?: string\n /** Public key (hex encoded) */\n publicKey: string\n}\n\n/** Transfer parameters */\nexport interface TransferParams {\n from: string\n to: string\n amount: string\n chain: string\n asset?: string\n}\n\n/** Unsigned transaction payload (chain-specific) */\nexport interface BioUnsignedTransaction {\n chainId: string\n data: unknown\n}\n\n/** Signed transaction payload (chain-specific) */\nexport interface BioSignedTransaction {\n chainId: string\n data: unknown\n signature: string\n}\n\n/** Provider request arguments */\nexport interface RequestArguments {\n method: string\n params?: unknown[]\n}\n\n/** Provider RPC error */\nexport interface ProviderRpcError extends Error {\n code: number\n data?: unknown\n}\n\n/** Event handler type */\nexport type EventHandler = (...args: T[]) => void\n\n/**\n * Bio Provider Interface (EIP-1193 style)\n */\nexport interface BioProvider {\n /** Make a request to the provider */\n request(args: RequestArguments): Promise\n\n /** Subscribe to an event */\n on(event: string, handler: EventHandler): void\n\n /** Unsubscribe from an event */\n off(event: string, handler: EventHandler): void\n\n /** Check if connected */\n isConnected(): boolean\n}\n\n/**\n * Bio method definitions\n */\nexport interface BioMethods {\n /** Request wallet accounts (shows connection UI) */\n bio_requestAccounts: () => Promise\n\n /** Get connected accounts (no UI) */\n bio_accounts: () => Promise\n\n /** Select an account (shows account picker UI) */\n bio_selectAccount: (opts?: { chain?: string }) => Promise\n\n /** Pick another wallet address (shows wallet picker UI) */\n bio_pickWallet: (opts?: { chain?: string; exclude?: string }) => Promise\n\n /** Sign a message, returns signature and public key (hex) */\n bio_signMessage: (params: { message: string; address: string }) => Promise<{ signature: string; publicKey: string }>\n\n /** Sign typed data, returns signature and public key (hex) */\n bio_signTypedData: (params: { data: object; address: string }) => Promise<{ signature: string; publicKey: string }>\n\n /** Create an unsigned transaction (no signature, no broadcast) */\n bio_createTransaction: (params: TransferParams) => Promise\n\n /** Sign an unsigned transaction (requires user confirmation) */\n bio_signTransaction: (params: { from: string; chain: string; unsignedTx: BioUnsignedTransaction }) => Promise\n\n /** Send a transaction */\n bio_sendTransaction: (params: TransferParams) => Promise<{ txHash: string }>\n\n /** Get current chain ID */\n bio_chainId: () => Promise\n\n /** Get balance */\n bio_getBalance: (params: { address: string; chain: string }) => Promise\n\n /** Close splash screen (indicates app is ready) */\n bio_closeSplashScreen: () => Promise\n}\n\n/**\n * Bio event definitions\n */\nexport interface BioEvents {\n /** Emitted when accounts change */\n accountsChanged: (accounts: BioAccount[]) => void\n\n /** Emitted when chain changes */\n chainChanged: (chainId: string) => void\n\n /** Emitted when connected */\n connect: (info: { chainId: string }) => void\n\n /** Emitted when disconnected */\n disconnect: (error: { code: number; message: string }) => void\n}\n\n/** Method names */\nexport type BioMethodName = keyof BioMethods\n\n/** Event names */\nexport type BioEventName = keyof BioEvents\n\n/** RPC error codes */\nexport const BioErrorCodes = {\n USER_REJECTED: 4001,\n UNAUTHORIZED: 4100,\n UNSUPPORTED_METHOD: 4200,\n DISCONNECTED: 4900,\n CHAIN_DISCONNECTED: 4901,\n INTERNAL_ERROR: -32603,\n INVALID_PARAMS: -32602,\n METHOD_NOT_FOUND: -32601,\n} as const\n\n/** Create a provider RPC error */\nexport function createProviderError(code: number, message: string, data?: unknown): ProviderRpcError {\n const error = new Error(message) as ProviderRpcError\n error.code = code\n error.data = data\n return error\n}\n","/**\n * Event emitter for Bio SDK\n */\n\nimport type { EventHandler } from './types'\n\nexport class EventEmitter {\n private handlers = new Map>()\n\n on(event: string, handler: EventHandler): void {\n let handlers = this.handlers.get(event)\n if (!handlers) {\n handlers = new Set()\n this.handlers.set(event, handlers)\n }\n handlers.add(handler)\n }\n\n off(event: string, handler: EventHandler): void {\n const handlers = this.handlers.get(event)\n if (handlers) {\n handlers.delete(handler)\n if (handlers.size === 0) {\n this.handlers.delete(event)\n }\n }\n }\n\n emit(event: string, ...args: unknown[]): void {\n const handlers = this.handlers.get(event)\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(...args)\n } catch (error) {\n console.error(`[BioSDK] Error in event handler for \"${event}\":`, error)\n }\n })\n }\n }\n\n removeAllListeners(event?: string): void {\n if (event) {\n this.handlers.delete(event)\n } else {\n this.handlers.clear()\n }\n }\n}\n","/**\n * Bio Provider Implementation\n * Communicates with KeyApp host via postMessage\n */\n\nimport type { BioProvider, RequestArguments, EventHandler } from './types'\nimport { BioErrorCodes, createProviderError } from './types'\nimport { EventEmitter } from './events'\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'bio_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'bio_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'bio_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\nexport class BioProviderImpl implements BioProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private connected = false\n private readonly targetOrigin: string\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n this.connect()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'bio_response') {\n this.handleResponse(data)\n } else if (data.type === 'bio_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n\n // Handle built-in events\n if (message.event === 'connect') {\n this.connected = true\n } else if (message.event === 'disconnect') {\n this.connected = false\n }\n }\n\n private connect(): void {\n // Send handshake to host\n this.postMessage({\n type: 'bio_request',\n id: this.generateId(),\n method: 'bio_connect',\n params: [],\n })\n }\n\n private generateId(): string {\n return `bio_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[BioSDK] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n async request(args: RequestArguments): Promise {\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'bio_request',\n id,\n method: args.method,\n params: args.params,\n })\n\n // Timeout after 5 minutes (for user interactions)\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n on(event: string, handler: EventHandler): void {\n this.events.on(event, handler)\n }\n\n off(event: string, handler: EventHandler): void {\n this.events.off(event, handler)\n }\n\n isConnected(): boolean {\n return this.connected\n }\n}\n","/**\n * Ethereum Provider (EIP-1193 Compatible)\n *\n * Provides window.ethereum for EVM-compatible dApps.\n * Communicates with KeyApp host via postMessage.\n */\n\nimport { EventEmitter } from './events'\nimport { BioErrorCodes, createProviderError, type ProviderRpcError } from './types'\nimport { toHexChainId, parseHexChainId, getKeyAppChainId, EVM_CHAIN_IDS } from './chain-id'\n\n/** EIP-1193 Request Arguments */\nexport interface EthRequestArguments {\n method: string\n params?: unknown[] | Record\n}\n\n/** EIP-1193 Provider Connect Info */\nexport interface ProviderConnectInfo {\n chainId: string\n}\n\n/** EIP-1193 Provider Message */\nexport interface ProviderMessage {\n type: string\n data: unknown\n}\n\n/** Transaction request (eth_sendTransaction) */\nexport interface TransactionRequest {\n from: string\n to?: string\n value?: string\n data?: string\n gas?: string\n gasPrice?: string\n maxFeePerGas?: string\n maxPriorityFeePerGas?: string\n nonce?: string\n}\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'eth_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'eth_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'eth_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\n/**\n * EIP-1193 Ethereum Provider Implementation\n */\nexport class EthereumProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private connected = false\n private currentChainId: string | null = null\n private accounts: string[] = []\n private readonly targetOrigin: string\n\n // EIP-1193 required properties\n readonly isMetaMask = false\n readonly isKeyApp = true\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'eth_response') {\n this.handleResponse(data)\n } else if (data.type === 'eth_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n\n // Handle built-in events\n if (message.event === 'connect') {\n this.connected = true\n const info = message.args[0] as ProviderConnectInfo\n this.currentChainId = info?.chainId ?? null\n } else if (message.event === 'disconnect') {\n this.connected = false\n this.accounts = []\n } else if (message.event === 'chainChanged') {\n this.currentChainId = message.args[0] as string\n } else if (message.event === 'accountsChanged') {\n this.accounts = message.args[0] as string[]\n }\n }\n\n private generateId(): string {\n return `eth_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[EthereumProvider] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n /**\n * EIP-1193 request method\n */\n async request(args: EthRequestArguments): Promise {\n const { method, params } = args\n const paramsArray = Array.isArray(params) ? params : params ? [params] : []\n\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'eth_request',\n id,\n method,\n params: paramsArray,\n })\n\n // Timeout after 5 minutes (for user interactions)\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n /**\n * Subscribe to an event\n */\n on(event: string, handler: (...args: unknown[]) => void): this {\n this.events.on(event, handler)\n return this\n }\n\n /**\n * Unsubscribe from an event\n */\n off(event: string, handler: (...args: unknown[]) => void): this {\n this.events.off(event, handler)\n return this\n }\n\n /**\n * Alias for off (Node.js EventEmitter compatibility)\n */\n removeListener(event: string, handler: (...args: unknown[]) => void): this {\n return this.off(event, handler)\n }\n\n /**\n * Add listener that fires only once\n */\n once(event: string, handler: (...args: unknown[]) => void): this {\n const wrapper = (...args: unknown[]) => {\n this.off(event, wrapper)\n handler(...args)\n }\n this.on(event, wrapper)\n return this\n }\n\n /**\n * EIP-1193 isConnected method\n */\n isConnected(): boolean {\n return this.connected\n }\n\n /**\n * Get current chain ID (cached)\n */\n get chainId(): string | null {\n return this.currentChainId\n }\n\n /**\n * Get selected address (first account)\n */\n get selectedAddress(): string | null {\n return this.accounts[0] ?? null\n }\n\n // ============================================\n // Legacy methods (for backwards compatibility)\n // ============================================\n\n /**\n * @deprecated Use request({ method: 'eth_requestAccounts' })\n */\n async enable(): Promise {\n return this.request({ method: 'eth_requestAccounts' })\n }\n\n /**\n * @deprecated Use request()\n */\n send(method: string, params?: unknown[]): Promise {\n return this.request({ method, params })\n }\n\n /**\n * @deprecated Use request()\n */\n sendAsync(\n payload: { method: string; params?: unknown[]; id?: number },\n callback: (error: Error | null, result?: { result: unknown }) => void\n ): void {\n this.request({ method: payload.method, params: payload.params })\n .then((result) => callback(null, { result }))\n .catch((error) => callback(error))\n }\n}\n\n// Extend Window interface\ndeclare global {\n interface Window {\n ethereum?: EthereumProvider\n }\n}\n\n/**\n * Initialize and inject the Ethereum provider into window.ethereum\n */\nexport function initEthereumProvider(targetOrigin = '*'): EthereumProvider {\n if (typeof window === 'undefined') {\n throw new Error('[EthereumProvider] Cannot initialize: window is not defined')\n }\n\n if (window.ethereum) {\n console.warn('[EthereumProvider] Provider already exists, returning existing instance')\n return window.ethereum\n }\n\n const provider = new EthereumProvider(targetOrigin)\n window.ethereum = provider\n\n console.log('[EthereumProvider] Provider initialized')\n return provider\n}\n","/**\n * Tron Provider (TronLink Compatible)\n *\n * Provides window.tronWeb and window.tronLink for Tron dApps.\n * Communicates with KeyApp host via postMessage.\n */\n\nimport { EventEmitter } from './events'\nimport { BioErrorCodes, createProviderError } from './types'\n\n/** Tron address format */\nexport interface TronAddress {\n base58: string\n hex: string\n}\n\n/** TronLink request arguments (EIP-1193 style) */\nexport interface TronRequestArguments {\n method: string\n params?: unknown\n}\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'tron_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'tron_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'tron_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\n/**\n * TronLink-compatible Provider\n */\nexport class TronLinkProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private readonly targetOrigin: string\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'tron_response') {\n this.handleResponse(data)\n } else if (data.type === 'tron_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n }\n\n private generateId(): string {\n return `tron_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[TronLinkProvider] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n /**\n * TronLink request method (EIP-1193 style)\n */\n async request(args: TronRequestArguments): Promise {\n const { method, params } = args\n const paramsArray = Array.isArray(params) ? params : params !== undefined ? [params] : []\n\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'tron_request',\n id,\n method,\n params: paramsArray,\n })\n\n // Timeout after 5 minutes\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n on(event: string, handler: (...args: unknown[]) => void): this {\n this.events.on(event, handler)\n return this\n }\n\n off(event: string, handler: (...args: unknown[]) => void): this {\n this.events.off(event, handler)\n return this\n }\n}\n\n/**\n * TronWeb-compatible API\n * Provides the subset of TronWeb API that KeyApp supports\n */\nexport class TronWebProvider {\n private tronLink: TronLinkProvider\n private _ready = false\n private _defaultAddress: TronAddress = { base58: '', hex: '' }\n\n /** TRX operations */\n readonly trx: TronWebTrx\n\n constructor(tronLink: TronLinkProvider) {\n this.tronLink = tronLink\n this.trx = new TronWebTrx(tronLink)\n\n // Listen for account changes\n tronLink.on('accountsChanged', (accounts: unknown) => {\n if (Array.isArray(accounts) && accounts.length > 0) {\n const addr = accounts[0] as TronAddress\n this._defaultAddress = addr\n this._ready = true\n } else {\n this._defaultAddress = { base58: '', hex: '' }\n this._ready = false\n }\n })\n }\n\n /** Whether TronWeb is ready (connected) */\n get ready(): boolean {\n return this._ready\n }\n\n /** Current default address */\n get defaultAddress(): TronAddress {\n return this._defaultAddress\n }\n\n /**\n * Set default address (called by host after connection)\n */\n setAddress(address: TronAddress): void {\n this._defaultAddress = address\n this._ready = true\n }\n\n /**\n * Check if an address is valid\n */\n isAddress(address: string): boolean {\n // Basic validation: base58 starts with T, hex starts with 41\n if (address.startsWith('T')) {\n return address.length === 34\n }\n if (address.startsWith('41')) {\n return address.length === 42\n }\n return false\n }\n\n /**\n * Convert address to hex format\n */\n address = {\n toHex: (base58: string): string => {\n // This is a stub - actual conversion requires TronWeb library\n // KeyApp will handle the conversion on the host side\n return base58\n },\n fromHex: (hex: string): string => {\n return hex\n },\n }\n}\n\n/**\n * TronWeb.trx operations\n */\nclass TronWebTrx {\n private tronLink: TronLinkProvider\n\n constructor(tronLink: TronLinkProvider) {\n this.tronLink = tronLink\n }\n\n /**\n * Sign a transaction\n */\n async sign(transaction: unknown): Promise {\n return this.tronLink.request({\n method: 'tron_signTransaction',\n params: transaction,\n })\n }\n\n /**\n * Send raw transaction (broadcast)\n */\n async sendRawTransaction(signedTransaction: unknown): Promise {\n return this.tronLink.request({\n method: 'tron_sendRawTransaction',\n params: signedTransaction,\n })\n }\n\n /**\n * Get account balance\n */\n async getBalance(address: string): Promise {\n return this.tronLink.request({\n method: 'tron_getBalance',\n params: address,\n })\n }\n\n /**\n * Get account info\n */\n async getAccount(address: string): Promise {\n return this.tronLink.request({\n method: 'tron_getAccount',\n params: address,\n })\n }\n}\n\n// Extend Window interface\ndeclare global {\n interface Window {\n tronLink?: TronLinkProvider\n tronWeb?: TronWebProvider\n }\n}\n\n/**\n * Initialize and inject the Tron providers\n */\nexport function initTronProvider(targetOrigin = '*'): { tronLink: TronLinkProvider; tronWeb: TronWebProvider } {\n if (typeof window === 'undefined') {\n throw new Error('[TronProvider] Cannot initialize: window is not defined')\n }\n\n if (window.tronLink && window.tronWeb) {\n console.warn('[TronProvider] Providers already exist, returning existing instances')\n return { tronLink: window.tronLink, tronWeb: window.tronWeb }\n }\n\n const tronLink = new TronLinkProvider(targetOrigin)\n const tronWeb = new TronWebProvider(tronLink)\n\n window.tronLink = tronLink\n window.tronWeb = tronWeb\n\n console.log('[TronProvider] Providers initialized')\n return { tronLink, tronWeb }\n}\n","/**\n * Chain ID utilities\n * Maps between KeyApp internal chain IDs and standard chain IDs\n */\n\n/** EVM Chain ID mapping (decimal) */\nexport const EVM_CHAIN_IDS: Record = {\n ethereum: 1,\n binance: 56,\n // Future chains\n // polygon: 137,\n // arbitrum: 42161,\n // optimism: 10,\n} as const\n\n/** Reverse mapping: EVM chainId -> KeyApp chain ID */\nexport const EVM_CHAIN_ID_TO_KEYAPP: Record = Object.fromEntries(\n Object.entries(EVM_CHAIN_IDS).map(([key, value]) => [value, key])\n)\n\n/** API chain name to KeyApp chain ID mapping */\nexport const API_CHAIN_TO_KEYAPP: Record = {\n ETH: 'ethereum',\n BSC: 'binance',\n TRON: 'tron',\n BFMCHAIN: 'bfmeta',\n BFCHAIN: 'bfchain',\n // Lowercase variants\n eth: 'ethereum',\n bsc: 'binance',\n tron: 'tron',\n bfmchain: 'bfmeta',\n bfchain: 'bfchain',\n} as const\n\n/** KeyApp chain ID to display name */\nexport const CHAIN_DISPLAY_NAMES: Record = {\n ethereum: 'Ethereum',\n binance: 'BNB Smart Chain',\n tron: 'Tron',\n bfmeta: 'BFMeta',\n bfchain: 'BFChain',\n} as const\n\n/**\n * Convert decimal chain ID to hex string (EIP-155 format)\n * @example toHexChainId(56) => '0x38'\n */\nexport function toHexChainId(chainId: number): string {\n return `0x${chainId.toString(16)}`\n}\n\n/**\n * Parse hex chain ID to decimal\n * @example parseHexChainId('0x38') => 56\n */\nexport function parseHexChainId(hexChainId: string): number {\n if (!hexChainId.startsWith('0x')) {\n throw new Error(`Invalid hex chain ID: ${hexChainId}`)\n }\n return parseInt(hexChainId, 16)\n}\n\n/**\n * Get KeyApp chain ID from EVM hex chain ID\n * @example getKeyAppChainId('0x38') => 'binance'\n */\nexport function getKeyAppChainId(hexChainId: string): string | null {\n const decimal = parseHexChainId(hexChainId)\n return EVM_CHAIN_ID_TO_KEYAPP[decimal] ?? null\n}\n\n/**\n * Get EVM hex chain ID from KeyApp chain ID\n * @example getEvmChainId('binance') => '0x38'\n */\nexport function getEvmChainId(keyAppChainId: string): string | null {\n const decimal = EVM_CHAIN_IDS[keyAppChainId]\n return decimal ? toHexChainId(decimal) : null\n}\n\n/**\n * Check if a chain is EVM compatible\n */\nexport function isEvmChain(chainId: string): boolean {\n return chainId in EVM_CHAIN_IDS\n}\n\n/**\n * Normalize API chain name to KeyApp chain ID\n * @example normalizeChainId('BSC') => 'binance'\n */\nexport function normalizeChainId(chainName: string): string {\n return API_CHAIN_TO_KEYAPP[chainName] ?? chainName.toLowerCase()\n}\n","/**\n * Bio SDK - Client SDK for Bio Ecosystem MiniApps\n *\n * Injects providers for multi-chain dApp support:\n * - `window.bio` - BioChain + KeyApp wallet features\n * - `window.ethereum` - EVM-compatible chains (ETH, BSC)\n * - `window.tronWeb` / `window.tronLink` - Tron chain\n *\n * @example\n * ```typescript\n * import '@biochain/bio-sdk'\n *\n * // BioChain operations\n * const accounts = await window.bio.request({ method: 'bio_requestAccounts' })\n *\n * // EVM operations (ETH, BSC)\n * const ethAccounts = await window.ethereum.request({ method: 'eth_requestAccounts' })\n *\n * // Tron operations\n * const tronAccounts = await window.tronLink.request({ method: 'tron_requestAccounts' })\n * ```\n */\n\nimport { BioProviderImpl } from './provider'\nimport { EthereumProvider, initEthereumProvider } from './ethereum-provider'\nimport { TronLinkProvider, TronWebProvider, initTronProvider } from './tron-provider'\nimport type { BioProvider } from './types'\n\n// Re-export types\nexport * from './types'\nexport * from './chain-id'\nexport { EventEmitter } from './events'\nexport { BioProviderImpl } from './provider'\nexport { EthereumProvider, initEthereumProvider } from './ethereum-provider'\nexport { TronLinkProvider, TronWebProvider, initTronProvider } from './tron-provider'\n\n// Extend Window interface (bio is declared in types.ts already for ethereum/tron)\ndeclare global {\n interface Window {\n bio?: BioProvider\n }\n}\n\n/**\n * Initialize and inject the Bio provider into window.bio\n */\nexport function initBioProvider(targetOrigin = '*'): BioProvider {\n if (typeof window === 'undefined') {\n throw new Error('[BioSDK] Cannot initialize: window is not defined')\n }\n\n if (window.bio) {\n console.warn('[BioSDK] Provider already exists, returning existing instance')\n return window.bio\n }\n\n const provider = new BioProviderImpl(targetOrigin)\n window.bio = provider\n\n console.log('[BioSDK] Provider initialized')\n return provider\n}\n\n/**\n * Initialize all providers (bio, ethereum, tron)\n */\nexport function initAllProviders(targetOrigin = '*'): {\n bio: BioProvider\n ethereum: EthereumProvider\n tronLink: TronLinkProvider\n tronWeb: TronWebProvider\n} {\n const bio = initBioProvider(targetOrigin)\n const ethereum = initEthereumProvider(targetOrigin)\n const { tronLink, tronWeb } = initTronProvider(targetOrigin)\n\n return { bio, ethereum, tronLink, tronWeb }\n}\n\n// Auto-initialize if running in browser\nif (typeof window !== 'undefined') {\n const init = () => {\n initBioProvider()\n initEthereumProvider()\n initTronProvider()\n }\n\n // Use a slight delay to ensure DOM is ready\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init)\n } else {\n init()\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/bio-sdk/dist/index.d.ts b/packages/bio-sdk/dist/index.d.ts deleted file mode 100644 index 10143204a..000000000 --- a/packages/bio-sdk/dist/index.d.ts +++ /dev/null @@ -1,445 +0,0 @@ -/** API chain name to KeyApp chain ID mapping */ -export declare const API_CHAIN_TO_KEYAPP: Record; - -/** - * Bio SDK Types - * EIP-1193 style provider interface for Bio ecosystem - */ -/** Account information */ -export declare interface BioAccount { - address: string; - chain: string; - name?: string; - /** Public key (hex encoded) */ - publicKey: string; -} - -/** RPC error codes */ -export declare const BioErrorCodes: { - readonly USER_REJECTED: 4001; - readonly UNAUTHORIZED: 4100; - readonly UNSUPPORTED_METHOD: 4200; - readonly DISCONNECTED: 4900; - readonly CHAIN_DISCONNECTED: 4901; - readonly INTERNAL_ERROR: -32603; - readonly INVALID_PARAMS: -32602; - readonly METHOD_NOT_FOUND: -32601; -}; - -/** Event names */ -export declare type BioEventName = keyof BioEvents; - -/** - * Bio event definitions - */ -export declare interface BioEvents { - /** Emitted when accounts change */ - accountsChanged: (accounts: BioAccount[]) => void; - /** Emitted when chain changes */ - chainChanged: (chainId: string) => void; - /** Emitted when connected */ - connect: (info: { - chainId: string; - }) => void; - /** Emitted when disconnected */ - disconnect: (error: { - code: number; - message: string; - }) => void; -} - -/** Method names */ -export declare type BioMethodName = keyof BioMethods; - -/** - * Bio method definitions - */ -export declare interface BioMethods { - /** Request wallet accounts (shows connection UI) */ - bio_requestAccounts: () => Promise; - /** Get connected accounts (no UI) */ - bio_accounts: () => Promise; - /** Select an account (shows account picker UI) */ - bio_selectAccount: (opts?: { - chain?: string; - }) => Promise; - /** Pick another wallet address (shows wallet picker UI) */ - bio_pickWallet: (opts?: { - chain?: string; - exclude?: string; - }) => Promise; - /** Sign a message, returns signature and public key (hex) */ - bio_signMessage: (params: { - message: string; - address: string; - }) => Promise<{ - signature: string; - publicKey: string; - }>; - /** Sign typed data, returns signature and public key (hex) */ - bio_signTypedData: (params: { - data: object; - address: string; - }) => Promise<{ - signature: string; - publicKey: string; - }>; - /** Create an unsigned transaction (no signature, no broadcast) */ - bio_createTransaction: (params: TransferParams) => Promise; - /** Sign an unsigned transaction (requires user confirmation) */ - bio_signTransaction: (params: { - from: string; - chain: string; - unsignedTx: BioUnsignedTransaction; - }) => Promise; - /** Send a transaction */ - bio_sendTransaction: (params: TransferParams) => Promise<{ - txHash: string; - }>; - /** Get current chain ID */ - bio_chainId: () => Promise; - /** Get balance */ - bio_getBalance: (params: { - address: string; - chain: string; - }) => Promise; - /** Close splash screen (indicates app is ready) */ - bio_closeSplashScreen: () => Promise; -} - -/** - * Bio Provider Interface (EIP-1193 style) - */ -export declare interface BioProvider { - /** Make a request to the provider */ - request(args: RequestArguments): Promise; - /** Subscribe to an event */ - on(event: string, handler: EventHandler): void; - /** Unsubscribe from an event */ - off(event: string, handler: EventHandler): void; - /** Check if connected */ - isConnected(): boolean; -} - -export declare class BioProviderImpl implements BioProvider { - private events; - private pendingRequests; - private requestIdCounter; - private connected; - private readonly targetOrigin; - constructor(targetOrigin?: string); - private setupMessageListener; - private handleMessage; - private handleResponse; - private handleEvent; - private connect; - private generateId; - private postMessage; - request(args: RequestArguments): Promise; - on(event: string, handler: EventHandler): void; - off(event: string, handler: EventHandler): void; - isConnected(): boolean; -} - -/** Signed transaction payload (chain-specific) */ -export declare interface BioSignedTransaction { - chainId: string; - data: unknown; - signature: string; -} - -/** Unsigned transaction payload (chain-specific) */ -export declare interface BioUnsignedTransaction { - chainId: string; - data: unknown; -} - -/** KeyApp chain ID to display name */ -export declare const CHAIN_DISPLAY_NAMES: Record; - -/** Create a provider RPC error */ -export declare function createProviderError(code: number, message: string, data?: unknown): ProviderRpcError; - -/** - * EIP-1193 Ethereum Provider Implementation - */ -export declare class EthereumProvider { - private events; - private pendingRequests; - private requestIdCounter; - private connected; - private currentChainId; - private accounts; - private readonly targetOrigin; - readonly isMetaMask = false; - readonly isKeyApp = true; - constructor(targetOrigin?: string); - private setupMessageListener; - private handleMessage; - private handleResponse; - private handleEvent; - private generateId; - private postMessage; - /** - * EIP-1193 request method - */ - request(args: EthRequestArguments): Promise; - /** - * Subscribe to an event - */ - on(event: string, handler: (...args: unknown[]) => void): this; - /** - * Unsubscribe from an event - */ - off(event: string, handler: (...args: unknown[]) => void): this; - /** - * Alias for off (Node.js EventEmitter compatibility) - */ - removeListener(event: string, handler: (...args: unknown[]) => void): this; - /** - * Add listener that fires only once - */ - once(event: string, handler: (...args: unknown[]) => void): this; - /** - * EIP-1193 isConnected method - */ - isConnected(): boolean; - /** - * Get current chain ID (cached) - */ - get chainId(): string | null; - /** - * Get selected address (first account) - */ - get selectedAddress(): string | null; - /** - * @deprecated Use request({ method: 'eth_requestAccounts' }) - */ - enable(): Promise; - /** - * @deprecated Use request() - */ - send(method: string, params?: unknown[]): Promise; - /** - * @deprecated Use request() - */ - sendAsync(payload: { - method: string; - params?: unknown[]; - id?: number; - }, callback: (error: Error | null, result?: { - result: unknown; - }) => void): void; -} - -/** - * Ethereum Provider (EIP-1193 Compatible) - * - * Provides window.ethereum for EVM-compatible dApps. - * Communicates with KeyApp host via postMessage. - */ -/** EIP-1193 Request Arguments */ -declare interface EthRequestArguments { - method: string; - params?: unknown[] | Record; -} - -export declare class EventEmitter { - private handlers; - on(event: string, handler: EventHandler): void; - off(event: string, handler: EventHandler): void; - emit(event: string, ...args: unknown[]): void; - removeAllListeners(event?: string): void; -} - -/** Event handler type */ -export declare type EventHandler = (...args: T[]) => void; - -/** Reverse mapping: EVM chainId -> KeyApp chain ID */ -export declare const EVM_CHAIN_ID_TO_KEYAPP: Record; - -/** EVM Chain ID mapping (decimal) */ -export declare const EVM_CHAIN_IDS: Record; - -/** - * Get EVM hex chain ID from KeyApp chain ID - * @example getEvmChainId('binance') => '0x38' - */ -export declare function getEvmChainId(keyAppChainId: string): string | null; - -/** - * Get KeyApp chain ID from EVM hex chain ID - * @example getKeyAppChainId('0x38') => 'binance' - */ -export declare function getKeyAppChainId(hexChainId: string): string | null; - -/** - * Initialize all providers (bio, ethereum, tron) - */ -export declare function initAllProviders(targetOrigin?: string): { - bio: BioProvider; - ethereum: EthereumProvider; - tronLink: TronLinkProvider; - tronWeb: TronWebProvider; -}; - -/** - * Initialize and inject the Bio provider into window.bio - */ -export declare function initBioProvider(targetOrigin?: string): BioProvider; - -/** - * Initialize and inject the Ethereum provider into window.ethereum - */ -export declare function initEthereumProvider(targetOrigin?: string): EthereumProvider; - -/** - * Initialize and inject the Tron providers - */ -export declare function initTronProvider(targetOrigin?: string): { - tronLink: TronLinkProvider; - tronWeb: TronWebProvider; -}; - -/** - * Check if a chain is EVM compatible - */ -export declare function isEvmChain(chainId: string): boolean; - -/** - * Normalize API chain name to KeyApp chain ID - * @example normalizeChainId('BSC') => 'binance' - */ -export declare function normalizeChainId(chainName: string): string; - -/** - * Parse hex chain ID to decimal - * @example parseHexChainId('0x38') => 56 - */ -export declare function parseHexChainId(hexChainId: string): number; - -/** Provider RPC error */ -export declare interface ProviderRpcError extends Error { - code: number; - data?: unknown; -} - -/** Provider request arguments */ -export declare interface RequestArguments { - method: string; - params?: unknown[]; -} - -/** - * Convert decimal chain ID to hex string (EIP-155 format) - * @example toHexChainId(56) => '0x38' - */ -export declare function toHexChainId(chainId: number): string; - -/** Transfer parameters */ -export declare interface TransferParams { - from: string; - to: string; - amount: string; - chain: string; - asset?: string; -} - -/** - * Tron Provider (TronLink Compatible) - * - * Provides window.tronWeb and window.tronLink for Tron dApps. - * Communicates with KeyApp host via postMessage. - */ -/** Tron address format */ -declare interface TronAddress { - base58: string; - hex: string; -} - -/** - * TronLink-compatible Provider - */ -export declare class TronLinkProvider { - private events; - private pendingRequests; - private requestIdCounter; - private readonly targetOrigin; - constructor(targetOrigin?: string); - private setupMessageListener; - private handleMessage; - private handleResponse; - private handleEvent; - private generateId; - private postMessage; - /** - * TronLink request method (EIP-1193 style) - */ - request(args: TronRequestArguments): Promise; - on(event: string, handler: (...args: unknown[]) => void): this; - off(event: string, handler: (...args: unknown[]) => void): this; -} - -/** TronLink request arguments (EIP-1193 style) */ -declare interface TronRequestArguments { - method: string; - params?: unknown; -} - -/** - * TronWeb-compatible API - * Provides the subset of TronWeb API that KeyApp supports - */ -export declare class TronWebProvider { - private tronLink; - private _ready; - private _defaultAddress; - /** TRX operations */ - readonly trx: TronWebTrx; - constructor(tronLink: TronLinkProvider); - /** Whether TronWeb is ready (connected) */ - get ready(): boolean; - /** Current default address */ - get defaultAddress(): TronAddress; - /** - * Set default address (called by host after connection) - */ - setAddress(address: TronAddress): void; - /** - * Check if an address is valid - */ - isAddress(address: string): boolean; - /** - * Convert address to hex format - */ - address: { - toHex: (base58: string) => string; - fromHex: (hex: string) => string; - }; -} - -/** - * TronWeb.trx operations - */ -declare class TronWebTrx { - private tronLink; - constructor(tronLink: TronLinkProvider); - /** - * Sign a transaction - */ - sign(transaction: unknown): Promise; - /** - * Send raw transaction (broadcast) - */ - sendRawTransaction(signedTransaction: unknown): Promise; - /** - * Get account balance - */ - getBalance(address: string): Promise; - /** - * Get account info - */ - getAccount(address: string): Promise; -} - -export { } diff --git a/packages/bio-sdk/dist/index.js b/packages/bio-sdk/dist/index.js deleted file mode 100644 index b1ba177e5..000000000 --- a/packages/bio-sdk/dist/index.js +++ /dev/null @@ -1,624 +0,0 @@ -const BioErrorCodes = { - USER_REJECTED: 4001, - UNAUTHORIZED: 4100, - UNSUPPORTED_METHOD: 4200, - DISCONNECTED: 4900, - CHAIN_DISCONNECTED: 4901, - INTERNAL_ERROR: -32603, - INVALID_PARAMS: -32602, - METHOD_NOT_FOUND: -32601 -}; -function createProviderError(code, message, data) { - const error = new Error(message); - error.code = code; - error.data = data; - return error; -} -class EventEmitter { - handlers = /* @__PURE__ */ new Map(); - on(event, handler) { - let handlers = this.handlers.get(event); - if (!handlers) { - handlers = /* @__PURE__ */ new Set(); - this.handlers.set(event, handlers); - } - handlers.add(handler); - } - off(event, handler) { - const handlers = this.handlers.get(event); - if (handlers) { - handlers.delete(handler); - if (handlers.size === 0) { - this.handlers.delete(event); - } - } - } - emit(event, ...args) { - const handlers = this.handlers.get(event); - if (handlers) { - handlers.forEach((handler) => { - try { - handler(...args); - } catch (error) { - console.error(`[BioSDK] Error in event handler for "${event}":`, error); - } - }); - } - } - removeAllListeners(event) { - if (event) { - this.handlers.delete(event); - } else { - this.handlers.clear(); - } - } -} -class BioProviderImpl { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - connected = false; - targetOrigin; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - this.connect(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "bio_response") { - this.handleResponse(data); - } else if (data.type === "bio_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - if (message.event === "connect") { - this.connected = true; - } else if (message.event === "disconnect") { - this.connected = false; - } - } - connect() { - this.postMessage({ - type: "bio_request", - id: this.generateId(), - method: "bio_connect", - params: [] - }); - } - generateId() { - return `bio_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[BioSDK] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - async request(args) { - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "bio_request", - id, - method: args.method, - params: args.params - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - on(event, handler) { - this.events.on(event, handler); - } - off(event, handler) { - this.events.off(event, handler); - } - isConnected() { - return this.connected; - } -} -class EthereumProvider { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - connected = false; - currentChainId = null; - accounts = []; - targetOrigin; - // EIP-1193 required properties - isMetaMask = false; - isKeyApp = true; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "eth_response") { - this.handleResponse(data); - } else if (data.type === "eth_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - if (message.event === "connect") { - this.connected = true; - const info = message.args[0]; - this.currentChainId = info?.chainId ?? null; - } else if (message.event === "disconnect") { - this.connected = false; - this.accounts = []; - } else if (message.event === "chainChanged") { - this.currentChainId = message.args[0]; - } else if (message.event === "accountsChanged") { - this.accounts = message.args[0]; - } - } - generateId() { - return `eth_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[EthereumProvider] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - /** - * EIP-1193 request method - */ - async request(args) { - const { method, params } = args; - const paramsArray = Array.isArray(params) ? params : params ? [params] : []; - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "eth_request", - id, - method, - params: paramsArray - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - /** - * Subscribe to an event - */ - on(event, handler) { - this.events.on(event, handler); - return this; - } - /** - * Unsubscribe from an event - */ - off(event, handler) { - this.events.off(event, handler); - return this; - } - /** - * Alias for off (Node.js EventEmitter compatibility) - */ - removeListener(event, handler) { - return this.off(event, handler); - } - /** - * Add listener that fires only once - */ - once(event, handler) { - const wrapper = (...args) => { - this.off(event, wrapper); - handler(...args); - }; - this.on(event, wrapper); - return this; - } - /** - * EIP-1193 isConnected method - */ - isConnected() { - return this.connected; - } - /** - * Get current chain ID (cached) - */ - get chainId() { - return this.currentChainId; - } - /** - * Get selected address (first account) - */ - get selectedAddress() { - return this.accounts[0] ?? null; - } - // ============================================ - // Legacy methods (for backwards compatibility) - // ============================================ - /** - * @deprecated Use request({ method: 'eth_requestAccounts' }) - */ - async enable() { - return this.request({ method: "eth_requestAccounts" }); - } - /** - * @deprecated Use request() - */ - send(method, params) { - return this.request({ method, params }); - } - /** - * @deprecated Use request() - */ - sendAsync(payload, callback) { - this.request({ method: payload.method, params: payload.params }).then((result) => callback(null, { result })).catch((error) => callback(error)); - } -} -function initEthereumProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[EthereumProvider] Cannot initialize: window is not defined"); - } - if (window.ethereum) { - console.warn("[EthereumProvider] Provider already exists, returning existing instance"); - return window.ethereum; - } - const provider = new EthereumProvider(targetOrigin); - window.ethereum = provider; - console.log("[EthereumProvider] Provider initialized"); - return provider; -} -class TronLinkProvider { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - targetOrigin; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "tron_response") { - this.handleResponse(data); - } else if (data.type === "tron_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - } - generateId() { - return `tron_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[TronLinkProvider] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - /** - * TronLink request method (EIP-1193 style) - */ - async request(args) { - const { method, params } = args; - const paramsArray = Array.isArray(params) ? params : params !== void 0 ? [params] : []; - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "tron_request", - id, - method, - params: paramsArray - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - on(event, handler) { - this.events.on(event, handler); - return this; - } - off(event, handler) { - this.events.off(event, handler); - return this; - } -} -class TronWebProvider { - tronLink; - _ready = false; - _defaultAddress = { base58: "", hex: "" }; - /** TRX operations */ - trx; - constructor(tronLink) { - this.tronLink = tronLink; - this.trx = new TronWebTrx(tronLink); - tronLink.on("accountsChanged", (accounts) => { - if (Array.isArray(accounts) && accounts.length > 0) { - const addr = accounts[0]; - this._defaultAddress = addr; - this._ready = true; - } else { - this._defaultAddress = { base58: "", hex: "" }; - this._ready = false; - } - }); - } - /** Whether TronWeb is ready (connected) */ - get ready() { - return this._ready; - } - /** Current default address */ - get defaultAddress() { - return this._defaultAddress; - } - /** - * Set default address (called by host after connection) - */ - setAddress(address) { - this._defaultAddress = address; - this._ready = true; - } - /** - * Check if an address is valid - */ - isAddress(address) { - if (address.startsWith("T")) { - return address.length === 34; - } - if (address.startsWith("41")) { - return address.length === 42; - } - return false; - } - /** - * Convert address to hex format - */ - address = { - toHex: (base58) => { - return base58; - }, - fromHex: (hex) => { - return hex; - } - }; -} -class TronWebTrx { - tronLink; - constructor(tronLink) { - this.tronLink = tronLink; - } - /** - * Sign a transaction - */ - async sign(transaction) { - return this.tronLink.request({ - method: "tron_signTransaction", - params: transaction - }); - } - /** - * Send raw transaction (broadcast) - */ - async sendRawTransaction(signedTransaction) { - return this.tronLink.request({ - method: "tron_sendRawTransaction", - params: signedTransaction - }); - } - /** - * Get account balance - */ - async getBalance(address) { - return this.tronLink.request({ - method: "tron_getBalance", - params: address - }); - } - /** - * Get account info - */ - async getAccount(address) { - return this.tronLink.request({ - method: "tron_getAccount", - params: address - }); - } -} -function initTronProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[TronProvider] Cannot initialize: window is not defined"); - } - if (window.tronLink && window.tronWeb) { - console.warn("[TronProvider] Providers already exist, returning existing instances"); - return { tronLink: window.tronLink, tronWeb: window.tronWeb }; - } - const tronLink = new TronLinkProvider(targetOrigin); - const tronWeb = new TronWebProvider(tronLink); - window.tronLink = tronLink; - window.tronWeb = tronWeb; - console.log("[TronProvider] Providers initialized"); - return { tronLink, tronWeb }; -} -const EVM_CHAIN_IDS = { - ethereum: 1, - binance: 56 - // Future chains - // polygon: 137, - // arbitrum: 42161, - // optimism: 10, -}; -const EVM_CHAIN_ID_TO_KEYAPP = Object.fromEntries( - Object.entries(EVM_CHAIN_IDS).map(([key, value]) => [value, key]) -); -const API_CHAIN_TO_KEYAPP = { - ETH: "ethereum", - BSC: "binance", - TRON: "tron", - BFMCHAIN: "bfmeta", - BFCHAIN: "bfchain", - // Lowercase variants - eth: "ethereum", - bsc: "binance", - tron: "tron", - bfmchain: "bfmeta", - bfchain: "bfchain" -}; -const CHAIN_DISPLAY_NAMES = { - ethereum: "Ethereum", - binance: "BNB Smart Chain", - tron: "Tron", - bfmeta: "BFMeta", - bfchain: "BFChain" -}; -function toHexChainId(chainId) { - return `0x${chainId.toString(16)}`; -} -function parseHexChainId(hexChainId) { - if (!hexChainId.startsWith("0x")) { - throw new Error(`Invalid hex chain ID: ${hexChainId}`); - } - return parseInt(hexChainId, 16); -} -function getKeyAppChainId(hexChainId) { - const decimal = parseHexChainId(hexChainId); - return EVM_CHAIN_ID_TO_KEYAPP[decimal] ?? null; -} -function getEvmChainId(keyAppChainId) { - const decimal = EVM_CHAIN_IDS[keyAppChainId]; - return decimal ? toHexChainId(decimal) : null; -} -function isEvmChain(chainId) { - return chainId in EVM_CHAIN_IDS; -} -function normalizeChainId(chainName) { - return API_CHAIN_TO_KEYAPP[chainName] ?? chainName.toLowerCase(); -} -function initBioProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[BioSDK] Cannot initialize: window is not defined"); - } - if (window.bio) { - console.warn("[BioSDK] Provider already exists, returning existing instance"); - return window.bio; - } - const provider = new BioProviderImpl(targetOrigin); - window.bio = provider; - console.log("[BioSDK] Provider initialized"); - return provider; -} -function initAllProviders(targetOrigin = "*") { - const bio = initBioProvider(targetOrigin); - const ethereum = initEthereumProvider(targetOrigin); - const { tronLink, tronWeb } = initTronProvider(targetOrigin); - return { bio, ethereum, tronLink, tronWeb }; -} -if (typeof window !== "undefined") { - const init = () => { - initBioProvider(); - initEthereumProvider(); - initTronProvider(); - }; - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", init); - } else { - init(); - } -} -export { - API_CHAIN_TO_KEYAPP, - BioErrorCodes, - BioProviderImpl, - CHAIN_DISPLAY_NAMES, - EVM_CHAIN_IDS, - EVM_CHAIN_ID_TO_KEYAPP, - EthereumProvider, - EventEmitter, - TronLinkProvider, - TronWebProvider, - createProviderError, - getEvmChainId, - getKeyAppChainId, - initAllProviders, - initBioProvider, - initEthereumProvider, - initTronProvider, - isEvmChain, - normalizeChainId, - parseHexChainId, - toHexChainId -}; -//# sourceMappingURL=index.js.map diff --git a/packages/bio-sdk/dist/index.js.map b/packages/bio-sdk/dist/index.js.map deleted file mode 100644 index 568249e36..000000000 --- a/packages/bio-sdk/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sources":["../src/types.ts","../src/events.ts","../src/provider.ts","../src/ethereum-provider.ts","../src/tron-provider.ts","../src/chain-id.ts","../src/index.ts"],"sourcesContent":["/**\n * Bio SDK Types\n * EIP-1193 style provider interface for Bio ecosystem\n */\n\n/** Account information */\nexport interface BioAccount {\n address: string\n chain: string\n name?: string\n /** Public key (hex encoded) */\n publicKey: string\n}\n\n/** Transfer parameters */\nexport interface TransferParams {\n from: string\n to: string\n amount: string\n chain: string\n asset?: string\n}\n\n/** Unsigned transaction payload (chain-specific) */\nexport interface BioUnsignedTransaction {\n chainId: string\n data: unknown\n}\n\n/** Signed transaction payload (chain-specific) */\nexport interface BioSignedTransaction {\n chainId: string\n data: unknown\n signature: string\n}\n\n/** Provider request arguments */\nexport interface RequestArguments {\n method: string\n params?: unknown[]\n}\n\n/** Provider RPC error */\nexport interface ProviderRpcError extends Error {\n code: number\n data?: unknown\n}\n\n/** Event handler type */\nexport type EventHandler = (...args: T[]) => void\n\n/**\n * Bio Provider Interface (EIP-1193 style)\n */\nexport interface BioProvider {\n /** Make a request to the provider */\n request(args: RequestArguments): Promise\n\n /** Subscribe to an event */\n on(event: string, handler: EventHandler): void\n\n /** Unsubscribe from an event */\n off(event: string, handler: EventHandler): void\n\n /** Check if connected */\n isConnected(): boolean\n}\n\n/**\n * Bio method definitions\n */\nexport interface BioMethods {\n /** Request wallet accounts (shows connection UI) */\n bio_requestAccounts: () => Promise\n\n /** Get connected accounts (no UI) */\n bio_accounts: () => Promise\n\n /** Select an account (shows account picker UI) */\n bio_selectAccount: (opts?: { chain?: string }) => Promise\n\n /** Pick another wallet address (shows wallet picker UI) */\n bio_pickWallet: (opts?: { chain?: string; exclude?: string }) => Promise\n\n /** Sign a message, returns signature and public key (hex) */\n bio_signMessage: (params: { message: string; address: string }) => Promise<{ signature: string; publicKey: string }>\n\n /** Sign typed data, returns signature and public key (hex) */\n bio_signTypedData: (params: { data: object; address: string }) => Promise<{ signature: string; publicKey: string }>\n\n /** Create an unsigned transaction (no signature, no broadcast) */\n bio_createTransaction: (params: TransferParams) => Promise\n\n /** Sign an unsigned transaction (requires user confirmation) */\n bio_signTransaction: (params: { from: string; chain: string; unsignedTx: BioUnsignedTransaction }) => Promise\n\n /** Send a transaction */\n bio_sendTransaction: (params: TransferParams) => Promise<{ txHash: string }>\n\n /** Get current chain ID */\n bio_chainId: () => Promise\n\n /** Get balance */\n bio_getBalance: (params: { address: string; chain: string }) => Promise\n\n /** Close splash screen (indicates app is ready) */\n bio_closeSplashScreen: () => Promise\n}\n\n/**\n * Bio event definitions\n */\nexport interface BioEvents {\n /** Emitted when accounts change */\n accountsChanged: (accounts: BioAccount[]) => void\n\n /** Emitted when chain changes */\n chainChanged: (chainId: string) => void\n\n /** Emitted when connected */\n connect: (info: { chainId: string }) => void\n\n /** Emitted when disconnected */\n disconnect: (error: { code: number; message: string }) => void\n}\n\n/** Method names */\nexport type BioMethodName = keyof BioMethods\n\n/** Event names */\nexport type BioEventName = keyof BioEvents\n\n/** RPC error codes */\nexport const BioErrorCodes = {\n USER_REJECTED: 4001,\n UNAUTHORIZED: 4100,\n UNSUPPORTED_METHOD: 4200,\n DISCONNECTED: 4900,\n CHAIN_DISCONNECTED: 4901,\n INTERNAL_ERROR: -32603,\n INVALID_PARAMS: -32602,\n METHOD_NOT_FOUND: -32601,\n} as const\n\n/** Create a provider RPC error */\nexport function createProviderError(code: number, message: string, data?: unknown): ProviderRpcError {\n const error = new Error(message) as ProviderRpcError\n error.code = code\n error.data = data\n return error\n}\n","/**\n * Event emitter for Bio SDK\n */\n\nimport type { EventHandler } from './types'\n\nexport class EventEmitter {\n private handlers = new Map>()\n\n on(event: string, handler: EventHandler): void {\n let handlers = this.handlers.get(event)\n if (!handlers) {\n handlers = new Set()\n this.handlers.set(event, handlers)\n }\n handlers.add(handler)\n }\n\n off(event: string, handler: EventHandler): void {\n const handlers = this.handlers.get(event)\n if (handlers) {\n handlers.delete(handler)\n if (handlers.size === 0) {\n this.handlers.delete(event)\n }\n }\n }\n\n emit(event: string, ...args: unknown[]): void {\n const handlers = this.handlers.get(event)\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(...args)\n } catch (error) {\n console.error(`[BioSDK] Error in event handler for \"${event}\":`, error)\n }\n })\n }\n }\n\n removeAllListeners(event?: string): void {\n if (event) {\n this.handlers.delete(event)\n } else {\n this.handlers.clear()\n }\n }\n}\n","/**\n * Bio Provider Implementation\n * Communicates with KeyApp host via postMessage\n */\n\nimport type { BioProvider, RequestArguments, EventHandler } from './types'\nimport { BioErrorCodes, createProviderError } from './types'\nimport { EventEmitter } from './events'\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'bio_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'bio_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'bio_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\nexport class BioProviderImpl implements BioProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private connected = false\n private readonly targetOrigin: string\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n this.connect()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'bio_response') {\n this.handleResponse(data)\n } else if (data.type === 'bio_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n\n // Handle built-in events\n if (message.event === 'connect') {\n this.connected = true\n } else if (message.event === 'disconnect') {\n this.connected = false\n }\n }\n\n private connect(): void {\n // Send handshake to host\n this.postMessage({\n type: 'bio_request',\n id: this.generateId(),\n method: 'bio_connect',\n params: [],\n })\n }\n\n private generateId(): string {\n return `bio_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[BioSDK] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n async request(args: RequestArguments): Promise {\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'bio_request',\n id,\n method: args.method,\n params: args.params,\n })\n\n // Timeout after 5 minutes (for user interactions)\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n on(event: string, handler: EventHandler): void {\n this.events.on(event, handler)\n }\n\n off(event: string, handler: EventHandler): void {\n this.events.off(event, handler)\n }\n\n isConnected(): boolean {\n return this.connected\n }\n}\n","/**\n * Ethereum Provider (EIP-1193 Compatible)\n *\n * Provides window.ethereum for EVM-compatible dApps.\n * Communicates with KeyApp host via postMessage.\n */\n\nimport { EventEmitter } from './events'\nimport { BioErrorCodes, createProviderError, type ProviderRpcError } from './types'\nimport { toHexChainId, parseHexChainId, getKeyAppChainId, EVM_CHAIN_IDS } from './chain-id'\n\n/** EIP-1193 Request Arguments */\nexport interface EthRequestArguments {\n method: string\n params?: unknown[] | Record\n}\n\n/** EIP-1193 Provider Connect Info */\nexport interface ProviderConnectInfo {\n chainId: string\n}\n\n/** EIP-1193 Provider Message */\nexport interface ProviderMessage {\n type: string\n data: unknown\n}\n\n/** Transaction request (eth_sendTransaction) */\nexport interface TransactionRequest {\n from: string\n to?: string\n value?: string\n data?: string\n gas?: string\n gasPrice?: string\n maxFeePerGas?: string\n maxPriorityFeePerGas?: string\n nonce?: string\n}\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'eth_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'eth_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'eth_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\n/**\n * EIP-1193 Ethereum Provider Implementation\n */\nexport class EthereumProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private connected = false\n private currentChainId: string | null = null\n private accounts: string[] = []\n private readonly targetOrigin: string\n\n // EIP-1193 required properties\n readonly isMetaMask = false\n readonly isKeyApp = true\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'eth_response') {\n this.handleResponse(data)\n } else if (data.type === 'eth_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n\n // Handle built-in events\n if (message.event === 'connect') {\n this.connected = true\n const info = message.args[0] as ProviderConnectInfo\n this.currentChainId = info?.chainId ?? null\n } else if (message.event === 'disconnect') {\n this.connected = false\n this.accounts = []\n } else if (message.event === 'chainChanged') {\n this.currentChainId = message.args[0] as string\n } else if (message.event === 'accountsChanged') {\n this.accounts = message.args[0] as string[]\n }\n }\n\n private generateId(): string {\n return `eth_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[EthereumProvider] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n /**\n * EIP-1193 request method\n */\n async request(args: EthRequestArguments): Promise {\n const { method, params } = args\n const paramsArray = Array.isArray(params) ? params : params ? [params] : []\n\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'eth_request',\n id,\n method,\n params: paramsArray,\n })\n\n // Timeout after 5 minutes (for user interactions)\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n /**\n * Subscribe to an event\n */\n on(event: string, handler: (...args: unknown[]) => void): this {\n this.events.on(event, handler)\n return this\n }\n\n /**\n * Unsubscribe from an event\n */\n off(event: string, handler: (...args: unknown[]) => void): this {\n this.events.off(event, handler)\n return this\n }\n\n /**\n * Alias for off (Node.js EventEmitter compatibility)\n */\n removeListener(event: string, handler: (...args: unknown[]) => void): this {\n return this.off(event, handler)\n }\n\n /**\n * Add listener that fires only once\n */\n once(event: string, handler: (...args: unknown[]) => void): this {\n const wrapper = (...args: unknown[]) => {\n this.off(event, wrapper)\n handler(...args)\n }\n this.on(event, wrapper)\n return this\n }\n\n /**\n * EIP-1193 isConnected method\n */\n isConnected(): boolean {\n return this.connected\n }\n\n /**\n * Get current chain ID (cached)\n */\n get chainId(): string | null {\n return this.currentChainId\n }\n\n /**\n * Get selected address (first account)\n */\n get selectedAddress(): string | null {\n return this.accounts[0] ?? null\n }\n\n // ============================================\n // Legacy methods (for backwards compatibility)\n // ============================================\n\n /**\n * @deprecated Use request({ method: 'eth_requestAccounts' })\n */\n async enable(): Promise {\n return this.request({ method: 'eth_requestAccounts' })\n }\n\n /**\n * @deprecated Use request()\n */\n send(method: string, params?: unknown[]): Promise {\n return this.request({ method, params })\n }\n\n /**\n * @deprecated Use request()\n */\n sendAsync(\n payload: { method: string; params?: unknown[]; id?: number },\n callback: (error: Error | null, result?: { result: unknown }) => void\n ): void {\n this.request({ method: payload.method, params: payload.params })\n .then((result) => callback(null, { result }))\n .catch((error) => callback(error))\n }\n}\n\n// Extend Window interface\ndeclare global {\n interface Window {\n ethereum?: EthereumProvider\n }\n}\n\n/**\n * Initialize and inject the Ethereum provider into window.ethereum\n */\nexport function initEthereumProvider(targetOrigin = '*'): EthereumProvider {\n if (typeof window === 'undefined') {\n throw new Error('[EthereumProvider] Cannot initialize: window is not defined')\n }\n\n if (window.ethereum) {\n console.warn('[EthereumProvider] Provider already exists, returning existing instance')\n return window.ethereum\n }\n\n const provider = new EthereumProvider(targetOrigin)\n window.ethereum = provider\n\n console.log('[EthereumProvider] Provider initialized')\n return provider\n}\n","/**\n * Tron Provider (TronLink Compatible)\n *\n * Provides window.tronWeb and window.tronLink for Tron dApps.\n * Communicates with KeyApp host via postMessage.\n */\n\nimport { EventEmitter } from './events'\nimport { BioErrorCodes, createProviderError } from './types'\n\n/** Tron address format */\nexport interface TronAddress {\n base58: string\n hex: string\n}\n\n/** TronLink request arguments (EIP-1193 style) */\nexport interface TronRequestArguments {\n method: string\n params?: unknown\n}\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'tron_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'tron_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'tron_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\n/**\n * TronLink-compatible Provider\n */\nexport class TronLinkProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private readonly targetOrigin: string\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'tron_response') {\n this.handleResponse(data)\n } else if (data.type === 'tron_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n }\n\n private generateId(): string {\n return `tron_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[TronLinkProvider] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n /**\n * TronLink request method (EIP-1193 style)\n */\n async request(args: TronRequestArguments): Promise {\n const { method, params } = args\n const paramsArray = Array.isArray(params) ? params : params !== undefined ? [params] : []\n\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'tron_request',\n id,\n method,\n params: paramsArray,\n })\n\n // Timeout after 5 minutes\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n on(event: string, handler: (...args: unknown[]) => void): this {\n this.events.on(event, handler)\n return this\n }\n\n off(event: string, handler: (...args: unknown[]) => void): this {\n this.events.off(event, handler)\n return this\n }\n}\n\n/**\n * TronWeb-compatible API\n * Provides the subset of TronWeb API that KeyApp supports\n */\nexport class TronWebProvider {\n private tronLink: TronLinkProvider\n private _ready = false\n private _defaultAddress: TronAddress = { base58: '', hex: '' }\n\n /** TRX operations */\n readonly trx: TronWebTrx\n\n constructor(tronLink: TronLinkProvider) {\n this.tronLink = tronLink\n this.trx = new TronWebTrx(tronLink)\n\n // Listen for account changes\n tronLink.on('accountsChanged', (accounts: unknown) => {\n if (Array.isArray(accounts) && accounts.length > 0) {\n const addr = accounts[0] as TronAddress\n this._defaultAddress = addr\n this._ready = true\n } else {\n this._defaultAddress = { base58: '', hex: '' }\n this._ready = false\n }\n })\n }\n\n /** Whether TronWeb is ready (connected) */\n get ready(): boolean {\n return this._ready\n }\n\n /** Current default address */\n get defaultAddress(): TronAddress {\n return this._defaultAddress\n }\n\n /**\n * Set default address (called by host after connection)\n */\n setAddress(address: TronAddress): void {\n this._defaultAddress = address\n this._ready = true\n }\n\n /**\n * Check if an address is valid\n */\n isAddress(address: string): boolean {\n // Basic validation: base58 starts with T, hex starts with 41\n if (address.startsWith('T')) {\n return address.length === 34\n }\n if (address.startsWith('41')) {\n return address.length === 42\n }\n return false\n }\n\n /**\n * Convert address to hex format\n */\n address = {\n toHex: (base58: string): string => {\n // This is a stub - actual conversion requires TronWeb library\n // KeyApp will handle the conversion on the host side\n return base58\n },\n fromHex: (hex: string): string => {\n return hex\n },\n }\n}\n\n/**\n * TronWeb.trx operations\n */\nclass TronWebTrx {\n private tronLink: TronLinkProvider\n\n constructor(tronLink: TronLinkProvider) {\n this.tronLink = tronLink\n }\n\n /**\n * Sign a transaction\n */\n async sign(transaction: unknown): Promise {\n return this.tronLink.request({\n method: 'tron_signTransaction',\n params: transaction,\n })\n }\n\n /**\n * Send raw transaction (broadcast)\n */\n async sendRawTransaction(signedTransaction: unknown): Promise {\n return this.tronLink.request({\n method: 'tron_sendRawTransaction',\n params: signedTransaction,\n })\n }\n\n /**\n * Get account balance\n */\n async getBalance(address: string): Promise {\n return this.tronLink.request({\n method: 'tron_getBalance',\n params: address,\n })\n }\n\n /**\n * Get account info\n */\n async getAccount(address: string): Promise {\n return this.tronLink.request({\n method: 'tron_getAccount',\n params: address,\n })\n }\n}\n\n// Extend Window interface\ndeclare global {\n interface Window {\n tronLink?: TronLinkProvider\n tronWeb?: TronWebProvider\n }\n}\n\n/**\n * Initialize and inject the Tron providers\n */\nexport function initTronProvider(targetOrigin = '*'): { tronLink: TronLinkProvider; tronWeb: TronWebProvider } {\n if (typeof window === 'undefined') {\n throw new Error('[TronProvider] Cannot initialize: window is not defined')\n }\n\n if (window.tronLink && window.tronWeb) {\n console.warn('[TronProvider] Providers already exist, returning existing instances')\n return { tronLink: window.tronLink, tronWeb: window.tronWeb }\n }\n\n const tronLink = new TronLinkProvider(targetOrigin)\n const tronWeb = new TronWebProvider(tronLink)\n\n window.tronLink = tronLink\n window.tronWeb = tronWeb\n\n console.log('[TronProvider] Providers initialized')\n return { tronLink, tronWeb }\n}\n","/**\n * Chain ID utilities\n * Maps between KeyApp internal chain IDs and standard chain IDs\n */\n\n/** EVM Chain ID mapping (decimal) */\nexport const EVM_CHAIN_IDS: Record = {\n ethereum: 1,\n binance: 56,\n // Future chains\n // polygon: 137,\n // arbitrum: 42161,\n // optimism: 10,\n} as const\n\n/** Reverse mapping: EVM chainId -> KeyApp chain ID */\nexport const EVM_CHAIN_ID_TO_KEYAPP: Record = Object.fromEntries(\n Object.entries(EVM_CHAIN_IDS).map(([key, value]) => [value, key])\n)\n\n/** API chain name to KeyApp chain ID mapping */\nexport const API_CHAIN_TO_KEYAPP: Record = {\n ETH: 'ethereum',\n BSC: 'binance',\n TRON: 'tron',\n BFMCHAIN: 'bfmeta',\n BFCHAIN: 'bfchain',\n // Lowercase variants\n eth: 'ethereum',\n bsc: 'binance',\n tron: 'tron',\n bfmchain: 'bfmeta',\n bfchain: 'bfchain',\n} as const\n\n/** KeyApp chain ID to display name */\nexport const CHAIN_DISPLAY_NAMES: Record = {\n ethereum: 'Ethereum',\n binance: 'BNB Smart Chain',\n tron: 'Tron',\n bfmeta: 'BFMeta',\n bfchain: 'BFChain',\n} as const\n\n/**\n * Convert decimal chain ID to hex string (EIP-155 format)\n * @example toHexChainId(56) => '0x38'\n */\nexport function toHexChainId(chainId: number): string {\n return `0x${chainId.toString(16)}`\n}\n\n/**\n * Parse hex chain ID to decimal\n * @example parseHexChainId('0x38') => 56\n */\nexport function parseHexChainId(hexChainId: string): number {\n if (!hexChainId.startsWith('0x')) {\n throw new Error(`Invalid hex chain ID: ${hexChainId}`)\n }\n return parseInt(hexChainId, 16)\n}\n\n/**\n * Get KeyApp chain ID from EVM hex chain ID\n * @example getKeyAppChainId('0x38') => 'binance'\n */\nexport function getKeyAppChainId(hexChainId: string): string | null {\n const decimal = parseHexChainId(hexChainId)\n return EVM_CHAIN_ID_TO_KEYAPP[decimal] ?? null\n}\n\n/**\n * Get EVM hex chain ID from KeyApp chain ID\n * @example getEvmChainId('binance') => '0x38'\n */\nexport function getEvmChainId(keyAppChainId: string): string | null {\n const decimal = EVM_CHAIN_IDS[keyAppChainId]\n return decimal ? toHexChainId(decimal) : null\n}\n\n/**\n * Check if a chain is EVM compatible\n */\nexport function isEvmChain(chainId: string): boolean {\n return chainId in EVM_CHAIN_IDS\n}\n\n/**\n * Normalize API chain name to KeyApp chain ID\n * @example normalizeChainId('BSC') => 'binance'\n */\nexport function normalizeChainId(chainName: string): string {\n return API_CHAIN_TO_KEYAPP[chainName] ?? chainName.toLowerCase()\n}\n","/**\n * Bio SDK - Client SDK for Bio Ecosystem MiniApps\n *\n * Injects providers for multi-chain dApp support:\n * - `window.bio` - BioChain + KeyApp wallet features\n * - `window.ethereum` - EVM-compatible chains (ETH, BSC)\n * - `window.tronWeb` / `window.tronLink` - Tron chain\n *\n * @example\n * ```typescript\n * import '@biochain/bio-sdk'\n *\n * // BioChain operations\n * const accounts = await window.bio.request({ method: 'bio_requestAccounts' })\n *\n * // EVM operations (ETH, BSC)\n * const ethAccounts = await window.ethereum.request({ method: 'eth_requestAccounts' })\n *\n * // Tron operations\n * const tronAccounts = await window.tronLink.request({ method: 'tron_requestAccounts' })\n * ```\n */\n\nimport { BioProviderImpl } from './provider'\nimport { EthereumProvider, initEthereumProvider } from './ethereum-provider'\nimport { TronLinkProvider, TronWebProvider, initTronProvider } from './tron-provider'\nimport type { BioProvider } from './types'\n\n// Re-export types\nexport * from './types'\nexport * from './chain-id'\nexport { EventEmitter } from './events'\nexport { BioProviderImpl } from './provider'\nexport { EthereumProvider, initEthereumProvider } from './ethereum-provider'\nexport { TronLinkProvider, TronWebProvider, initTronProvider } from './tron-provider'\n\n// Extend Window interface (bio is declared in types.ts already for ethereum/tron)\ndeclare global {\n interface Window {\n bio?: BioProvider\n }\n}\n\n/**\n * Initialize and inject the Bio provider into window.bio\n */\nexport function initBioProvider(targetOrigin = '*'): BioProvider {\n if (typeof window === 'undefined') {\n throw new Error('[BioSDK] Cannot initialize: window is not defined')\n }\n\n if (window.bio) {\n console.warn('[BioSDK] Provider already exists, returning existing instance')\n return window.bio\n }\n\n const provider = new BioProviderImpl(targetOrigin)\n window.bio = provider\n\n console.log('[BioSDK] Provider initialized')\n return provider\n}\n\n/**\n * Initialize all providers (bio, ethereum, tron)\n */\nexport function initAllProviders(targetOrigin = '*'): {\n bio: BioProvider\n ethereum: EthereumProvider\n tronLink: TronLinkProvider\n tronWeb: TronWebProvider\n} {\n const bio = initBioProvider(targetOrigin)\n const ethereum = initEthereumProvider(targetOrigin)\n const { tronLink, tronWeb } = initTronProvider(targetOrigin)\n\n return { bio, ethereum, tronLink, tronWeb }\n}\n\n// Auto-initialize if running in browser\nif (typeof window !== 'undefined') {\n const init = () => {\n initBioProvider()\n initEthereumProvider()\n initTronProvider()\n }\n\n // Use a slight delay to ensure DOM is ready\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init)\n } else {\n init()\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/bio-sdk/dist/index.umd.js b/packages/bio-sdk/dist/index.umd.js deleted file mode 100644 index c4e0a6fae..000000000 --- a/packages/bio-sdk/dist/index.umd.js +++ /dev/null @@ -1,628 +0,0 @@ -(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.BioSDK = {})); -})(this, (function(exports2) { - "use strict"; - const BioErrorCodes = { - USER_REJECTED: 4001, - UNAUTHORIZED: 4100, - UNSUPPORTED_METHOD: 4200, - DISCONNECTED: 4900, - CHAIN_DISCONNECTED: 4901, - INTERNAL_ERROR: -32603, - INVALID_PARAMS: -32602, - METHOD_NOT_FOUND: -32601 - }; - function createProviderError(code, message, data) { - const error = new Error(message); - error.code = code; - error.data = data; - return error; - } - class EventEmitter { - handlers = /* @__PURE__ */ new Map(); - on(event, handler) { - let handlers = this.handlers.get(event); - if (!handlers) { - handlers = /* @__PURE__ */ new Set(); - this.handlers.set(event, handlers); - } - handlers.add(handler); - } - off(event, handler) { - const handlers = this.handlers.get(event); - if (handlers) { - handlers.delete(handler); - if (handlers.size === 0) { - this.handlers.delete(event); - } - } - } - emit(event, ...args) { - const handlers = this.handlers.get(event); - if (handlers) { - handlers.forEach((handler) => { - try { - handler(...args); - } catch (error) { - console.error(`[BioSDK] Error in event handler for "${event}":`, error); - } - }); - } - } - removeAllListeners(event) { - if (event) { - this.handlers.delete(event); - } else { - this.handlers.clear(); - } - } - } - class BioProviderImpl { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - connected = false; - targetOrigin; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - this.connect(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "bio_response") { - this.handleResponse(data); - } else if (data.type === "bio_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - if (message.event === "connect") { - this.connected = true; - } else if (message.event === "disconnect") { - this.connected = false; - } - } - connect() { - this.postMessage({ - type: "bio_request", - id: this.generateId(), - method: "bio_connect", - params: [] - }); - } - generateId() { - return `bio_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[BioSDK] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - async request(args) { - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "bio_request", - id, - method: args.method, - params: args.params - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - on(event, handler) { - this.events.on(event, handler); - } - off(event, handler) { - this.events.off(event, handler); - } - isConnected() { - return this.connected; - } - } - class EthereumProvider { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - connected = false; - currentChainId = null; - accounts = []; - targetOrigin; - // EIP-1193 required properties - isMetaMask = false; - isKeyApp = true; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "eth_response") { - this.handleResponse(data); - } else if (data.type === "eth_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - if (message.event === "connect") { - this.connected = true; - const info = message.args[0]; - this.currentChainId = info?.chainId ?? null; - } else if (message.event === "disconnect") { - this.connected = false; - this.accounts = []; - } else if (message.event === "chainChanged") { - this.currentChainId = message.args[0]; - } else if (message.event === "accountsChanged") { - this.accounts = message.args[0]; - } - } - generateId() { - return `eth_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[EthereumProvider] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - /** - * EIP-1193 request method - */ - async request(args) { - const { method, params } = args; - const paramsArray = Array.isArray(params) ? params : params ? [params] : []; - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "eth_request", - id, - method, - params: paramsArray - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - /** - * Subscribe to an event - */ - on(event, handler) { - this.events.on(event, handler); - return this; - } - /** - * Unsubscribe from an event - */ - off(event, handler) { - this.events.off(event, handler); - return this; - } - /** - * Alias for off (Node.js EventEmitter compatibility) - */ - removeListener(event, handler) { - return this.off(event, handler); - } - /** - * Add listener that fires only once - */ - once(event, handler) { - const wrapper = (...args) => { - this.off(event, wrapper); - handler(...args); - }; - this.on(event, wrapper); - return this; - } - /** - * EIP-1193 isConnected method - */ - isConnected() { - return this.connected; - } - /** - * Get current chain ID (cached) - */ - get chainId() { - return this.currentChainId; - } - /** - * Get selected address (first account) - */ - get selectedAddress() { - return this.accounts[0] ?? null; - } - // ============================================ - // Legacy methods (for backwards compatibility) - // ============================================ - /** - * @deprecated Use request({ method: 'eth_requestAccounts' }) - */ - async enable() { - return this.request({ method: "eth_requestAccounts" }); - } - /** - * @deprecated Use request() - */ - send(method, params) { - return this.request({ method, params }); - } - /** - * @deprecated Use request() - */ - sendAsync(payload, callback) { - this.request({ method: payload.method, params: payload.params }).then((result) => callback(null, { result })).catch((error) => callback(error)); - } - } - function initEthereumProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[EthereumProvider] Cannot initialize: window is not defined"); - } - if (window.ethereum) { - console.warn("[EthereumProvider] Provider already exists, returning existing instance"); - return window.ethereum; - } - const provider = new EthereumProvider(targetOrigin); - window.ethereum = provider; - console.log("[EthereumProvider] Provider initialized"); - return provider; - } - class TronLinkProvider { - events = new EventEmitter(); - pendingRequests = /* @__PURE__ */ new Map(); - requestIdCounter = 0; - targetOrigin; - constructor(targetOrigin = "*") { - this.targetOrigin = targetOrigin; - this.setupMessageListener(); - } - setupMessageListener() { - window.addEventListener("message", this.handleMessage.bind(this)); - } - handleMessage(event) { - const data = event.data; - if (!data || typeof data !== "object") return; - if (data.type === "tron_response") { - this.handleResponse(data); - } else if (data.type === "tron_event") { - this.handleEvent(data); - } - } - handleResponse(message) { - const pending = this.pendingRequests.get(message.id); - if (!pending) return; - this.pendingRequests.delete(message.id); - if (message.success) { - pending.resolve(message.result); - } else { - const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: "Unknown error" }; - pending.reject(createProviderError(error.code, error.message, error.data)); - } - } - handleEvent(message) { - this.events.emit(message.event, ...message.args); - } - generateId() { - return `tron_${Date.now()}_${++this.requestIdCounter}`; - } - postMessage(message) { - if (window.parent === window) { - console.warn("[TronLinkProvider] Not running in iframe, cannot communicate with host"); - return; - } - window.parent.postMessage(message, this.targetOrigin); - } - /** - * TronLink request method (EIP-1193 style) - */ - async request(args) { - const { method, params } = args; - const paramsArray = Array.isArray(params) ? params : params !== void 0 ? [params] : []; - const id = this.generateId(); - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { - resolve, - reject - }); - this.postMessage({ - type: "tron_request", - id, - method, - params: paramsArray - }); - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, "Request timeout")); - } - }, 5 * 60 * 1e3); - }); - } - on(event, handler) { - this.events.on(event, handler); - return this; - } - off(event, handler) { - this.events.off(event, handler); - return this; - } - } - class TronWebProvider { - tronLink; - _ready = false; - _defaultAddress = { base58: "", hex: "" }; - /** TRX operations */ - trx; - constructor(tronLink) { - this.tronLink = tronLink; - this.trx = new TronWebTrx(tronLink); - tronLink.on("accountsChanged", (accounts) => { - if (Array.isArray(accounts) && accounts.length > 0) { - const addr = accounts[0]; - this._defaultAddress = addr; - this._ready = true; - } else { - this._defaultAddress = { base58: "", hex: "" }; - this._ready = false; - } - }); - } - /** Whether TronWeb is ready (connected) */ - get ready() { - return this._ready; - } - /** Current default address */ - get defaultAddress() { - return this._defaultAddress; - } - /** - * Set default address (called by host after connection) - */ - setAddress(address) { - this._defaultAddress = address; - this._ready = true; - } - /** - * Check if an address is valid - */ - isAddress(address) { - if (address.startsWith("T")) { - return address.length === 34; - } - if (address.startsWith("41")) { - return address.length === 42; - } - return false; - } - /** - * Convert address to hex format - */ - address = { - toHex: (base58) => { - return base58; - }, - fromHex: (hex) => { - return hex; - } - }; - } - class TronWebTrx { - tronLink; - constructor(tronLink) { - this.tronLink = tronLink; - } - /** - * Sign a transaction - */ - async sign(transaction) { - return this.tronLink.request({ - method: "tron_signTransaction", - params: transaction - }); - } - /** - * Send raw transaction (broadcast) - */ - async sendRawTransaction(signedTransaction) { - return this.tronLink.request({ - method: "tron_sendRawTransaction", - params: signedTransaction - }); - } - /** - * Get account balance - */ - async getBalance(address) { - return this.tronLink.request({ - method: "tron_getBalance", - params: address - }); - } - /** - * Get account info - */ - async getAccount(address) { - return this.tronLink.request({ - method: "tron_getAccount", - params: address - }); - } - } - function initTronProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[TronProvider] Cannot initialize: window is not defined"); - } - if (window.tronLink && window.tronWeb) { - console.warn("[TronProvider] Providers already exist, returning existing instances"); - return { tronLink: window.tronLink, tronWeb: window.tronWeb }; - } - const tronLink = new TronLinkProvider(targetOrigin); - const tronWeb = new TronWebProvider(tronLink); - window.tronLink = tronLink; - window.tronWeb = tronWeb; - console.log("[TronProvider] Providers initialized"); - return { tronLink, tronWeb }; - } - const EVM_CHAIN_IDS = { - ethereum: 1, - binance: 56 - // Future chains - // polygon: 137, - // arbitrum: 42161, - // optimism: 10, - }; - const EVM_CHAIN_ID_TO_KEYAPP = Object.fromEntries( - Object.entries(EVM_CHAIN_IDS).map(([key, value]) => [value, key]) - ); - const API_CHAIN_TO_KEYAPP = { - ETH: "ethereum", - BSC: "binance", - TRON: "tron", - BFMCHAIN: "bfmeta", - BFCHAIN: "bfchain", - // Lowercase variants - eth: "ethereum", - bsc: "binance", - tron: "tron", - bfmchain: "bfmeta", - bfchain: "bfchain" - }; - const CHAIN_DISPLAY_NAMES = { - ethereum: "Ethereum", - binance: "BNB Smart Chain", - tron: "Tron", - bfmeta: "BFMeta", - bfchain: "BFChain" - }; - function toHexChainId(chainId) { - return `0x${chainId.toString(16)}`; - } - function parseHexChainId(hexChainId) { - if (!hexChainId.startsWith("0x")) { - throw new Error(`Invalid hex chain ID: ${hexChainId}`); - } - return parseInt(hexChainId, 16); - } - function getKeyAppChainId(hexChainId) { - const decimal = parseHexChainId(hexChainId); - return EVM_CHAIN_ID_TO_KEYAPP[decimal] ?? null; - } - function getEvmChainId(keyAppChainId) { - const decimal = EVM_CHAIN_IDS[keyAppChainId]; - return decimal ? toHexChainId(decimal) : null; - } - function isEvmChain(chainId) { - return chainId in EVM_CHAIN_IDS; - } - function normalizeChainId(chainName) { - return API_CHAIN_TO_KEYAPP[chainName] ?? chainName.toLowerCase(); - } - function initBioProvider(targetOrigin = "*") { - if (typeof window === "undefined") { - throw new Error("[BioSDK] Cannot initialize: window is not defined"); - } - if (window.bio) { - console.warn("[BioSDK] Provider already exists, returning existing instance"); - return window.bio; - } - const provider = new BioProviderImpl(targetOrigin); - window.bio = provider; - console.log("[BioSDK] Provider initialized"); - return provider; - } - function initAllProviders(targetOrigin = "*") { - const bio = initBioProvider(targetOrigin); - const ethereum = initEthereumProvider(targetOrigin); - const { tronLink, tronWeb } = initTronProvider(targetOrigin); - return { bio, ethereum, tronLink, tronWeb }; - } - if (typeof window !== "undefined") { - const init = () => { - initBioProvider(); - initEthereumProvider(); - initTronProvider(); - }; - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", init); - } else { - init(); - } - } - exports2.API_CHAIN_TO_KEYAPP = API_CHAIN_TO_KEYAPP; - exports2.BioErrorCodes = BioErrorCodes; - exports2.BioProviderImpl = BioProviderImpl; - exports2.CHAIN_DISPLAY_NAMES = CHAIN_DISPLAY_NAMES; - exports2.EVM_CHAIN_IDS = EVM_CHAIN_IDS; - exports2.EVM_CHAIN_ID_TO_KEYAPP = EVM_CHAIN_ID_TO_KEYAPP; - exports2.EthereumProvider = EthereumProvider; - exports2.EventEmitter = EventEmitter; - exports2.TronLinkProvider = TronLinkProvider; - exports2.TronWebProvider = TronWebProvider; - exports2.createProviderError = createProviderError; - exports2.getEvmChainId = getEvmChainId; - exports2.getKeyAppChainId = getKeyAppChainId; - exports2.initAllProviders = initAllProviders; - exports2.initBioProvider = initBioProvider; - exports2.initEthereumProvider = initEthereumProvider; - exports2.initTronProvider = initTronProvider; - exports2.isEvmChain = isEvmChain; - exports2.normalizeChainId = normalizeChainId; - exports2.parseHexChainId = parseHexChainId; - exports2.toHexChainId = toHexChainId; - Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" }); -})); -//# sourceMappingURL=index.umd.js.map diff --git a/packages/bio-sdk/dist/index.umd.js.map b/packages/bio-sdk/dist/index.umd.js.map deleted file mode 100644 index 3c87bf412..000000000 --- a/packages/bio-sdk/dist/index.umd.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.umd.js","sources":["../src/types.ts","../src/events.ts","../src/provider.ts","../src/ethereum-provider.ts","../src/tron-provider.ts","../src/chain-id.ts","../src/index.ts"],"sourcesContent":["/**\n * Bio SDK Types\n * EIP-1193 style provider interface for Bio ecosystem\n */\n\n/** Account information */\nexport interface BioAccount {\n address: string\n chain: string\n name?: string\n /** Public key (hex encoded) */\n publicKey: string\n}\n\n/** Transfer parameters */\nexport interface TransferParams {\n from: string\n to: string\n amount: string\n chain: string\n asset?: string\n}\n\n/** Unsigned transaction payload (chain-specific) */\nexport interface BioUnsignedTransaction {\n chainId: string\n data: unknown\n}\n\n/** Signed transaction payload (chain-specific) */\nexport interface BioSignedTransaction {\n chainId: string\n data: unknown\n signature: string\n}\n\n/** Provider request arguments */\nexport interface RequestArguments {\n method: string\n params?: unknown[]\n}\n\n/** Provider RPC error */\nexport interface ProviderRpcError extends Error {\n code: number\n data?: unknown\n}\n\n/** Event handler type */\nexport type EventHandler = (...args: T[]) => void\n\n/**\n * Bio Provider Interface (EIP-1193 style)\n */\nexport interface BioProvider {\n /** Make a request to the provider */\n request(args: RequestArguments): Promise\n\n /** Subscribe to an event */\n on(event: string, handler: EventHandler): void\n\n /** Unsubscribe from an event */\n off(event: string, handler: EventHandler): void\n\n /** Check if connected */\n isConnected(): boolean\n}\n\n/**\n * Bio method definitions\n */\nexport interface BioMethods {\n /** Request wallet accounts (shows connection UI) */\n bio_requestAccounts: () => Promise\n\n /** Get connected accounts (no UI) */\n bio_accounts: () => Promise\n\n /** Select an account (shows account picker UI) */\n bio_selectAccount: (opts?: { chain?: string }) => Promise\n\n /** Pick another wallet address (shows wallet picker UI) */\n bio_pickWallet: (opts?: { chain?: string; exclude?: string }) => Promise\n\n /** Sign a message, returns signature and public key (hex) */\n bio_signMessage: (params: { message: string; address: string }) => Promise<{ signature: string; publicKey: string }>\n\n /** Sign typed data, returns signature and public key (hex) */\n bio_signTypedData: (params: { data: object; address: string }) => Promise<{ signature: string; publicKey: string }>\n\n /** Create an unsigned transaction (no signature, no broadcast) */\n bio_createTransaction: (params: TransferParams) => Promise\n\n /** Sign an unsigned transaction (requires user confirmation) */\n bio_signTransaction: (params: { from: string; chain: string; unsignedTx: BioUnsignedTransaction }) => Promise\n\n /** Send a transaction */\n bio_sendTransaction: (params: TransferParams) => Promise<{ txHash: string }>\n\n /** Get current chain ID */\n bio_chainId: () => Promise\n\n /** Get balance */\n bio_getBalance: (params: { address: string; chain: string }) => Promise\n\n /** Close splash screen (indicates app is ready) */\n bio_closeSplashScreen: () => Promise\n}\n\n/**\n * Bio event definitions\n */\nexport interface BioEvents {\n /** Emitted when accounts change */\n accountsChanged: (accounts: BioAccount[]) => void\n\n /** Emitted when chain changes */\n chainChanged: (chainId: string) => void\n\n /** Emitted when connected */\n connect: (info: { chainId: string }) => void\n\n /** Emitted when disconnected */\n disconnect: (error: { code: number; message: string }) => void\n}\n\n/** Method names */\nexport type BioMethodName = keyof BioMethods\n\n/** Event names */\nexport type BioEventName = keyof BioEvents\n\n/** RPC error codes */\nexport const BioErrorCodes = {\n USER_REJECTED: 4001,\n UNAUTHORIZED: 4100,\n UNSUPPORTED_METHOD: 4200,\n DISCONNECTED: 4900,\n CHAIN_DISCONNECTED: 4901,\n INTERNAL_ERROR: -32603,\n INVALID_PARAMS: -32602,\n METHOD_NOT_FOUND: -32601,\n} as const\n\n/** Create a provider RPC error */\nexport function createProviderError(code: number, message: string, data?: unknown): ProviderRpcError {\n const error = new Error(message) as ProviderRpcError\n error.code = code\n error.data = data\n return error\n}\n","/**\n * Event emitter for Bio SDK\n */\n\nimport type { EventHandler } from './types'\n\nexport class EventEmitter {\n private handlers = new Map>()\n\n on(event: string, handler: EventHandler): void {\n let handlers = this.handlers.get(event)\n if (!handlers) {\n handlers = new Set()\n this.handlers.set(event, handlers)\n }\n handlers.add(handler)\n }\n\n off(event: string, handler: EventHandler): void {\n const handlers = this.handlers.get(event)\n if (handlers) {\n handlers.delete(handler)\n if (handlers.size === 0) {\n this.handlers.delete(event)\n }\n }\n }\n\n emit(event: string, ...args: unknown[]): void {\n const handlers = this.handlers.get(event)\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(...args)\n } catch (error) {\n console.error(`[BioSDK] Error in event handler for \"${event}\":`, error)\n }\n })\n }\n }\n\n removeAllListeners(event?: string): void {\n if (event) {\n this.handlers.delete(event)\n } else {\n this.handlers.clear()\n }\n }\n}\n","/**\n * Bio Provider Implementation\n * Communicates with KeyApp host via postMessage\n */\n\nimport type { BioProvider, RequestArguments, EventHandler } from './types'\nimport { BioErrorCodes, createProviderError } from './types'\nimport { EventEmitter } from './events'\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'bio_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'bio_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'bio_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\nexport class BioProviderImpl implements BioProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private connected = false\n private readonly targetOrigin: string\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n this.connect()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'bio_response') {\n this.handleResponse(data)\n } else if (data.type === 'bio_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n\n // Handle built-in events\n if (message.event === 'connect') {\n this.connected = true\n } else if (message.event === 'disconnect') {\n this.connected = false\n }\n }\n\n private connect(): void {\n // Send handshake to host\n this.postMessage({\n type: 'bio_request',\n id: this.generateId(),\n method: 'bio_connect',\n params: [],\n })\n }\n\n private generateId(): string {\n return `bio_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[BioSDK] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n async request(args: RequestArguments): Promise {\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'bio_request',\n id,\n method: args.method,\n params: args.params,\n })\n\n // Timeout after 5 minutes (for user interactions)\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n on(event: string, handler: EventHandler): void {\n this.events.on(event, handler)\n }\n\n off(event: string, handler: EventHandler): void {\n this.events.off(event, handler)\n }\n\n isConnected(): boolean {\n return this.connected\n }\n}\n","/**\n * Ethereum Provider (EIP-1193 Compatible)\n *\n * Provides window.ethereum for EVM-compatible dApps.\n * Communicates with KeyApp host via postMessage.\n */\n\nimport { EventEmitter } from './events'\nimport { BioErrorCodes, createProviderError, type ProviderRpcError } from './types'\nimport { toHexChainId, parseHexChainId, getKeyAppChainId, EVM_CHAIN_IDS } from './chain-id'\n\n/** EIP-1193 Request Arguments */\nexport interface EthRequestArguments {\n method: string\n params?: unknown[] | Record\n}\n\n/** EIP-1193 Provider Connect Info */\nexport interface ProviderConnectInfo {\n chainId: string\n}\n\n/** EIP-1193 Provider Message */\nexport interface ProviderMessage {\n type: string\n data: unknown\n}\n\n/** Transaction request (eth_sendTransaction) */\nexport interface TransactionRequest {\n from: string\n to?: string\n value?: string\n data?: string\n gas?: string\n gasPrice?: string\n maxFeePerGas?: string\n maxPriorityFeePerGas?: string\n nonce?: string\n}\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'eth_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'eth_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'eth_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\n/**\n * EIP-1193 Ethereum Provider Implementation\n */\nexport class EthereumProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private connected = false\n private currentChainId: string | null = null\n private accounts: string[] = []\n private readonly targetOrigin: string\n\n // EIP-1193 required properties\n readonly isMetaMask = false\n readonly isKeyApp = true\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'eth_response') {\n this.handleResponse(data)\n } else if (data.type === 'eth_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n\n // Handle built-in events\n if (message.event === 'connect') {\n this.connected = true\n const info = message.args[0] as ProviderConnectInfo\n this.currentChainId = info?.chainId ?? null\n } else if (message.event === 'disconnect') {\n this.connected = false\n this.accounts = []\n } else if (message.event === 'chainChanged') {\n this.currentChainId = message.args[0] as string\n } else if (message.event === 'accountsChanged') {\n this.accounts = message.args[0] as string[]\n }\n }\n\n private generateId(): string {\n return `eth_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[EthereumProvider] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n /**\n * EIP-1193 request method\n */\n async request(args: EthRequestArguments): Promise {\n const { method, params } = args\n const paramsArray = Array.isArray(params) ? params : params ? [params] : []\n\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'eth_request',\n id,\n method,\n params: paramsArray,\n })\n\n // Timeout after 5 minutes (for user interactions)\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n /**\n * Subscribe to an event\n */\n on(event: string, handler: (...args: unknown[]) => void): this {\n this.events.on(event, handler)\n return this\n }\n\n /**\n * Unsubscribe from an event\n */\n off(event: string, handler: (...args: unknown[]) => void): this {\n this.events.off(event, handler)\n return this\n }\n\n /**\n * Alias for off (Node.js EventEmitter compatibility)\n */\n removeListener(event: string, handler: (...args: unknown[]) => void): this {\n return this.off(event, handler)\n }\n\n /**\n * Add listener that fires only once\n */\n once(event: string, handler: (...args: unknown[]) => void): this {\n const wrapper = (...args: unknown[]) => {\n this.off(event, wrapper)\n handler(...args)\n }\n this.on(event, wrapper)\n return this\n }\n\n /**\n * EIP-1193 isConnected method\n */\n isConnected(): boolean {\n return this.connected\n }\n\n /**\n * Get current chain ID (cached)\n */\n get chainId(): string | null {\n return this.currentChainId\n }\n\n /**\n * Get selected address (first account)\n */\n get selectedAddress(): string | null {\n return this.accounts[0] ?? null\n }\n\n // ============================================\n // Legacy methods (for backwards compatibility)\n // ============================================\n\n /**\n * @deprecated Use request({ method: 'eth_requestAccounts' })\n */\n async enable(): Promise {\n return this.request({ method: 'eth_requestAccounts' })\n }\n\n /**\n * @deprecated Use request()\n */\n send(method: string, params?: unknown[]): Promise {\n return this.request({ method, params })\n }\n\n /**\n * @deprecated Use request()\n */\n sendAsync(\n payload: { method: string; params?: unknown[]; id?: number },\n callback: (error: Error | null, result?: { result: unknown }) => void\n ): void {\n this.request({ method: payload.method, params: payload.params })\n .then((result) => callback(null, { result }))\n .catch((error) => callback(error))\n }\n}\n\n// Extend Window interface\ndeclare global {\n interface Window {\n ethereum?: EthereumProvider\n }\n}\n\n/**\n * Initialize and inject the Ethereum provider into window.ethereum\n */\nexport function initEthereumProvider(targetOrigin = '*'): EthereumProvider {\n if (typeof window === 'undefined') {\n throw new Error('[EthereumProvider] Cannot initialize: window is not defined')\n }\n\n if (window.ethereum) {\n console.warn('[EthereumProvider] Provider already exists, returning existing instance')\n return window.ethereum\n }\n\n const provider = new EthereumProvider(targetOrigin)\n window.ethereum = provider\n\n console.log('[EthereumProvider] Provider initialized')\n return provider\n}\n","/**\n * Tron Provider (TronLink Compatible)\n *\n * Provides window.tronWeb and window.tronLink for Tron dApps.\n * Communicates with KeyApp host via postMessage.\n */\n\nimport { EventEmitter } from './events'\nimport { BioErrorCodes, createProviderError } from './types'\n\n/** Tron address format */\nexport interface TronAddress {\n base58: string\n hex: string\n}\n\n/** TronLink request arguments (EIP-1193 style) */\nexport interface TronRequestArguments {\n method: string\n params?: unknown\n}\n\n/** Message sent to host */\ninterface RequestMessage {\n type: 'tron_request'\n id: string\n method: string\n params?: unknown[]\n}\n\n/** Response from host */\ninterface ResponseMessage {\n type: 'tron_response'\n id: string\n success: boolean\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\n/** Event from host */\ninterface EventMessage {\n type: 'tron_event'\n event: string\n args: unknown[]\n}\n\ntype HostMessage = ResponseMessage | EventMessage\n\n/**\n * TronLink-compatible Provider\n */\nexport class TronLinkProvider {\n private events = new EventEmitter()\n private pendingRequests = new Map void\n reject: (error: Error) => void\n }>()\n private requestIdCounter = 0\n private readonly targetOrigin: string\n\n constructor(targetOrigin = '*') {\n this.targetOrigin = targetOrigin\n this.setupMessageListener()\n }\n\n private setupMessageListener(): void {\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n private handleMessage(event: MessageEvent): void {\n const data = event.data as HostMessage\n if (!data || typeof data !== 'object') return\n\n if (data.type === 'tron_response') {\n this.handleResponse(data)\n } else if (data.type === 'tron_event') {\n this.handleEvent(data)\n }\n }\n\n private handleResponse(message: ResponseMessage): void {\n const pending = this.pendingRequests.get(message.id)\n if (!pending) return\n\n this.pendingRequests.delete(message.id)\n\n if (message.success) {\n pending.resolve(message.result)\n } else {\n const error = message.error ?? { code: BioErrorCodes.INTERNAL_ERROR, message: 'Unknown error' }\n pending.reject(createProviderError(error.code, error.message, error.data))\n }\n }\n\n private handleEvent(message: EventMessage): void {\n this.events.emit(message.event, ...message.args)\n }\n\n private generateId(): string {\n return `tron_${Date.now()}_${++this.requestIdCounter}`\n }\n\n private postMessage(message: RequestMessage): void {\n if (window.parent === window) {\n console.warn('[TronLinkProvider] Not running in iframe, cannot communicate with host')\n return\n }\n window.parent.postMessage(message, this.targetOrigin)\n }\n\n /**\n * TronLink request method (EIP-1193 style)\n */\n async request(args: TronRequestArguments): Promise {\n const { method, params } = args\n const paramsArray = Array.isArray(params) ? params : params !== undefined ? [params] : []\n\n const id = this.generateId()\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n })\n\n this.postMessage({\n type: 'tron_request',\n id,\n method,\n params: paramsArray,\n })\n\n // Timeout after 5 minutes\n setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id)\n reject(createProviderError(BioErrorCodes.INTERNAL_ERROR, 'Request timeout'))\n }\n }, 5 * 60 * 1000)\n })\n }\n\n on(event: string, handler: (...args: unknown[]) => void): this {\n this.events.on(event, handler)\n return this\n }\n\n off(event: string, handler: (...args: unknown[]) => void): this {\n this.events.off(event, handler)\n return this\n }\n}\n\n/**\n * TronWeb-compatible API\n * Provides the subset of TronWeb API that KeyApp supports\n */\nexport class TronWebProvider {\n private tronLink: TronLinkProvider\n private _ready = false\n private _defaultAddress: TronAddress = { base58: '', hex: '' }\n\n /** TRX operations */\n readonly trx: TronWebTrx\n\n constructor(tronLink: TronLinkProvider) {\n this.tronLink = tronLink\n this.trx = new TronWebTrx(tronLink)\n\n // Listen for account changes\n tronLink.on('accountsChanged', (accounts: unknown) => {\n if (Array.isArray(accounts) && accounts.length > 0) {\n const addr = accounts[0] as TronAddress\n this._defaultAddress = addr\n this._ready = true\n } else {\n this._defaultAddress = { base58: '', hex: '' }\n this._ready = false\n }\n })\n }\n\n /** Whether TronWeb is ready (connected) */\n get ready(): boolean {\n return this._ready\n }\n\n /** Current default address */\n get defaultAddress(): TronAddress {\n return this._defaultAddress\n }\n\n /**\n * Set default address (called by host after connection)\n */\n setAddress(address: TronAddress): void {\n this._defaultAddress = address\n this._ready = true\n }\n\n /**\n * Check if an address is valid\n */\n isAddress(address: string): boolean {\n // Basic validation: base58 starts with T, hex starts with 41\n if (address.startsWith('T')) {\n return address.length === 34\n }\n if (address.startsWith('41')) {\n return address.length === 42\n }\n return false\n }\n\n /**\n * Convert address to hex format\n */\n address = {\n toHex: (base58: string): string => {\n // This is a stub - actual conversion requires TronWeb library\n // KeyApp will handle the conversion on the host side\n return base58\n },\n fromHex: (hex: string): string => {\n return hex\n },\n }\n}\n\n/**\n * TronWeb.trx operations\n */\nclass TronWebTrx {\n private tronLink: TronLinkProvider\n\n constructor(tronLink: TronLinkProvider) {\n this.tronLink = tronLink\n }\n\n /**\n * Sign a transaction\n */\n async sign(transaction: unknown): Promise {\n return this.tronLink.request({\n method: 'tron_signTransaction',\n params: transaction,\n })\n }\n\n /**\n * Send raw transaction (broadcast)\n */\n async sendRawTransaction(signedTransaction: unknown): Promise {\n return this.tronLink.request({\n method: 'tron_sendRawTransaction',\n params: signedTransaction,\n })\n }\n\n /**\n * Get account balance\n */\n async getBalance(address: string): Promise {\n return this.tronLink.request({\n method: 'tron_getBalance',\n params: address,\n })\n }\n\n /**\n * Get account info\n */\n async getAccount(address: string): Promise {\n return this.tronLink.request({\n method: 'tron_getAccount',\n params: address,\n })\n }\n}\n\n// Extend Window interface\ndeclare global {\n interface Window {\n tronLink?: TronLinkProvider\n tronWeb?: TronWebProvider\n }\n}\n\n/**\n * Initialize and inject the Tron providers\n */\nexport function initTronProvider(targetOrigin = '*'): { tronLink: TronLinkProvider; tronWeb: TronWebProvider } {\n if (typeof window === 'undefined') {\n throw new Error('[TronProvider] Cannot initialize: window is not defined')\n }\n\n if (window.tronLink && window.tronWeb) {\n console.warn('[TronProvider] Providers already exist, returning existing instances')\n return { tronLink: window.tronLink, tronWeb: window.tronWeb }\n }\n\n const tronLink = new TronLinkProvider(targetOrigin)\n const tronWeb = new TronWebProvider(tronLink)\n\n window.tronLink = tronLink\n window.tronWeb = tronWeb\n\n console.log('[TronProvider] Providers initialized')\n return { tronLink, tronWeb }\n}\n","/**\n * Chain ID utilities\n * Maps between KeyApp internal chain IDs and standard chain IDs\n */\n\n/** EVM Chain ID mapping (decimal) */\nexport const EVM_CHAIN_IDS: Record = {\n ethereum: 1,\n binance: 56,\n // Future chains\n // polygon: 137,\n // arbitrum: 42161,\n // optimism: 10,\n} as const\n\n/** Reverse mapping: EVM chainId -> KeyApp chain ID */\nexport const EVM_CHAIN_ID_TO_KEYAPP: Record = Object.fromEntries(\n Object.entries(EVM_CHAIN_IDS).map(([key, value]) => [value, key])\n)\n\n/** API chain name to KeyApp chain ID mapping */\nexport const API_CHAIN_TO_KEYAPP: Record = {\n ETH: 'ethereum',\n BSC: 'binance',\n TRON: 'tron',\n BFMCHAIN: 'bfmeta',\n BFCHAIN: 'bfchain',\n // Lowercase variants\n eth: 'ethereum',\n bsc: 'binance',\n tron: 'tron',\n bfmchain: 'bfmeta',\n bfchain: 'bfchain',\n} as const\n\n/** KeyApp chain ID to display name */\nexport const CHAIN_DISPLAY_NAMES: Record = {\n ethereum: 'Ethereum',\n binance: 'BNB Smart Chain',\n tron: 'Tron',\n bfmeta: 'BFMeta',\n bfchain: 'BFChain',\n} as const\n\n/**\n * Convert decimal chain ID to hex string (EIP-155 format)\n * @example toHexChainId(56) => '0x38'\n */\nexport function toHexChainId(chainId: number): string {\n return `0x${chainId.toString(16)}`\n}\n\n/**\n * Parse hex chain ID to decimal\n * @example parseHexChainId('0x38') => 56\n */\nexport function parseHexChainId(hexChainId: string): number {\n if (!hexChainId.startsWith('0x')) {\n throw new Error(`Invalid hex chain ID: ${hexChainId}`)\n }\n return parseInt(hexChainId, 16)\n}\n\n/**\n * Get KeyApp chain ID from EVM hex chain ID\n * @example getKeyAppChainId('0x38') => 'binance'\n */\nexport function getKeyAppChainId(hexChainId: string): string | null {\n const decimal = parseHexChainId(hexChainId)\n return EVM_CHAIN_ID_TO_KEYAPP[decimal] ?? null\n}\n\n/**\n * Get EVM hex chain ID from KeyApp chain ID\n * @example getEvmChainId('binance') => '0x38'\n */\nexport function getEvmChainId(keyAppChainId: string): string | null {\n const decimal = EVM_CHAIN_IDS[keyAppChainId]\n return decimal ? toHexChainId(decimal) : null\n}\n\n/**\n * Check if a chain is EVM compatible\n */\nexport function isEvmChain(chainId: string): boolean {\n return chainId in EVM_CHAIN_IDS\n}\n\n/**\n * Normalize API chain name to KeyApp chain ID\n * @example normalizeChainId('BSC') => 'binance'\n */\nexport function normalizeChainId(chainName: string): string {\n return API_CHAIN_TO_KEYAPP[chainName] ?? chainName.toLowerCase()\n}\n","/**\n * Bio SDK - Client SDK for Bio Ecosystem MiniApps\n *\n * Injects providers for multi-chain dApp support:\n * - `window.bio` - BioChain + KeyApp wallet features\n * - `window.ethereum` - EVM-compatible chains (ETH, BSC)\n * - `window.tronWeb` / `window.tronLink` - Tron chain\n *\n * @example\n * ```typescript\n * import '@biochain/bio-sdk'\n *\n * // BioChain operations\n * const accounts = await window.bio.request({ method: 'bio_requestAccounts' })\n *\n * // EVM operations (ETH, BSC)\n * const ethAccounts = await window.ethereum.request({ method: 'eth_requestAccounts' })\n *\n * // Tron operations\n * const tronAccounts = await window.tronLink.request({ method: 'tron_requestAccounts' })\n * ```\n */\n\nimport { BioProviderImpl } from './provider'\nimport { EthereumProvider, initEthereumProvider } from './ethereum-provider'\nimport { TronLinkProvider, TronWebProvider, initTronProvider } from './tron-provider'\nimport type { BioProvider } from './types'\n\n// Re-export types\nexport * from './types'\nexport * from './chain-id'\nexport { EventEmitter } from './events'\nexport { BioProviderImpl } from './provider'\nexport { EthereumProvider, initEthereumProvider } from './ethereum-provider'\nexport { TronLinkProvider, TronWebProvider, initTronProvider } from './tron-provider'\n\n// Extend Window interface (bio is declared in types.ts already for ethereum/tron)\ndeclare global {\n interface Window {\n bio?: BioProvider\n }\n}\n\n/**\n * Initialize and inject the Bio provider into window.bio\n */\nexport function initBioProvider(targetOrigin = '*'): BioProvider {\n if (typeof window === 'undefined') {\n throw new Error('[BioSDK] Cannot initialize: window is not defined')\n }\n\n if (window.bio) {\n console.warn('[BioSDK] Provider already exists, returning existing instance')\n return window.bio\n }\n\n const provider = new BioProviderImpl(targetOrigin)\n window.bio = provider\n\n console.log('[BioSDK] Provider initialized')\n return provider\n}\n\n/**\n * Initialize all providers (bio, ethereum, tron)\n */\nexport function initAllProviders(targetOrigin = '*'): {\n bio: BioProvider\n ethereum: EthereumProvider\n tronLink: TronLinkProvider\n tronWeb: TronWebProvider\n} {\n const bio = initBioProvider(targetOrigin)\n const ethereum = initEthereumProvider(targetOrigin)\n const { tronLink, tronWeb } = initTronProvider(targetOrigin)\n\n return { bio, ethereum, tronLink, tronWeb }\n}\n\n// Auto-initialize if running in browser\nif (typeof window !== 'undefined') {\n const init = () => {\n initBioProvider()\n initEthereumProvider()\n initTronProvider()\n }\n\n // Use a slight delay to ensure DOM is ready\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init)\n } else {\n init()\n }\n}\n"],"names":[],"mappings":";;;;AAqIO,QAAM,gBAAgB;AAAA,IAC3B,eAAe;AAAA,IACf,cAAc;AAAA,IACd,oBAAoB;AAAA,IACpB,cAAc;AAAA,IACd,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,EACpB;AAGO,WAAS,oBAAoB,MAAc,SAAiB,MAAkC;AACnG,UAAM,QAAQ,IAAI,MAAM,OAAO;AAC/B,UAAM,OAAO;AACb,UAAM,OAAO;AACb,WAAO;AAAA,EACT;AAAA,EChJO,MAAM,aAAa;AAAA,IAChB,+BAAe,IAAA;AAAA,IAEvB,GAAG,OAAe,SAA6B;AAC7C,UAAI,WAAW,KAAK,SAAS,IAAI,KAAK;AACtC,UAAI,CAAC,UAAU;AACb,uCAAe,IAAA;AACf,aAAK,SAAS,IAAI,OAAO,QAAQ;AAAA,MACnC;AACA,eAAS,IAAI,OAAO;AAAA,IACtB;AAAA,IAEA,IAAI,OAAe,SAA6B;AAC9C,YAAM,WAAW,KAAK,SAAS,IAAI,KAAK;AACxC,UAAI,UAAU;AACZ,iBAAS,OAAO,OAAO;AACvB,YAAI,SAAS,SAAS,GAAG;AACvB,eAAK,SAAS,OAAO,KAAK;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA,IAEA,KAAK,UAAkB,MAAuB;AAC5C,YAAM,WAAW,KAAK,SAAS,IAAI,KAAK;AACxC,UAAI,UAAU;AACZ,iBAAS,QAAQ,CAAC,YAAY;AAC5B,cAAI;AACF,oBAAQ,GAAG,IAAI;AAAA,UACjB,SAAS,OAAO;AACd,oBAAQ,MAAM,wCAAwC,KAAK,MAAM,KAAK;AAAA,UACxE;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IAEA,mBAAmB,OAAsB;AACvC,UAAI,OAAO;AACT,aAAK,SAAS,OAAO,KAAK;AAAA,MAC5B,OAAO;AACL,aAAK,SAAS,MAAA;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAAA,ECbO,MAAM,gBAAuC;AAAA,IAC1C,SAAS,IAAI,aAAA;AAAA,IACb,sCAAsB,IAAA;AAAA,IAItB,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACH;AAAA,IAEjB,YAAY,eAAe,KAAK;AAC9B,WAAK,eAAe;AACpB,WAAK,qBAAA;AACL,WAAK,QAAA;AAAA,IACP;AAAA,IAEQ,uBAA6B;AACnC,aAAO,iBAAiB,WAAW,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,IAClE;AAAA,IAEQ,cAAc,OAA2B;AAC/C,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAEvC,UAAI,KAAK,SAAS,gBAAgB;AAChC,aAAK,eAAe,IAAI;AAAA,MAC1B,WAAW,KAAK,SAAS,aAAa;AACpC,aAAK,YAAY,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,IAEQ,eAAe,SAAgC;AACrD,YAAM,UAAU,KAAK,gBAAgB,IAAI,QAAQ,EAAE;AACnD,UAAI,CAAC,QAAS;AAEd,WAAK,gBAAgB,OAAO,QAAQ,EAAE;AAEtC,UAAI,QAAQ,SAAS;AACnB,gBAAQ,QAAQ,QAAQ,MAAM;AAAA,MAChC,OAAO;AACL,cAAM,QAAQ,QAAQ,SAAS,EAAE,MAAM,cAAc,gBAAgB,SAAS,gBAAA;AAC9E,gBAAQ,OAAO,oBAAoB,MAAM,MAAM,MAAM,SAAS,MAAM,IAAI,CAAC;AAAA,MAC3E;AAAA,IACF;AAAA,IAEQ,YAAY,SAA6B;AAC/C,WAAK,OAAO,KAAK,QAAQ,OAAO,GAAG,QAAQ,IAAI;AAG/C,UAAI,QAAQ,UAAU,WAAW;AAC/B,aAAK,YAAY;AAAA,MACnB,WAAW,QAAQ,UAAU,cAAc;AACzC,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AAAA,IAEQ,UAAgB;AAEtB,WAAK,YAAY;AAAA,QACf,MAAM;AAAA,QACN,IAAI,KAAK,WAAA;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ,CAAA;AAAA,MAAC,CACV;AAAA,IACH;AAAA,IAEQ,aAAqB;AAC3B,aAAO,OAAO,KAAK,IAAA,CAAK,IAAI,EAAE,KAAK,gBAAgB;AAAA,IACrD;AAAA,IAEQ,YAAY,SAA+B;AACjD,UAAI,OAAO,WAAW,QAAQ;AAC5B,gBAAQ,KAAK,8DAA8D;AAC3E;AAAA,MACF;AACA,aAAO,OAAO,YAAY,SAAS,KAAK,YAAY;AAAA,IACtD;AAAA,IAEA,MAAM,QAAqB,MAAoC;AAC7D,YAAM,KAAK,KAAK,WAAA;AAEhB,aAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,aAAK,gBAAgB,IAAI,IAAI;AAAA,UAC3B;AAAA,UACA;AAAA,QAAA,CACD;AAED,aAAK,YAAY;AAAA,UACf,MAAM;AAAA,UACN;AAAA,UACA,QAAQ,KAAK;AAAA,UACb,QAAQ,KAAK;AAAA,QAAA,CACd;AAGD,mBAAW,MAAM;AACf,cAAI,KAAK,gBAAgB,IAAI,EAAE,GAAG;AAChC,iBAAK,gBAAgB,OAAO,EAAE;AAC9B,mBAAO,oBAAoB,cAAc,gBAAgB,iBAAiB,CAAC;AAAA,UAC7E;AAAA,QACF,GAAG,IAAI,KAAK,GAAI;AAAA,MAClB,CAAC;AAAA,IACH;AAAA,IAEA,GAAG,OAAe,SAA6B;AAC7C,WAAK,OAAO,GAAG,OAAO,OAAO;AAAA,IAC/B;AAAA,IAEA,IAAI,OAAe,SAA6B;AAC9C,WAAK,OAAO,IAAI,OAAO,OAAO;AAAA,IAChC;AAAA,IAEA,cAAuB;AACrB,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AAAA,EChFO,MAAM,iBAAiB;AAAA,IACpB,SAAS,IAAI,aAAA;AAAA,IACb,sCAAsB,IAAA;AAAA,IAItB,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,iBAAgC;AAAA,IAChC,WAAqB,CAAA;AAAA,IACZ;AAAA;AAAA,IAGR,aAAa;AAAA,IACb,WAAW;AAAA,IAEpB,YAAY,eAAe,KAAK;AAC9B,WAAK,eAAe;AACpB,WAAK,qBAAA;AAAA,IACP;AAAA,IAEQ,uBAA6B;AACnC,aAAO,iBAAiB,WAAW,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,IAClE;AAAA,IAEQ,cAAc,OAA2B;AAC/C,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAEvC,UAAI,KAAK,SAAS,gBAAgB;AAChC,aAAK,eAAe,IAAI;AAAA,MAC1B,WAAW,KAAK,SAAS,aAAa;AACpC,aAAK,YAAY,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,IAEQ,eAAe,SAAgC;AACrD,YAAM,UAAU,KAAK,gBAAgB,IAAI,QAAQ,EAAE;AACnD,UAAI,CAAC,QAAS;AAEd,WAAK,gBAAgB,OAAO,QAAQ,EAAE;AAEtC,UAAI,QAAQ,SAAS;AACnB,gBAAQ,QAAQ,QAAQ,MAAM;AAAA,MAChC,OAAO;AACL,cAAM,QAAQ,QAAQ,SAAS,EAAE,MAAM,cAAc,gBAAgB,SAAS,gBAAA;AAC9E,gBAAQ,OAAO,oBAAoB,MAAM,MAAM,MAAM,SAAS,MAAM,IAAI,CAAC;AAAA,MAC3E;AAAA,IACF;AAAA,IAEQ,YAAY,SAA6B;AAC/C,WAAK,OAAO,KAAK,QAAQ,OAAO,GAAG,QAAQ,IAAI;AAG/C,UAAI,QAAQ,UAAU,WAAW;AAC/B,aAAK,YAAY;AACjB,cAAM,OAAO,QAAQ,KAAK,CAAC;AAC3B,aAAK,iBAAiB,MAAM,WAAW;AAAA,MACzC,WAAW,QAAQ,UAAU,cAAc;AACzC,aAAK,YAAY;AACjB,aAAK,WAAW,CAAA;AAAA,MAClB,WAAW,QAAQ,UAAU,gBAAgB;AAC3C,aAAK,iBAAiB,QAAQ,KAAK,CAAC;AAAA,MACtC,WAAW,QAAQ,UAAU,mBAAmB;AAC9C,aAAK,WAAW,QAAQ,KAAK,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,IAEQ,aAAqB;AAC3B,aAAO,OAAO,KAAK,IAAA,CAAK,IAAI,EAAE,KAAK,gBAAgB;AAAA,IACrD;AAAA,IAEQ,YAAY,SAA+B;AACjD,UAAI,OAAO,WAAW,QAAQ;AAC5B,gBAAQ,KAAK,wEAAwE;AACrF;AAAA,MACF;AACA,aAAO,OAAO,YAAY,SAAS,KAAK,YAAY;AAAA,IACtD;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,QAAqB,MAAuC;AAChE,YAAM,EAAE,QAAQ,OAAA,IAAW;AAC3B,YAAM,cAAc,MAAM,QAAQ,MAAM,IAAI,SAAS,SAAS,CAAC,MAAM,IAAI,CAAA;AAEzE,YAAM,KAAK,KAAK,WAAA;AAEhB,aAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,aAAK,gBAAgB,IAAI,IAAI;AAAA,UAC3B;AAAA,UACA;AAAA,QAAA,CACD;AAED,aAAK,YAAY;AAAA,UACf,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,QAAA,CACT;AAGD,mBAAW,MAAM;AACf,cAAI,KAAK,gBAAgB,IAAI,EAAE,GAAG;AAChC,iBAAK,gBAAgB,OAAO,EAAE;AAC9B,mBAAO,oBAAoB,cAAc,gBAAgB,iBAAiB,CAAC;AAAA,UAC7E;AAAA,QACF,GAAG,IAAI,KAAK,GAAI;AAAA,MAClB,CAAC;AAAA,IACH;AAAA;AAAA;AAAA;AAAA,IAKA,GAAG,OAAe,SAA6C;AAC7D,WAAK,OAAO,GAAG,OAAO,OAAO;AAC7B,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA,IAKA,IAAI,OAAe,SAA6C;AAC9D,WAAK,OAAO,IAAI,OAAO,OAAO;AAC9B,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA,IAKA,eAAe,OAAe,SAA6C;AACzE,aAAO,KAAK,IAAI,OAAO,OAAO;AAAA,IAChC;AAAA;AAAA;AAAA;AAAA,IAKA,KAAK,OAAe,SAA6C;AAC/D,YAAM,UAAU,IAAI,SAAoB;AACtC,aAAK,IAAI,OAAO,OAAO;AACvB,gBAAQ,GAAG,IAAI;AAAA,MACjB;AACA,WAAK,GAAG,OAAO,OAAO;AACtB,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA,IAKA,cAAuB;AACrB,aAAO,KAAK;AAAA,IACd;AAAA;AAAA;AAAA;AAAA,IAKA,IAAI,UAAyB;AAC3B,aAAO,KAAK;AAAA,IACd;AAAA;AAAA;AAAA;AAAA,IAKA,IAAI,kBAAiC;AACnC,aAAO,KAAK,SAAS,CAAC,KAAK;AAAA,IAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM,SAA4B;AAChC,aAAO,KAAK,QAAQ,EAAE,QAAQ,uBAAuB;AAAA,IACvD;AAAA;AAAA;AAAA;AAAA,IAKA,KAAK,QAAgB,QAAsC;AACzD,aAAO,KAAK,QAAQ,EAAE,QAAQ,QAAQ;AAAA,IACxC;AAAA;AAAA;AAAA;AAAA,IAKA,UACE,SACA,UACM;AACN,WAAK,QAAQ,EAAE,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,OAAA,CAAQ,EAC5D,KAAK,CAAC,WAAW,SAAS,MAAM,EAAE,QAAQ,CAAC,EAC3C,MAAM,CAAC,UAAU,SAAS,KAAK,CAAC;AAAA,IACrC;AAAA,EACF;AAYO,WAAS,qBAAqB,eAAe,KAAuB;AACzE,QAAI,OAAO,WAAW,aAAa;AACjC,YAAM,IAAI,MAAM,6DAA6D;AAAA,IAC/E;AAEA,QAAI,OAAO,UAAU;AACnB,cAAQ,KAAK,yEAAyE;AACtF,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,WAAW,IAAI,iBAAiB,YAAY;AAClD,WAAO,WAAW;AAElB,YAAQ,IAAI,yCAAyC;AACrD,WAAO;AAAA,EACT;AAAA,ECnPO,MAAM,iBAAiB;AAAA,IACpB,SAAS,IAAI,aAAA;AAAA,IACb,sCAAsB,IAAA;AAAA,IAItB,mBAAmB;AAAA,IACV;AAAA,IAEjB,YAAY,eAAe,KAAK;AAC9B,WAAK,eAAe;AACpB,WAAK,qBAAA;AAAA,IACP;AAAA,IAEQ,uBAA6B;AACnC,aAAO,iBAAiB,WAAW,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,IAClE;AAAA,IAEQ,cAAc,OAA2B;AAC/C,YAAM,OAAO,MAAM;AACnB,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAEvC,UAAI,KAAK,SAAS,iBAAiB;AACjC,aAAK,eAAe,IAAI;AAAA,MAC1B,WAAW,KAAK,SAAS,cAAc;AACrC,aAAK,YAAY,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,IAEQ,eAAe,SAAgC;AACrD,YAAM,UAAU,KAAK,gBAAgB,IAAI,QAAQ,EAAE;AACnD,UAAI,CAAC,QAAS;AAEd,WAAK,gBAAgB,OAAO,QAAQ,EAAE;AAEtC,UAAI,QAAQ,SAAS;AACnB,gBAAQ,QAAQ,QAAQ,MAAM;AAAA,MAChC,OAAO;AACL,cAAM,QAAQ,QAAQ,SAAS,EAAE,MAAM,cAAc,gBAAgB,SAAS,gBAAA;AAC9E,gBAAQ,OAAO,oBAAoB,MAAM,MAAM,MAAM,SAAS,MAAM,IAAI,CAAC;AAAA,MAC3E;AAAA,IACF;AAAA,IAEQ,YAAY,SAA6B;AAC/C,WAAK,OAAO,KAAK,QAAQ,OAAO,GAAG,QAAQ,IAAI;AAAA,IACjD;AAAA,IAEQ,aAAqB;AAC3B,aAAO,QAAQ,KAAK,IAAA,CAAK,IAAI,EAAE,KAAK,gBAAgB;AAAA,IACtD;AAAA,IAEQ,YAAY,SAA+B;AACjD,UAAI,OAAO,WAAW,QAAQ;AAC5B,gBAAQ,KAAK,wEAAwE;AACrF;AAAA,MACF;AACA,aAAO,OAAO,YAAY,SAAS,KAAK,YAAY;AAAA,IACtD;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,QAAqB,MAAwC;AACjE,YAAM,EAAE,QAAQ,OAAA,IAAW;AAC3B,YAAM,cAAc,MAAM,QAAQ,MAAM,IAAI,SAAS,WAAW,SAAY,CAAC,MAAM,IAAI,CAAA;AAEvF,YAAM,KAAK,KAAK,WAAA;AAEhB,aAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,aAAK,gBAAgB,IAAI,IAAI;AAAA,UAC3B;AAAA,UACA;AAAA,QAAA,CACD;AAED,aAAK,YAAY;AAAA,UACf,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,QAAA,CACT;AAGD,mBAAW,MAAM;AACf,cAAI,KAAK,gBAAgB,IAAI,EAAE,GAAG;AAChC,iBAAK,gBAAgB,OAAO,EAAE;AAC9B,mBAAO,oBAAoB,cAAc,gBAAgB,iBAAiB,CAAC;AAAA,UAC7E;AAAA,QACF,GAAG,IAAI,KAAK,GAAI;AAAA,MAClB,CAAC;AAAA,IACH;AAAA,IAEA,GAAG,OAAe,SAA6C;AAC7D,WAAK,OAAO,GAAG,OAAO,OAAO;AAC7B,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,OAAe,SAA6C;AAC9D,WAAK,OAAO,IAAI,OAAO,OAAO;AAC9B,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAMO,MAAM,gBAAgB;AAAA,IACnB;AAAA,IACA,SAAS;AAAA,IACT,kBAA+B,EAAE,QAAQ,IAAI,KAAK,GAAA;AAAA;AAAA,IAGjD;AAAA,IAET,YAAY,UAA4B;AACtC,WAAK,WAAW;AAChB,WAAK,MAAM,IAAI,WAAW,QAAQ;AAGlC,eAAS,GAAG,mBAAmB,CAAC,aAAsB;AACpD,YAAI,MAAM,QAAQ,QAAQ,KAAK,SAAS,SAAS,GAAG;AAClD,gBAAM,OAAO,SAAS,CAAC;AACvB,eAAK,kBAAkB;AACvB,eAAK,SAAS;AAAA,QAChB,OAAO;AACL,eAAK,kBAAkB,EAAE,QAAQ,IAAI,KAAK,GAAA;AAC1C,eAAK,SAAS;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH;AAAA;AAAA,IAGA,IAAI,QAAiB;AACnB,aAAO,KAAK;AAAA,IACd;AAAA;AAAA,IAGA,IAAI,iBAA8B;AAChC,aAAO,KAAK;AAAA,IACd;AAAA;AAAA;AAAA;AAAA,IAKA,WAAW,SAA4B;AACrC,WAAK,kBAAkB;AACvB,WAAK,SAAS;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA,IAKA,UAAU,SAA0B;AAElC,UAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,eAAO,QAAQ,WAAW;AAAA,MAC5B;AACA,UAAI,QAAQ,WAAW,IAAI,GAAG;AAC5B,eAAO,QAAQ,WAAW;AAAA,MAC5B;AACA,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA,IAKA,UAAU;AAAA,MACR,OAAO,CAAC,WAA2B;AAGjC,eAAO;AAAA,MACT;AAAA,MACA,SAAS,CAAC,QAAwB;AAChC,eAAO;AAAA,MACT;AAAA,IAAA;AAAA,EAEJ;AAAA,EAKA,MAAM,WAAW;AAAA,IACP;AAAA,IAER,YAAY,UAA4B;AACtC,WAAK,WAAW;AAAA,IAClB;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,KAAK,aAAwC;AACjD,aAAO,KAAK,SAAS,QAAQ;AAAA,QAC3B,QAAQ;AAAA,QACR,QAAQ;AAAA,MAAA,CACT;AAAA,IACH;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,mBAAmB,mBAA8C;AACrE,aAAO,KAAK,SAAS,QAAQ;AAAA,QAC3B,QAAQ;AAAA,QACR,QAAQ;AAAA,MAAA,CACT;AAAA,IACH;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,WAAW,SAAkC;AACjD,aAAO,KAAK,SAAS,QAAQ;AAAA,QAC3B,QAAQ;AAAA,QACR,QAAQ;AAAA,MAAA,CACT;AAAA,IACH;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,WAAW,SAAmC;AAClD,aAAO,KAAK,SAAS,QAAQ;AAAA,QAC3B,QAAQ;AAAA,QACR,QAAQ;AAAA,MAAA,CACT;AAAA,IACH;AAAA,EACF;AAaO,WAAS,iBAAiB,eAAe,KAA+D;AAC7G,QAAI,OAAO,WAAW,aAAa;AACjC,YAAM,IAAI,MAAM,yDAAyD;AAAA,IAC3E;AAEA,QAAI,OAAO,YAAY,OAAO,SAAS;AACrC,cAAQ,KAAK,sEAAsE;AACnF,aAAO,EAAE,UAAU,OAAO,UAAU,SAAS,OAAO,QAAA;AAAA,IACtD;AAEA,UAAM,WAAW,IAAI,iBAAiB,YAAY;AAClD,UAAM,UAAU,IAAI,gBAAgB,QAAQ;AAE5C,WAAO,WAAW;AAClB,WAAO,UAAU;AAEjB,YAAQ,IAAI,sCAAsC;AAClD,WAAO,EAAE,UAAU,QAAA;AAAA,EACrB;AC/SO,QAAM,gBAAwC;AAAA,IACnD,UAAU;AAAA,IACV,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKX;AAGO,QAAM,yBAAiD,OAAO;AAAA,IACnE,OAAO,QAAQ,aAAa,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,OAAO,GAAG,CAAC;AAAA,EAClE;AAGO,QAAM,sBAA8C;AAAA,IACzD,KAAK;AAAA,IACL,KAAK;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA;AAAA,IAET,KAAK;AAAA,IACL,KAAK;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAGO,QAAM,sBAA8C;AAAA,IACzD,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,EACX;AAMO,WAAS,aAAa,SAAyB;AACpD,WAAO,KAAK,QAAQ,SAAS,EAAE,CAAC;AAAA,EAClC;AAMO,WAAS,gBAAgB,YAA4B;AAC1D,QAAI,CAAC,WAAW,WAAW,IAAI,GAAG;AAChC,YAAM,IAAI,MAAM,yBAAyB,UAAU,EAAE;AAAA,IACvD;AACA,WAAO,SAAS,YAAY,EAAE;AAAA,EAChC;AAMO,WAAS,iBAAiB,YAAmC;AAClE,UAAM,UAAU,gBAAgB,UAAU;AAC1C,WAAO,uBAAuB,OAAO,KAAK;AAAA,EAC5C;AAMO,WAAS,cAAc,eAAsC;AAClE,UAAM,UAAU,cAAc,aAAa;AAC3C,WAAO,UAAU,aAAa,OAAO,IAAI;AAAA,EAC3C;AAKO,WAAS,WAAW,SAA0B;AACnD,WAAO,WAAW;AAAA,EACpB;AAMO,WAAS,iBAAiB,WAA2B;AAC1D,WAAO,oBAAoB,SAAS,KAAK,UAAU,YAAA;AAAA,EACrD;AChDO,WAAS,gBAAgB,eAAe,KAAkB;AAC/D,QAAI,OAAO,WAAW,aAAa;AACjC,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AAEA,QAAI,OAAO,KAAK;AACd,cAAQ,KAAK,+DAA+D;AAC5E,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,WAAW,IAAI,gBAAgB,YAAY;AACjD,WAAO,MAAM;AAEb,YAAQ,IAAI,+BAA+B;AAC3C,WAAO;AAAA,EACT;AAKO,WAAS,iBAAiB,eAAe,KAK9C;AACA,UAAM,MAAM,gBAAgB,YAAY;AACxC,UAAM,WAAW,qBAAqB,YAAY;AAClD,UAAM,EAAE,UAAU,YAAY,iBAAiB,YAAY;AAE3D,WAAO,EAAE,KAAK,UAAU,UAAU,QAAA;AAAA,EACpC;AAGA,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,OAAO,MAAM;AACjB,sBAAA;AACA,2BAAA;AACA,uBAAA;AAAA,IACF;AAGA,QAAI,SAAS,eAAe,WAAW;AACrC,eAAS,iBAAiB,oBAAoB,IAAI;AAAA,IACpD,OAAO;AACL,WAAA;AAAA,IACF;AAAA,EACF;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/packages/bio-sdk/package.json b/packages/bio-sdk/package.json index 6db80076b..7e51f88ca 100644 --- a/packages/bio-sdk/package.json +++ b/packages/bio-sdk/package.json @@ -1,24 +1,20 @@ { "name": "@biochain/bio-sdk", - "version": "0.1.0", + "version": "0.2.0", "description": "Bio Ecosystem SDK for MiniApps - window.bio provider", "type": "module", - "main": "./dist/index.cjs", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", + "module": "./src/index.ts", "exports": { ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs", - "types": "./dist/index.d.ts" + "default": "./src/index.ts" } }, "files": [ - "dist" + "src" ], "scripts": { - "build": "vite build", - "dev": "vite build --watch", + "build": "echo 'SDK has no build'", + "dev": "echo 'SDK has no dev'", "typecheck": "tsc --noEmit", "typecheck:run": "tsc --noEmit", "test": "vitest", diff --git a/packages/bio-sdk/src/miniapp-context.test.ts b/packages/bio-sdk/src/miniapp-context.test.ts index 3d5ca2155..f56bf1af7 100644 --- a/packages/bio-sdk/src/miniapp-context.test.ts +++ b/packages/bio-sdk/src/miniapp-context.test.ts @@ -12,12 +12,16 @@ const sampleContext: MiniappContext = { safeAreaInsets: { top: 12, right: 0, bottom: 8, left: 0 }, platform: 'web', locale: 'en-US', + direction: 'ltr', }, host: { name: 'KeyApp', version: '0.1.0', }, updatedAt: new Date().toISOString(), + theme: { + colorMode: 'light', + }, } describe('miniapp context sdk', () => { @@ -111,5 +115,8 @@ describe('miniapp context sdk', () => { const styles = getComputedStyle(document.documentElement) expect(styles.getPropertyValue('--keyapp-safe-area-top').trim()).toBe('12px') expect(styles.getPropertyValue('--keyapp-safe-area-bottom').trim()).toBe('8px') + expect(styles.getPropertyValue('--keyapp-lang').trim()).toBe('en-US') + expect(styles.getPropertyValue('--keyapp-direction').trim()).toBe('ltr') + expect(styles.getPropertyValue('--keyapp-color-mode').trim()).toBe('light') }) }) diff --git a/packages/bio-sdk/src/miniapp-context.ts b/packages/bio-sdk/src/miniapp-context.ts index 5707446d9..0d4331d34 100644 --- a/packages/bio-sdk/src/miniapp-context.ts +++ b/packages/bio-sdk/src/miniapp-context.ts @@ -13,6 +13,7 @@ export const MiniappContextEnvSchema = z.object({ safeAreaInsets: MiniappSafeAreaInsetsSchema, platform: z.enum(['web', 'dweb', 'ios', 'android']).optional(), locale: z.string().optional(), + direction: z.enum(['ltr', 'rtl']).optional(), }) export const MiniappContextHostSchema = z.object({ @@ -187,6 +188,14 @@ function readLocale(): string | undefined { return undefined } +function readDirection(): 'ltr' | 'rtl' | undefined { + if (typeof document !== 'undefined') { + const dir = document.documentElement.dir + if (dir === 'ltr' || dir === 'rtl') return dir + } + return undefined +} + function buildDefaultContext(): MiniappContext { return { version: 1, @@ -194,6 +203,7 @@ function buildDefaultContext(): MiniappContext { safeAreaInsets: readSafeAreaInsets(), platform: 'web', locale: readLocale(), + direction: readDirection(), }, host: { name: 'KeyApp', @@ -333,4 +343,14 @@ export function applyMiniappSafeAreaCssVars( element.style.setProperty('--keyapp-safe-area-right', `${right}px`) element.style.setProperty('--keyapp-safe-area-bottom', `${bottom}px`) element.style.setProperty('--keyapp-safe-area-left', `${left}px`) + + if (context.env.locale) { + element.style.setProperty('--keyapp-lang', context.env.locale) + } + if (context.env.direction) { + element.style.setProperty('--keyapp-direction', context.env.direction) + } + if (context.theme?.colorMode) { + element.style.setProperty('--keyapp-color-mode', context.theme.colorMode) + } } diff --git a/src/services/miniapp-runtime/index.ts b/src/services/miniapp-runtime/index.ts index 7751039d1..37698551e 100644 --- a/src/services/miniapp-runtime/index.ts +++ b/src/services/miniapp-runtime/index.ts @@ -274,6 +274,9 @@ function buildMiniappContextPayload(): MiniappContextPayload { i18n.language || document?.documentElement?.lang || (typeof navigator !== 'undefined' ? navigator.language : 'en'); + const direction = typeof i18n.dir === 'function' + ? i18n.dir(locale) + : (document?.documentElement?.dir || 'ltr'); const platform = isDwebEnvironment() ? 'dweb' : 'web'; return { @@ -282,6 +285,7 @@ function buildMiniappContextPayload(): MiniappContextPayload { safeAreaInsets: getMiniappSafeAreaInsets(), platform, locale, + direction, }, host: { name: 'KeyApp',