@kadanza/extension-sdk 0.0.14 → 0.2.0

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/dist/index.d.ts CHANGED
@@ -31,7 +31,9 @@ export declare const CONNECTION_EVENTS: {
31
31
  readonly updatePageSettings: "UPDATE_PAGE_SETTINGS";
32
32
  /** Parent → child: result of a page settings update. */
33
33
  readonly pageSettingsUpdated: "PAGE_SETTINGS_UPDATED";
34
- /** Childparent: extension route changed. */
34
+ /** Parentchild: ask the SPA to navigate without reloading the iframe. */
35
+ readonly requestNavigationChange: "REQUEST_NAVIGATION_CHANGE";
36
+ /** Child → parent: extension route changed (ack or spontaneous). */
35
37
  readonly navigationChange: "NAVIGATION_CHANGE";
36
38
  };
37
39
 
@@ -42,6 +44,11 @@ export declare type ConnectionEvent = (typeof CONNECTION_EVENTS)[keyof typeof CO
42
44
  export declare interface ConnectOptions {
43
45
  /** Handshake timeout in milliseconds (default 10_000). */
44
46
  timeoutMs?: number;
47
+ /**
48
+ * Routing mode announced to the parent on `HANDSHAKE_INIT`.
49
+ * HashRouter SPAs should pass `client-hash`. Default: `server`.
50
+ */
51
+ routingType?: RoutingType;
45
52
  /**
46
53
  * Enables proactive auth-token refresh. After the handshake, the SDK reads
47
54
  * the auth token's Unix-seconds `expires` value and arms a one-shot timer
@@ -78,11 +85,26 @@ export declare interface ConnectOptions {
78
85
  */
79
86
  export declare const createExtensionSDK: () => IExtensionSDK;
80
87
 
81
- /** Branding tokens from the parent for aligning extension UI. */
88
+ /** Default when `routingType` is omitted or unrecognized. */
89
+ export declare const DEFAULT_ROUTING_TYPE: RoutingType;
90
+
91
+ /**
92
+ * Tenant branding from the parent, for aligning extension UI with the host.
93
+ *
94
+ * The host should send this whenever a tenant is in context (Experience Pages
95
+ * and Admin Console). Handshake still succeeds if it is omitted — treat every
96
+ * field as optional.
97
+ *
98
+ * - `primaryColor` — tenant palette primary; accents, buttons, links
99
+ * - `fontFamily` — tenant font family; body and UI type
100
+ * - `borderRadius` — tenant roundness; controls and cards
101
+ *
102
+ * Values may be missing when the tenant has no setting configured.
103
+ */
82
104
  export declare interface DesignTokens {
83
- primaryColor: string;
84
- fontFamily: string;
85
- borderRadius: string;
105
+ primaryColor?: string;
106
+ fontFamily?: string;
107
+ borderRadius?: string;
86
108
  }
87
109
 
88
110
  /**
@@ -94,17 +116,28 @@ export declare interface DesignTokens {
94
116
  */
95
117
  export declare function enrichExtensionUrl(urlRaw: string | null | undefined, tenantUrl?: string): URL | null;
96
118
 
97
- /** Context about the embedded extension and its host page from handshake. */
119
+ /**
120
+ * Host context from handshake. Only set when the parent includes it.
121
+ *
122
+ * Field availability depends on where the iframe is mounted:
123
+ * - Experience Pages typically send `spaceId` and `pageId`.
124
+ * - Admin Console Pages typically omit those — there is no space page.
125
+ *
126
+ * Other identity fields are sent when the host knows them. Handshake does not
127
+ * require this object.
128
+ */
98
129
  export declare interface ExtensionDetails {
99
- extensionId: string;
100
- tenantId: string;
101
- tenantDomain: string;
102
- /** Base URL of the parent application. */
103
- baseUrl: string;
104
- spaceId: string;
105
- pageId: string;
130
+ extensionId?: string;
131
+ tenantId?: string;
132
+ tenantDomain?: string;
133
+ /** Base URL of the parent application (used to derive the Platform API origin). */
134
+ baseUrl?: string;
135
+ /** Space that owns the Experience Page. Omitted in Admin Console. */
136
+ spaceId?: string;
137
+ /** Experience Page id. Omitted in Admin Console. */
138
+ pageId?: string;
106
139
  /** BCP 47 locale from the parent. */
107
- locale: string;
140
+ locale?: string;
108
141
  }
109
142
 
110
143
  /** Envelope for parent/child `postMessage` traffic. */
@@ -130,6 +163,7 @@ export declare class ExtensionSDK implements IExtensionSDK {
130
163
  emitRequestAuthTokenRefresh(options?: RequestOptions): Promise<AuthToken>;
131
164
  emitUpdatePageSettings(payload: UpdatePageSettingsPayload, options?: RequestOptions): Promise<PageSettingsUpdatedPayload>;
132
165
  onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;
166
+ onNavigate(handler: (payload: RequestNavigationChangePayload) => void): () => void;
133
167
  onAuthTokenRefresh(handler: (authToken: AuthToken) => void): () => void;
134
168
  emitNavigationChange(payload: NavigationChangePayload): void;
135
169
  }
@@ -140,7 +174,9 @@ export declare class ExtensionSDKHost implements IExtensionSDKHost {
140
174
  constructor(options: ExtensionSDKHostOptions);
141
175
  start(): void;
142
176
  destroy(): void;
177
+ getRoutingType(): RoutingType;
143
178
  emitLoadPageSettings(settings: PageSettings | null): void;
179
+ requestNavigationChange(payload: RequestNavigationChangePayload, options?: RequestOptions): Promise<NavigationChangePayload>;
144
180
  }
145
181
 
146
182
  /**
@@ -155,10 +191,11 @@ export declare interface ExtensionSDKHostOptions {
155
191
  /** Extension origin used as `postMessage` targetOrigin / source filter. */
156
192
  origin: string;
157
193
  /**
158
- * Builds the handshake payload (including auth token) when the child sends
159
- * `HANDSHAKE_INIT`.
194
+ * Builds the handshake payload when the child sends `HANDSHAKE_INIT`.
195
+ * Context fields are optional; send `designTokens` whenever a tenant is in
196
+ * context. `spaceId` / `pageId` apply to Experience Pages only.
160
197
  */
161
- resolveHandshakePayload: () => HandshakePayload | Promise<HandshakePayload>;
198
+ resolveHandshakePayload: (extensionSDKHost: IExtensionSDKHost) => Partial<HandshakePayload> | Promise<Partial<HandshakePayload>>;
162
199
  /**
163
200
  * Resolves a fresh auth token when the child sends `REQUEST_TOKEN_REFRESH`.
164
201
  */
@@ -168,13 +205,33 @@ export declare interface ExtensionSDKHostOptions {
168
205
  * Return `true` on success; the host emits `PAGE_SETTINGS_UPDATED`.
169
206
  */
170
207
  onUpdatePageSettings?: (settings: PageSettings) => boolean | Promise<boolean>;
208
+ /**
209
+ * Called when the child reports a route change via `NAVIGATION_CHANGE`
210
+ * (spontaneous or as ACK to {@link IExtensionSDKHost.requestNavigationChange}).
211
+ */
212
+ onNavigationChange?: (payload: NavigationChangePayload) => void;
171
213
  }
172
214
 
173
- /** Payload delivered with a successful `HANDSHAKE_ACK`. */
215
+ /** Payload for `HANDSHAKE_INIT` (child parent). */
216
+ export declare interface HandshakeInitPayload {
217
+ /**
218
+ * Declares how the extension routes. Omit or unknown → `server`.
219
+ * Soft navigation requires `client-hash`.
220
+ */
221
+ routingType?: RoutingType;
222
+ }
223
+
224
+ /**
225
+ * Context delivered with `HANDSHAKE_ACK`.
226
+ *
227
+ * Handshake completes as soon as the parent ACKs. Every property is optional
228
+ * and only populated when that host surface provides it. After `connect()`,
229
+ * omitted wire fields are normalized to `null`.
230
+ */
174
231
  export declare interface HandshakePayload {
175
- authToken: AuthToken;
176
- extensionDetails: ExtensionDetails;
177
- designTokens: DesignTokens;
232
+ authToken: AuthToken | null;
233
+ extensionDetails: ExtensionDetails | null;
234
+ designTokens: DesignTokens | null;
178
235
  pageSettings: PageSettings | null;
179
236
  }
180
237
 
@@ -200,10 +257,19 @@ export declare interface IExtensionSDK {
200
257
  * Opt-in proactive refresh can be enabled with
201
258
  * `authTokenAutoRefresh: true` (optional `authTokenBufferMs`).
202
259
  *
203
- * @param options - Handshake timeout and optional auth-token auto-refresh.
260
+ * Pass `routingType: "client-hash"` for HashRouter SPAs so the parent can
261
+ * soft-navigate without reloading the iframe. Default is `"server"`.
262
+ *
263
+ * Handshake completes when the parent sends `HANDSHAKE_ACK`. Context fields
264
+ * (`authToken`, `extensionDetails`, `designTokens`, `pageSettings`) are
265
+ * optional — omitted values are `null`. Use them only when the current host
266
+ * surface provides them (e.g. `spaceId` / `pageId` on Experience Pages,
267
+ * `designTokens` when a tenant is in context).
268
+ *
269
+ * @param options - Handshake timeout, routing type, and optional auth-token auto-refresh.
204
270
  * @throws {InvalidOriginError} When `tenantUrl` is missing or invalid.
205
271
  * @throws When not embedded in a parent frame, destroyed, or the handshake
206
- * times out / returns an invalid payload.
272
+ * times out.
207
273
  */
208
274
  connect(options?: ConnectOptions): Promise<HandshakePayload>;
209
275
  /**
@@ -215,11 +281,20 @@ export declare interface IExtensionSDK {
215
281
  destroy(): void;
216
282
  /** Whether a successful handshake has completed and not been destroyed. */
217
283
  readonly isConnected: boolean;
218
- /** Last auth token from handshake or refresh; `null` until connected. */
284
+ /**
285
+ * Last auth token from handshake or refresh.
286
+ * `null` until connected, or when the host omitted it.
287
+ */
219
288
  getAuthToken(): AuthToken | null;
220
- /** Extension context from handshake; `null` until connected. */
289
+ /**
290
+ * Extension context from handshake.
291
+ * `null` until connected, or when the host omitted it.
292
+ */
221
293
  getExtensionDetails(): ExtensionDetails | null;
222
- /** Design tokens from handshake; `null` until connected. */
294
+ /**
295
+ * Tenant branding from handshake.
296
+ * `null` until connected, or when the host omitted it.
297
+ */
223
298
  getDesignTokens(): DesignTokens | null;
224
299
  /**
225
300
  * Latest page settings from handshake or `LOAD_PAGE_SETTINGS`;
@@ -237,8 +312,8 @@ export declare interface IExtensionSDK {
237
312
  */
238
313
  getTenantUrl(): string | null;
239
314
  /**
240
- * Platform API origin derived from handshake `baseUrl`;
241
- * `null` until connected.
315
+ * Platform API origin derived from handshake `baseUrl`.
316
+ * `null` until connected, or when `extensionDetails.baseUrl` was omitted.
242
317
  */
243
318
  getApiUrl(): string | null;
244
319
  /**
@@ -250,8 +325,9 @@ export declare interface IExtensionSDK {
250
325
  * @typeParam T - Expected JSON response body.
251
326
  * @param endpoint - Root-relative API path.
252
327
  * @param options - Standard fetch options.
253
- * @throws When not connected, the endpoint is invalid, the request fails,
254
- * or the response is not successful JSON.
328
+ * @throws When not connected, handshake did not include `authToken` and
329
+ * `extensionDetails` (`baseUrl`, `tenantDomain`), the endpoint is invalid,
330
+ * the request fails, or the response is not successful JSON.
255
331
  */
256
332
  apiCall<T>(endpoint: string, options?: RequestInit): Promise<T>;
257
333
  /**
@@ -277,6 +353,14 @@ export declare interface IExtensionSDK {
277
353
  * @returns Unsubscribe function.
278
354
  */
279
355
  onLoadPageSettings(handler: (settings: PageSettings) => void): () => void;
356
+ /**
357
+ * Registers a handler for parent-initiated `REQUEST_NAVIGATION_CHANGE`.
358
+ * Navigate the SPA to `payload.path` and acknowledge with
359
+ * {@link emitNavigationChange}.
360
+ *
361
+ * @returns Unsubscribe function.
362
+ */
363
+ onNavigate(handler: (payload: RequestNavigationChangePayload) => void): () => void;
280
364
  /**
281
365
  * Registers a handler for auth-token updates (requested or parent-pushed).
282
366
  *
@@ -286,7 +370,8 @@ export declare interface IExtensionSDK {
286
370
  /**
287
371
  * Notifies the parent that the extension's route changed.
288
372
  *
289
- * Wire: `NAVIGATION_CHANGE` (fire-and-forget).
373
+ * Wire: `NAVIGATION_CHANGE` (fire-and-forget). Also used as the ACK after
374
+ * handling {@link onNavigate}.
290
375
  *
291
376
  * @param payload - Navigation change payload (`path` within the extension).
292
377
  * @throws When not connected or destroyed.
@@ -310,12 +395,30 @@ export declare interface IExtensionSDKHost {
310
395
  * Tears down the listener. The instance cannot be restarted after destroy.
311
396
  */
312
397
  destroy(): void;
398
+ /**
399
+ * Routing type from the last `HANDSHAKE_INIT`. Defaults to `server` until
400
+ * the child connects (and for omit / unrecognized values).
401
+ */
402
+ getRoutingType(): RoutingType;
313
403
  /**
314
404
  * Asks the child to open page settings UI with the given values.
315
405
  *
316
406
  * Wire: `LOAD_PAGE_SETTINGS` (fire-and-forget).
317
407
  */
318
408
  emitLoadPageSettings(settings: PageSettings | null): void;
409
+ /**
410
+ * Asks the child SPA to navigate to `payload.path` without reloading the
411
+ * iframe. Resolves when the child ACKs with a matching `NAVIGATION_CHANGE`.
412
+ *
413
+ * Soft navigation is intended for `client-hash` extensions; the host decides
414
+ * whether to call this or fall back to an iframe `src` reload.
415
+ *
416
+ * @param payload - Target path within the extension.
417
+ * @param options - Optional request timeout (default 10s).
418
+ * @throws When destroyed, a request is already in progress, the payload is
419
+ * invalid, or the request times out.
420
+ */
421
+ requestNavigationChange(payload: RequestNavigationChangePayload, options?: RequestOptions): Promise<NavigationChangePayload>;
319
422
  }
320
423
 
321
424
  /** Thrown when `tenantUrl` is missing or not a safe postMessage origin. */
@@ -333,9 +436,21 @@ export declare function isValidExtensionUrl(urlRaw: string | null | undefined, c
333
436
 
334
437
  /** Payload for `NAVIGATION_CHANGE` (child → parent). */
335
438
  export declare interface NavigationChangePayload {
439
+ /** Path within the extension (e.g. `/settings`). */
336
440
  path: string;
441
+ /**
442
+ * Query string of the child's current route, including the leading `?`
443
+ * (e.g. `?tab=history`). Omitted or empty means no query.
444
+ *
445
+ * For `client-hash` extensions the child reads this from its hash fragment,
446
+ * not from `window.location.search`.
447
+ */
448
+ search?: string;
337
449
  }
338
450
 
451
+ /** Normalize a wire / option value to a known {@link RoutingType}. */
452
+ export declare function normalizeRoutingType(value: unknown): RoutingType;
453
+
339
454
  /** Opaque page-level settings bag owned by the extension and synced with the parent. */
340
455
  export declare type PageSettings = Record<string, unknown>;
341
456
 
@@ -364,6 +479,20 @@ export declare function postToChild(contentWindow: Window, origin: string, type:
364
479
  */
365
480
  export declare function readTenantUrlFromLocation(search?: string): string | null;
366
481
 
482
+ /** Payload for `REQUEST_NAVIGATION_CHANGE` (parent → child). */
483
+ export declare interface RequestNavigationChangePayload {
484
+ /** Path within the extension (e.g. `/settings`). */
485
+ path: string;
486
+ /**
487
+ * Query string for the target route, including the leading `?`
488
+ * (e.g. `?tab=history`). Omitted or empty means no query.
489
+ *
490
+ * For `client-hash` extensions the query belongs inside the hash fragment;
491
+ * the child SDK consumer is responsible for applying it to its router.
492
+ */
493
+ search?: string;
494
+ }
495
+
367
496
  /** Options for request/response SDK methods that wait on a parent reply. */
368
497
  export declare interface RequestOptions {
369
498
  /** Request timeout in milliseconds (default 10_000). */
@@ -380,6 +509,15 @@ export declare interface RequestOptions {
380
509
  */
381
510
  export declare function resolveAllowedOrigin(tenantUrl: string | null): string;
382
511
 
512
+ /**
513
+ * How the extension handles in-app routing.
514
+ *
515
+ * - `server` — full document loads (iframe `src` reload). Default.
516
+ * - `client-hash` — hash router SPA; supports soft navigation via
517
+ * `REQUEST_NAVIGATION_CHANGE` / `NAVIGATION_CHANGE`.
518
+ */
519
+ export declare type RoutingType = "server" | "client-hash";
520
+
383
521
  /**
384
522
  * Subscribes to `message` events from a child frame at `origin`.
385
523
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadanza/extension-sdk",
3
- "version": "0.0.14",
3
+ "version": "0.2.0",
4
4
  "description": "Helpers for building extensions",
5
5
  "keywords": [
6
6
  "kadanza",