npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@omni-gate/frame-client-sdk

v0.3.0

Published

Frame-side (iframe/child window) SDK for Omni Gate sub-applications: heartbeat, JWT relay, module navigation and message forwarding to the parent shell. This is the package the many sub-apps install.

Downloads

214

Readme

@omni-gate/frame-client-sdk

The frame-side (iframe / child window) half of the Omni Gate frame-bridge SDK. This is the package a sub-application running inside the platform shell installs — one shell, many sub-apps, and this is what the sub-apps use.

It only talks to the parent window through a message bridge; it never calls a REST API directly and carries no parent-only dependency (no dexie, no JWT-expiry monitor). For the parent/shell counterpart, see @omni-gate/frame-parent-sdk.

简体中文 | English

Installation

npm install @omni-gate/frame-client-sdk @ticatec/iframe-message-bridge

This package re-exports everything from @omni-gate/frame-core (Permissions, BaseRestServiceProxy, i18nRes, constants), so most sub-apps only need to install this one package.

Quick Start

import OmniClientApi from '@omni-gate/frame-client-sdk';
import { MessageBridgeClient } from '@ticatec/iframe-message-bridge';

// Create the message bridge client, pointing at the parent window's origin
const bridge = new MessageBridgeClient('https://shell.example.com');

// Initialize the client API (singleton)
const api = await OmniClientApi.initialize(bridge);
const me = await api.getMe();

OmniClientApi starts a heartbeat as soon as it's initialized (default interval: 10s) and listens for user interaction (keydown, keyup, mousemove, mousedown, mouseup, click) to know whether to keep sending it, so the parent can track whether the sub-app tab is actually alive.

API Reference

OmniClientApi.initialize(bridge, options?)

Creates (once) and returns the singleton instance, wrapped in a Promise.

