or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# Superset UI Switchboard

1

2

Switchboard is a TypeScript library that provides secure communication between iframe windows and their parent windows using the MessageChannel API. It enables structured method calls across window boundaries with support for both synchronous (get) and asynchronous (emit) patterns, specifically designed for the Superset embedded SDK.

3

4

## Package Information

5

6

- **Package Name**: @superset-ui/switchboard

7

- **Package Type**: npm

8

- **Language**: TypeScript

9

- **Installation**: `npm install @superset-ui/switchboard`

10

11

## Core Imports

12

13

```typescript

14

import { Switchboard, type Params } from "@superset-ui/switchboard";

15

```

16

17

For CommonJS:

18

19

```javascript

20

const { Switchboard } = require("@superset-ui/switchboard");

21

```

22

23

## Basic Usage

24

25

```typescript

26

import { Switchboard } from "@superset-ui/switchboard";

27

28

// Create MessageChannel for communication

29

const channel = new MessageChannel();

30

31

// Parent window switchboard

32

const parentBoard = new Switchboard({

33

port: channel.port1,

34

name: 'parent',

35

debug: true

36

});

37

38

// Child window switchboard (inside iframe)

39

const childBoard = new Switchboard({

40

port: channel.port2,

41

name: 'child'

42

});

43

44

// Define method on child side

45

childBoard.defineMethod('getData', (args) => {

46

return { data: 'hello', timestamp: Date.now() };

47

});

48

49

// Start listening

50

childBoard.start();

51

52

// Call method from parent side

53

const result = await parentBoard.get('getData', { query: 'users' });

54

console.log(result); // { data: 'hello', timestamp: 1638360000000 }

55

56

// Fire-and-forget call

57

parentBoard.emit('logEvent', { event: 'user_action' });

58

```

59

60

## Architecture

61

62

Switchboard is built around the MessageChannel API and provides:

63

64

- **Method Registration**: Define callable methods on either side of the communication channel

65

- **Synchronous Calls**: `get()` method calls with Promise-based responses

66

- **Asynchronous Calls**: `emit()` method calls without waiting for responses

67

- **Error Handling**: Built-in error catching and messaging for failed method calls

68

- **Message Identification**: Unique message IDs to match requests with responses

69

- **Debug Support**: Optional logging for troubleshooting communication issues

70

71

## Capabilities

72

73

### Switchboard Class

74

75

Main class for managing iframe-parent window communication using MessageChannel.

76

77

```typescript { .api }

78

/**

79

* A utility for communications between an iframe and its parent, used by the Superset embedded SDK.

80

* This builds useful patterns on top of the basic functionality offered by MessageChannel.

81

*/

82

class Switchboard {

83

/** The MessagePort used for communication */

84

port: MessagePort;

85

/** Instance name for debugging */

86

name: string;

87

/** Registry of callable methods */

88

methods: Record<string, Method<any, unknown>>;

89

/** Internal message ID counter */

90

incrementor: number;

91

/** Debug mode flag */

92

debugMode: boolean;

93

94

constructor(params: Params);

95

}

96

```

97

98

### Constructor

99

100

Creates a new Switchboard instance with the specified configuration.

101

102

```typescript { .api }

103

/**

104

* Creates a new Switchboard instance

105

* @param params - Configuration parameters

106

*/

107

constructor(params: Params);

108

```

109

110

### Method Registration

111

112

Defines a method that can be called from the other side of the communication channel.

113

114

```typescript { .api }

115

/**

116

* Defines a method that can be "called" from the other side by sending an event

117

* @param methodName - Name of the method to register

118

* @param executor - Function to execute when method is called

119

*/

120

defineMethod<A = any, R = any>(methodName: string, executor: Method<A, R>): void;

121

```

122

123

**Usage Example:**

124

125

```typescript

126

switchboard.defineMethod('authenticate', async (credentials) => {

127

const { username, password } = credentials;

128

const token = await authenticateUser(username, password);

129

return { token, userId: 123, expires: Date.now() + 3600000 };

130

});

131

```

132

133

### Synchronous Method Calls

134

135

Calls a method registered on the other side and returns the result via Promise.

