@pmate/bridge 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # Native application opening
2
+
3
+ The application catalog and deployment URL lookup belong to the apps API. The bridge only opens the resolved URL in a separate native WebView, leaving its caller alive.
4
+
5
+ ```tsx
6
+ import { openApp, useIsPmate } from "@pmate/bridge"
7
+ await openApp({ url: "https://issues.pmate.chat/" })
8
+ ```
9
+
10
+ Requires a host implementing `container.openApp`. Resolves `{ opened: true }` after native presentation, not after page load. Rejects unsupported hosts and invalid URLs; callers must display the error. HTTPS and loopback HTTP are allowed; embedded URL credentials are rejected. No authentication token is added to the URL. Closing the child returns to its existing parent. Browser apps should use their explicit browser navigation path instead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/bridge",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "files": [
5
5
  "src",
6
6
  "global.d.ts"
package/src/hooks.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useState } from "react"
2
2
  import { useOptionalPmateBridgeContext } from "./context"
3
3
  import {
4
+ openApp,
4
5
  getFallbackSafeInsets,
5
6
  getNotificationStatus,
6
7
  getPushToken,
@@ -111,3 +112,7 @@ export function useGetPushToken() {
111
112
  [context]
112
113
  )
113
114
  }
115
+
116
+ export function useOpenApp() {
117
+ return openApp
118
+ }
package/src/index.ts CHANGED
@@ -2,6 +2,9 @@ export * from "./PmateBridgeProvider"
2
2
  export * from "./SafeAreaVarsProvider"
3
3
  export * from "./hooks"
4
4
  export {
5
+ openApp,
6
+ getPendingAppOpen,
7
+ onAppOpenRequested,
5
8
  cancelAlarm,
6
9
  cancelAllAlarms,
7
10
  connectBluetoothDevice,
package/src/internal.ts CHANGED
@@ -245,3 +245,34 @@ export function onBluetoothEvent(
245
245
  }
246
246
  return (window as PmateWindow).PmateBridge!.on!(`bluetooth.${eventName}`, handler)
247
247
  }
248
+
249
+ /** Native only. Errors are surfaced; this never navigates or replaces the caller. */
250
+ export async function openApp(options: import("./types").OpenAppOptions): Promise<import("./types").OpenAppResult> {
251
+ const url = new URL(options.url)
252
+ const local = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)
253
+ if ((url.protocol !== "https:" && !(local && url.protocol === "http:")) || url.username || url.password) {
254
+ return Promise.reject(new Error("App URL must use HTTPS (or loopback HTTP) without credentials"))
255
+ }
256
+ const notificationId = options.notificationId
257
+ if (notificationId !== undefined && (!notificationId || notificationId.length > 512)) throw new Error("Invalid notification ID")
258
+ const result = await requireBridge<import("./types").OpenAppResult>("container.openApp", { url: url.href, ...(notificationId === undefined ? {} : { notificationId }) })
259
+ if (result?.opened !== true) throw new Error("Native app opening was not confirmed")
260
+ return result
261
+ }
262
+
263
+ /** Available only to a native host's designated launcher. Older hosts reject it. */
264
+ export async function getPendingAppOpen(): Promise<import("./types").AppOpenIntent | null> {
265
+ const result = await requireBridge<import("./types").AppOpenIntent | null>("container.getPendingAppOpen")
266
+ if (result === null) return null
267
+ if (!result || typeof result.appId !== "string" || !result.appId || result.appId.length > 128
268
+ || typeof result.notificationId !== "string" || !result.notificationId || result.notificationId.length > 512
269
+ || typeof result.origin !== "string" || result.origin.length > 2048) throw new Error("Invalid application notification")
270
+ const origin = new URL(result.origin)
271
+ if (origin.protocol !== "https:" || origin.username || origin.password || origin.pathname !== "/" || origin.search || origin.hash) throw new Error("Invalid application notification origin")
272
+ return { appId: result.appId, notificationId: result.notificationId, origin: origin.origin }
273
+ }
274
+
275
+ /** A hint to re-read the pending intent; the event itself never supplies a URL to open. */
276
+ export function onAppOpenRequested(handler: () => void): () => void {
277
+ return hasWindow() ? (window as PmateWindow).PmateBridge?.on?.("container.appOpenRequested", handler) ?? (() => {}) : () => {}
278
+ }
package/src/types.ts CHANGED
@@ -207,3 +207,9 @@ export interface BluetoothDataEvent {
207
207
  encoding: BluetoothBinaryEncoding
208
208
  receivedAt: number
209
209
  }
210
+
211
+ /** Opens a separate native WebView. The caller resolves the app URL via the apps API. */
212
+ export interface OpenAppOptions { url: string; notificationId?: string }
213
+ export interface OpenAppResult { opened: true }
214
+ /** Destination only; notification content is delivered to the target app after authentication. */
215
+ export interface AppOpenIntent { appId: string; notificationId: string; origin: string }