Same-origin (the platform's default, alias-based deployment): the Promise resolves immediately. Parent and sub-app share the same sessionStorage/ localStorage, so BaseRestServiceProxy in the sub-app already sees whatever the parent writes there — OmniClientApi doesn't need to do anything.

Cross-origin: parent and sub-app storage are separate, so initialize() makes one round trip to the parent for the current JWT and writes it into the sub-app's own storage before the Promise resolves — await it before issuing your first REST call. From then on, OmniClientApi listens for the parent's token-renewal and logoff broadcasts and keeps that local copy in sync automatically; you don't need to call getJwtToken() yourself just to feed BaseRestServiceProxy.

const options = {
  jwtStorage: sessionStorage,   // where to write the token in cross-origin mode (default: sessionStorage)
  jwtStorageKey: 'omni-token',  // storage key (default: 'omni-token')
};
const api = await OmniClientApi.initialize(bridge, options);

options only matters for cross-origin deployments, and only to pick where the synced token is written. It must match whatever jwtStorage/ jwtStorageKey your sub-app's own BaseRestServiceProxy subclass uses — otherwise the two will read and write different storage/keys and the token will never be found.

OmniClientApi.getInstance()

Returns the singleton instance created by initialize(). Throws if called before initialize() or after destroy() — it never returns null, so you don't need to null-check every call site.

sameOrigin / crossOrigin

Read-only getters. The SDK detects at construction time whether it can read window.parent.location.origin (same-origin) or not (cross-origin).

The platform's committed deployment model is same-origin, alias-based hosting, and most of the JWT/logoff behavior described below is written with that in mind. OmniClientApi also handles the cross-origin case (see initialize() above): it keeps a local copy of the JWT in sync via broadcasts instead of relying on shared storage. What cross-origin sub-apps still don't get for free is everything else this SDK proxies through the parent (getMe, getPermission, getOptionsData, ...) — those already go over the bridge either way, so they're unaffected either way.

User / permission / dictionary reads (all proxied to the parent)

  • getMe<T = any>(): Promise<T> — current user, via OmniParentApi.getMe() on the other side.
  • getPermission<T = Record<string, any>>(appCode: string): Promise<T> — application-level permission map.
  • getEntityPermission<T = Record<string, any>>(appCode: string, entityCode: string): Promise<T> — entity-level permission map.
  • getOptionsData<T = any>(dicNames: string | string[]): Promise<T> — one or more data-dictionary option lists.
  • getChildrenOptions<T = any>(dic: string, code: string): Promise<T> — child options of a hierarchical dictionary node.
  • getErrorMessage(serviceCode: string, errorCode: string): Promise<string> — localized text for a single error code (language is decided by the parent). Only the resolved string crosses the bridge — the parent keeps its own full error-message table to itself. Returns a fallback "error code not found" message instead of undefined when the code isn't in the table.
  • getCurrentLanguage(): Promise<string> — current language code (e.g. 'en', 'zh-CN'), falling back to 'en'.

Each generic type parameter defaults to the untyped shape shown above, so existing call sites keep working unchanged; a sub-app that knows its own data shape can specify it explicitly, e.g. api.getMe<CurrentUser>().

All of these simply call bridge.emit(...) and return {} (or []) if the parent doesn't answer — they never throw for a missing parent response (getCurrentLanguage() is the one exception, returning the plain string 'en' as its fallback rather than an empty object).

openModule(modHash: string, params: any): void

Fire-and-forget message asking the parent shell to open another module.

JWT token handling

await api.getJwtToken(); // always asks the parent window; no local read/cache of its own

getJwtToken() never reads or caches locally — every call goes straight to the parent. It's meant for the rare case where you need the raw token string yourself (e.g. to hand to a non-HTTP client); BaseRestServiceProxy doesn't use it.

BaseRestServiceProxy's request interceptor is synchronous, so it can't await a bridge round trip on every outgoing request — it reads the token straight out of storage.getItem(...). Same-origin, that's free: sub-apps are deployed as path aliases under the shell's own domain (not subdomains), so the sub-app's sessionStorage/localStorage is the shell's storage, and whatever the parent writes (initial login, token renewal) is visible immediately, no sync needed. Cross-origin, that storage is the sub-app's own and starts out empty; initialize() (see above) seeds it from the parent once and keeps it current via broadcasts, so BaseRestServiceProxy still finds a fresh token there without knowing or caring which deployment mode it's running in.

Logoff handling

Sub-apps hold no local copy of anything the parent owns — not the JWT token, not getMe/getPermission/getOptionsData results. Same-origin, the parent (OmniParentApi.broadcastLogoff()) clears the JWT token from that same shared storage before broadcasting, so by the time the sub-app's LOGOFF handler runs, there's nothing local left for it to clean up. Cross-origin, the parent's cleanup only touches its own storage, so OmniClientApi clears its own synced copy when the LOGOFF broadcast arrives — this happens automatically, onLogoff() below is purely for your own UI reaction.

api.onLogoff((data) => {
  console.log('logged off at', data.timestamp);
  router.push('/login');
});

onLogoff() is purely a notification hook now (for UI reactions like redirecting to a login screen) — it no longer clears storage by default.

Broadcasts

api.onBroadcast('theme-changed', (data) => applyTheme(data.theme));
api.offBroadcast('theme-changed');
api.clearBroadcastHandlers();

destroy(): void

Stops the heartbeat timer, removes DOM/message listeners, and clears the singleton instance.

TypeScript

Fully typed; the shared types come from @omni-gate/frame-core and are re-exported here.

Dependencies

  • @omni-gate/frame-core (workspace dependency)
  • @ticatec/iframe-message-bridgepeer dependency. OmniClientApi.initialize(bridge) takes a MessageBridgeClient instance you construct yourself, so this package doesn't bundle its own copy — install it alongside @omni-gate/frame-client-sdk:
    npm install @omni-gate/frame-client-sdk @ticatec/iframe-message-bridge

License

MIT