136

137

```typescript { .api }

138

/**

139

* Calls a method registered on the other side, and returns the result.

140

* Sends a "get" message, waits for a "reply" message with the result.

141

*

142

* @param method - Name of the method to call

143

* @param args - Arguments to pass (must be serializable)

144

* @returns Promise resolving to the method's return value

145

* @throws Error if method not found or execution fails

146

*/

147

get<T = unknown>(method: string, args?: unknown): Promise<T>;

148

```

149

150

**Usage Example:**

151

152

```typescript

153

// Call a method and wait for response

154

try {

155

const userData = await switchboard.get('fetchUserData', { userId: 123 });

156

console.log('User:', userData.name);

157

} catch (error) {

158

console.error('Failed to fetch user data:', error.message);

159

}

160

```

161

162

### Asynchronous Method Calls

163

164

Calls a method on the other side without waiting for a response (fire-and-forget).

165

166

```typescript { .api }

167

/**

168

* Emit calls a method on the other side just like get does.

169

* But emit doesn't wait for a response, it just sends and forgets.

170

*

171

* @param method - Name of the method to call

172

* @param args - Arguments to pass (must be serializable)

173

*/

174

emit(method: string, args?: unknown): void;

175

```

176

177

**Usage Example:**

178

179

```typescript

180

// Fire-and-forget method call

181

switchboard.emit('trackEvent', {

182

event: 'dashboard_viewed',

183

dashboardId: 456,

184

timestamp: Date.now()

185

});

186

```

187

188

### Start Communication

189

190

Starts the MessagePort to begin listening for messages.

191

192

```typescript { .api }

193

/**

194

* Starts the MessagePort to begin listening for messages

195

*/

196

start(): void;

197

```

198

199

## Types

200

201

### Params Interface

202

203

Configuration parameters for Switchboard constructor.

204

205

```typescript { .api }

206

/**

207

* Configuration parameters for Switchboard constructor

208

*/

209

interface Params {

210

/** MessagePort for communication (required) */

211

port: MessagePort;

212

/** Name for debugging purposes (default: 'switchboard') */

213

name?: string;

214

/** Enable debug logging (default: false) */

215

debug?: boolean;

216

}

217

```

218

219

### Method Type

220

221

Function signature for methods that can be registered with defineMethod.

222

223

```typescript { .api }

224

/**

225

* Function signature for methods that can be registered with defineMethod

226

* @template A - Type of arguments object

227

* @template R - Return type

228

*/

229

type Method<A extends {}, R> = (args: A) => R | Promise<R>;

230

```

231

232

## Error Handling

233

234

Switchboard provides built-in error handling for method calls:

235

236

- **Method Not Found**: If a called method doesn't exist, the Promise rejects with a descriptive error

237

- **Execution Errors**: If a method throws an error during execution, it's caught and returned as an error message

238

- **Serialization**: Only serializable data can be passed as arguments or return values (no functions, DOM nodes, etc.)

239

240

**Error Handling Example:**

241

242

```typescript

243

try {

244

const result = await switchboard.get('nonexistentMethod');

245

} catch (error) {

246

// Error message will be: "[switchboard_name] Method "nonexistentMethod" is not defined"

247

console.error('Method call failed:', error.message);

248

}

249

```

250

251

## Communication Flow

252

253

1. **Setup**: Both sides create Switchboard instances with their respective MessagePorts

254

2. **Method Registration**: Each side registers methods using `defineMethod()`

255

3. **Start Listening**: Call `start()` to begin processing messages

256

4. **Method Calls**: Use `get()` for responses or `emit()` for fire-and-forget calls

257

5. **Message Processing**: Switchboard handles message routing, method execution, and response delivery

258

259

## Common Use Cases

260

261

- **Embedded Dashboards**: Communication between Superset dashboards and parent applications

262

- **Authentication**: Passing authentication tokens between iframe and parent

263

- **Configuration**: Sending configuration updates from parent to iframe

264

- **Event Tracking**: Sending analytics events from iframe to parent

265

- **Data Exchange**: Requesting and sharing data between contexts while maintaining security boundaries