learning_ai_common_plat/packages/broadcast-client/src/deep-link.ts
Saravana Achu Mac 5fb49215cd chore(broadcast-client): document deep-link diagnostics
What changed:

- Keep broadcast deep-link warnings for host app integration failures.

- Add narrow lint justifications for the two intentional console diagnostics.

Warning impact:

- @bytelyst/broadcast-client no-console: 2 warnings -> 0 warnings.

Verification:

- pnpm --filter @bytelyst/broadcast-client build

- pnpm --filter @bytelyst/broadcast-client test

- pnpm --filter @bytelyst/broadcast-client exec eslint . --ext .ts,.tsx

- pnpm typecheck

- pnpm test

- pnpm lint

Co-Authored-By: GPT-5 Codex <noreply@openai.com>
2026-05-04 16:07:13 -07:00

166 lines
3.9 KiB
TypeScript

/**
* Deep Link Router — TypeScript
* Handles routing from push notification deep links to app screens
*/
export interface DeepLinkRoute {
screen: string;
params?: Record<string, string>;
}
export type DeepLinkHandler = (route: DeepLinkRoute) => void;
/**
* Deep Link Router class
*/
export class DeepLinkRouter {
private handlers = new Map<string, DeepLinkHandler>();
private fallbackHandler?: DeepLinkHandler;
/**
* Register a handler for a specific screen
*/
register(screen: string, handler: DeepLinkHandler): void {
this.handlers.set(screen, handler);
}
/**
* Set a fallback handler for unregistered screens
*/
setFallback(handler: DeepLinkHandler): void {
this.fallbackHandler = handler;
}
/**
* Parse a deep link URL and extract route
*/
parseDeepLink(url: string): DeepLinkRoute | null {
try {
const urlObj = new URL(url);
// Handle app-specific URLs: myapp://screen/params
if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') {
const routeParts = [urlObj.host, ...urlObj.pathname.split('/').filter(Boolean)].filter(
Boolean
);
const screen = routeParts[0] || 'home';
const params: Record<string, string> = {};
// Parse query params
urlObj.searchParams.forEach((value, key) => {
params[key] = value;
});
return { screen, params };
}
// Handle web URLs with deep link params
const deepLinkParam = urlObj.searchParams.get('dl');
if (deepLinkParam) {
return this.parseDeepLink(deepLinkParam);
}
// Handle path-based routing: /screen/params
const pathParts = urlObj.pathname.split('/').filter(Boolean);
if (pathParts.length > 0) {
const screen = pathParts[0];
const params: Record<string, string> = {};
urlObj.searchParams.forEach((value, key) => {
params[key] = value;
});
return { screen, params };
}
return null;
} catch {
return null;
}
}
/**
* Handle a deep link route
*/
handle(route: DeepLinkRoute): boolean {
const handler = this.handlers.get(route.screen);
if (handler) {
handler(route);
return true;
}
if (this.fallbackHandler) {
this.fallbackHandler(route);
return true;
}
// eslint-disable-next-line no-console -- Deep-link consumers need an opt-in diagnostic when a route is dropped.
console.warn(`[DeepLink] No handler for screen: ${route.screen}`);
return false;
}
/**
* Process a deep link URL end-to-end
*/
process(url: string): boolean {
const route = this.parseDeepLink(url);
if (!route) {
// eslint-disable-next-line no-console -- Parse failures are intentionally surfaced to host apps during integration.
console.warn(`[DeepLink] Failed to parse: ${url}`);
return false;
}
return this.handle(route);
}
}
/**
* Create a broadcast deep link URL
*/
export function createBroadcastDeepLink(
baseUrl: string,
screen: string,
params?: Record<string, string>,
broadcastId?: string
): string {
const url = new URL(baseUrl);
url.pathname = `/${screen}`;
if (params) {
Object.entries(params).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
}
if (broadcastId) {
url.searchParams.set('broadcastId', broadcastId);
}
return url.toString();
}
/**
* Common deep link screens for broadcast/survey flows
*/
export const DeepLinkScreens = {
// Broadcasts
BROADCAST_DETAIL: 'broadcast',
ANNOUNCEMENTS: 'announcements',
// Surveys
SURVEY: 'survey',
SURVEY_LIST: 'surveys',
// Product-specific (examples)
SETTINGS: 'settings',
PROFILE: 'profile',
UPGRADE: 'upgrade',
SUPPORT: 'support',
// Fallback
HOME: 'home',
} as const;
// Singleton instance for app-wide use
export const deepLinkRouter = new DeepLinkRouter();