@contrail/extensions-sdk 1.0.18-5 → 1.0.19

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.
@@ -2,7 +2,7 @@ name: empty-passing-test
2
2
  on: pull_request
3
3
  jobs:
4
4
  test:
5
- runs-on: ubuntu-20.04
5
+ runs-on: ubuntu-24.04
6
6
  strategy:
7
7
  matrix:
8
8
  node-version: [16.17.0]
@@ -7,15 +7,18 @@ on:
7
7
  workflow_dispatch: # allow running in github actions manually
8
8
  jobs:
9
9
  test:
10
- runs-on: ubuntu-20.04
10
+ runs-on: ubuntu-24.04
11
+ strategy:
12
+ matrix:
13
+ node-version: [20]
11
14
  steps:
15
+ - uses: actions/checkout@v4
16
+
12
17
  - name: Node.js
13
- uses: actions/setup-node@v3
18
+ uses: actions/setup-node@v4
14
19
  with:
15
20
  node-version: ${{ matrix.node-version }}
16
21
 
17
- - uses: actions/checkout@v3
18
-
19
22
  - name: NPM Install
20
23
  env:
21
24
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,10 +1,9 @@
1
- import { AppContext, AppExtensionEvent } from '../apps/app-context';
1
+ import { AppContext } from '../apps/app-context';
2
2
  import { EntitiesClientAction } from '../entities/entities-action-handler';
3
3
  import { AppExtensionMessage } from '../extensions/app-extension-message-handler';
4
4
  import { TypesClientAction } from '../types/types-action-handler';
5
5
  export interface RegisterExtensionActions {
6
- init(context: AppContext): Promise<void>;
7
- emitEvent(event: AppExtensionEvent): void;
6
+ init(context: AppContext): void;
8
7
  }
9
8
  export interface ExtensionActions {
10
9
  close(): void;
@@ -12,7 +11,6 @@ export interface ExtensionActions {
12
11
  sendMessageToTypesClient(message: TypesClientAction): Promise<any>;
13
12
  sendMessageToHost(message: AppExtensionMessage): Promise<any>;
14
13
  }
15
- export declare let EXTENSION_ACTIONS: ExtensionActions | undefined;
14
+ export declare let EXTENSION_ACTIONS: ExtensionActions;
16
15
  export declare function setExtensionActions(actions: ExtensionActions): void;
17
- export declare function getExtensionActions(): ExtensionActions | undefined;
18
- export declare function getExtensionActionsOrThrow(): ExtensionActions;
16
+ export declare function getExtensionActions(): ExtensionActions;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getExtensionActionsOrThrow = exports.getExtensionActions = exports.setExtensionActions = exports.EXTENSION_ACTIONS = void 0;
3
+ exports.getExtensionActions = exports.setExtensionActions = exports.EXTENSION_ACTIONS = void 0;
4
4
  function setExtensionActions(actions) {
5
5
  exports.EXTENSION_ACTIONS = actions;
6
6
  }
@@ -9,11 +9,3 @@ function getExtensionActions() {
9
9
  return exports.EXTENSION_ACTIONS;
10
10
  }
11
11
  exports.getExtensionActions = getExtensionActions;
12
- function getExtensionActionsOrThrow() {
13
- const actions = exports.EXTENSION_ACTIONS;
14
- if (!actions) {
15
- throw new Error('Extension is not connected. Ensure AppExtension.registerAppExtension() has resolved before using this API.');
16
- }
17
- return actions;
18
- }
19
- exports.getExtensionActionsOrThrow = getExtensionActionsOrThrow;
@@ -84,15 +84,6 @@ export interface ShowcaseContext {
84
84
  previewFileId?: string;
85
85
  backingAssortmentId?: string;
86
86
  }
87
- export declare enum AppEventType {
88
- SelectedElementsChanged = "selected_elements_changed",
89
- ShowcaseFramesChanged = "showcase_frames_changed",
90
- ShowcaseCurrentFrameChanged = "showcase_current_frame_changed"
91
- }
92
- export interface AppExtensionEvent<T = any> {
93
- type: AppEventType;
94
- data: T;
95
- }
96
87
  export declare const CONTEXT: AppContext;
97
88
  export declare function setAppContext(context: AppContext): void;
98
89
  export declare function getAppContext(): AppContext;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAppContext = exports.setAppContext = exports.CONTEXT = exports.AppEventType = exports.VibeIQAppType = void 0;
3
+ exports.getAppContext = exports.setAppContext = exports.CONTEXT = exports.VibeIQAppType = void 0;
4
4
  var VibeIQAppType;
5
5
  (function (VibeIQAppType) {
6
6
  VibeIQAppType["BOARDS"] = "boards";
@@ -8,12 +8,6 @@ var VibeIQAppType;
8
8
  VibeIQAppType["SHOWCASE"] = "showcase";
9
9
  VibeIQAppType["ADMIN_CONSOLE"] = "admin_console";
10
10
  })(VibeIQAppType = exports.VibeIQAppType || (exports.VibeIQAppType = {}));
11
- var AppEventType;
12
- (function (AppEventType) {
13
- AppEventType["SelectedElementsChanged"] = "selected_elements_changed";
14
- AppEventType["ShowcaseFramesChanged"] = "showcase_frames_changed";
15
- AppEventType["ShowcaseCurrentFrameChanged"] = "showcase_current_frame_changed";
16
- })(AppEventType = exports.AppEventType || (exports.AppEventType = {}));
17
11
  exports.CONTEXT = {};
18
12
  function setAppContext(context) {
19
13
  Object.assign(exports.CONTEXT, context);
@@ -7,7 +7,14 @@ export declare enum BoardCommand {
7
7
  GET_ELEMENTS = "board:get_elements",
8
8
  RECOLOR_IMAGES = "board:recolor_images",
9
9
  ADD_CONTENT_TO_ENTITY_FROM_ELEMENTS = "board:add_content_to_entity_from_elements",
10
- ADD_FRAMES_FROM_TEMPLATE = "board:add_frames_from_template"
10
+ ADD_FRAMES_FROM_TEMPLATE = "board:add_frames_from_template",
11
+ ADD_ITEMS_TO_CLIPBOARD = "board:add_items_to_clipboard"
12
+ }
13
+ export interface ClipboardItemsPayload {
14
+ clipboardItems: Array<{
15
+ itemId: string;
16
+ projectItemId?: string | null;
17
+ }>;
11
18
  }
12
19
  export interface BoardDocumentElement extends DocumentElement {
13
20
  [key: string]: any;
@@ -51,6 +58,10 @@ export declare class BoardsApp {
51
58
  static getElements(criteria: object): Promise<Array<DocumentElement>>;
52
59
  static createNewContentAndAssignToEntity(assignmentOptions: any, entity: any, elements: Array<DocumentElement>): Promise<Array<DocumentElement>>;
53
60
  static recolorImages(imageElements: Array<DocumentElement>, hexCode: string, hexMap?: {}): Promise<Array<DocumentElement>>;
61
+ static addItemsToClipboard(clipboardItems: Array<{
62
+ itemId: string;
63
+ projectItemId?: string | null;
64
+ }>): void;
54
65
  private static validateBoardContext;
55
66
  private static validateAndReturnResults;
56
67
  }
@@ -21,6 +21,7 @@ var BoardCommand;
21
21
  BoardCommand["RECOLOR_IMAGES"] = "board:recolor_images";
22
22
  BoardCommand["ADD_CONTENT_TO_ENTITY_FROM_ELEMENTS"] = "board:add_content_to_entity_from_elements";
23
23
  BoardCommand["ADD_FRAMES_FROM_TEMPLATE"] = "board:add_frames_from_template";
24
+ BoardCommand["ADD_ITEMS_TO_CLIPBOARD"] = "board:add_items_to_clipboard";
24
25
  })(BoardCommand = exports.BoardCommand || (exports.BoardCommand = {}));
25
26
  class BoardsApp {
26
27
  static getCurrentBoard() {
@@ -30,7 +31,7 @@ class BoardsApp {
30
31
  static addElements(elements) {
31
32
  BoardsApp.validateBoardContext();
32
33
  if (elements === null || elements === void 0 ? void 0 : elements.length) {
33
- (0, actions_1.getExtensionActionsOrThrow)().sendMessageToHost({
34
+ (0, actions_1.getExtensionActions)().sendMessageToHost({
34
35
  command: BoardCommand.ADD_ELEMENTS,
35
36
  data: elements,
36
37
  });
@@ -40,7 +41,7 @@ class BoardsApp {
40
41
  return __awaiter(this, void 0, void 0, function* () {
41
42
  BoardsApp.validateBoardContext();
42
43
  if (frames === null || frames === void 0 ? void 0 : frames.length) {
43
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToHost({
44
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToHost({
44
45
  command: BoardCommand.ADD_FRAMES_FROM_TEMPLATE,
45
46
  data: frames,
46
47
  });
@@ -53,13 +54,13 @@ class BoardsApp {
53
54
  BoardsApp.validateBoardContext();
54
55
  const elementsWithIds = elements.filter((element) => !!element.id);
55
56
  if (elementsWithIds === null || elementsWithIds === void 0 ? void 0 : elementsWithIds.length) {
56
- (0, actions_1.getExtensionActionsOrThrow)().sendMessageToHost({ command: BoardCommand.DELETE_ELEMENTS, data: elementsWithIds });
57
+ (0, actions_1.getExtensionActions)().sendMessageToHost({ command: BoardCommand.DELETE_ELEMENTS, data: elementsWithIds });
57
58
  }
58
59
  }
59
60
  static modifyElements(changeObjects) {
60
61
  BoardsApp.validateBoardContext();
61
62
  if (changeObjects === null || changeObjects === void 0 ? void 0 : changeObjects.length) {
62
- (0, actions_1.getExtensionActionsOrThrow)().sendMessageToHost({
63
+ (0, actions_1.getExtensionActions)().sendMessageToHost({
63
64
  command: BoardCommand.MODIFY_ELEMENTS,
64
65
  data: changeObjects,
65
66
  });
@@ -68,7 +69,7 @@ class BoardsApp {
68
69
  static getElements(criteria) {
69
70
  return __awaiter(this, void 0, void 0, function* () {
70
71
  BoardsApp.validateBoardContext();
71
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToHost({
72
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToHost({
72
73
  command: BoardCommand.GET_ELEMENTS,
73
74
  data: criteria,
74
75
  });
@@ -83,7 +84,7 @@ class BoardsApp {
83
84
  entity,
84
85
  elements,
85
86
  };
86
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToHost({
87
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToHost({
87
88
  command: BoardCommand.ADD_CONTENT_TO_ENTITY_FROM_ELEMENTS,
88
89
  data,
89
90
  });
@@ -93,13 +94,22 @@ class BoardsApp {
93
94
  static recolorImages(imageElements, hexCode, hexMap = {}) {
94
95
  return __awaiter(this, void 0, void 0, function* () {
95
96
  BoardsApp.validateBoardContext();
96
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToHost({
97
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToHost({
97
98
  command: BoardCommand.RECOLOR_IMAGES,
98
99
  data: { imageElements, hexCode, hexMap },
99
100
  });
100
101
  return this.validateAndReturnResults(results);
101
102
  });
102
103
  }
104
+ static addItemsToClipboard(clipboardItems) {
105
+ BoardsApp.validateBoardContext();
106
+ if (clipboardItems === null || clipboardItems === void 0 ? void 0 : clipboardItems.length) {
107
+ (0, actions_1.getExtensionActions)().sendMessageToHost({
108
+ command: BoardCommand.ADD_ITEMS_TO_CLIPBOARD,
109
+ data: { clipboardItems },
110
+ });
111
+ }
112
+ }
103
113
  static validateBoardContext() {
104
114
  const context = (0, app_context_1.getAppContext)();
105
115
  if (context.appContext.vibeIQApp !== app_context_1.VibeIQAppType.BOARDS || !context.appContext.board) {
@@ -1,3 +1,4 @@
1
1
  export { getAppContext, AppContext, BoardContext, PlanContext, ShowcaseContext, VibeIQAppType } from './app-context';
2
2
  export * from './boards';
3
3
  export * from './plan';
4
+ export * from './showcase';
package/lib/apps/index.js CHANGED
@@ -20,3 +20,4 @@ Object.defineProperty(exports, "getAppContext", { enumerable: true, get: functio
20
20
  Object.defineProperty(exports, "VibeIQAppType", { enumerable: true, get: function () { return app_context_1.VibeIQAppType; } });
21
21
  __exportStar(require("./boards"), exports);
22
22
  __exportStar(require("./plan"), exports);
23
+ __exportStar(require("./showcase"), exports);
package/lib/apps/plan.js CHANGED
@@ -35,18 +35,18 @@ class PlanApp {
35
35
  static addRows(collectionElements) {
36
36
  PlanApp.validatePlanContext();
37
37
  if (collectionElements === null || collectionElements === void 0 ? void 0 : collectionElements.length) {
38
- const actions = (0, actions_1.getExtensionActionsOrThrow)();
38
+ const actions = (0, actions_1.getExtensionActions)();
39
39
  actions.sendMessageToHost({ command: PlanCommand.ADD_ROWS, data: collectionElements });
40
40
  }
41
41
  }
42
42
  static clearSelectedRows() {
43
43
  PlanApp.validatePlanContext();
44
- const actions = (0, actions_1.getExtensionActionsOrThrow)();
44
+ const actions = (0, actions_1.getExtensionActions)();
45
45
  actions.sendMessageToHost({ command: PlanCommand.CLEAR_SELECTED_ROWS });
46
46
  }
47
47
  static showMessage(message) {
48
48
  PlanApp.validatePlanContext();
49
- const actions = (0, actions_1.getExtensionActionsOrThrow)();
49
+ const actions = (0, actions_1.getExtensionActions)();
50
50
  actions.sendMessageToHost({ command: PlanCommand.SHOW_MESSAGE, data: message });
51
51
  }
52
52
  static validatePlanContext() {
@@ -0,0 +1,11 @@
1
+ export declare enum ShowcaseCommand {
2
+ ADD_ITEMS_TO_CLIPBOARD = "showcase:add_items_to_clipboard"
3
+ }
4
+ export declare class ShowcaseApp {
5
+ static getCurrentShowcase(): any;
6
+ static addItemsToClipboard(clipboardItems: Array<{
7
+ itemId: string;
8
+ projectItemId?: string | null;
9
+ }>): void;
10
+ private static validateShowcaseContext;
11
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ShowcaseApp = exports.ShowcaseCommand = void 0;
4
+ const actions_1 = require("../actions/actions");
5
+ const app_context_1 = require("./app-context");
6
+ var ShowcaseCommand;
7
+ (function (ShowcaseCommand) {
8
+ ShowcaseCommand["ADD_ITEMS_TO_CLIPBOARD"] = "showcase:add_items_to_clipboard";
9
+ })(ShowcaseCommand = exports.ShowcaseCommand || (exports.ShowcaseCommand = {}));
10
+ class ShowcaseApp {
11
+ static getCurrentShowcase() {
12
+ var _a;
13
+ ShowcaseApp.validateShowcaseContext();
14
+ return (_a = (0, app_context_1.getAppContext)().appContext) === null || _a === void 0 ? void 0 : _a.showcase;
15
+ }
16
+ static addItemsToClipboard(clipboardItems) {
17
+ ShowcaseApp.validateShowcaseContext();
18
+ if (clipboardItems === null || clipboardItems === void 0 ? void 0 : clipboardItems.length) {
19
+ (0, actions_1.getExtensionActions)().sendMessageToHost({
20
+ command: ShowcaseCommand.ADD_ITEMS_TO_CLIPBOARD,
21
+ data: { clipboardItems },
22
+ });
23
+ }
24
+ }
25
+ static validateShowcaseContext() {
26
+ var _a, _b;
27
+ const context = (0, app_context_1.getAppContext)();
28
+ if (((_a = context.appContext) === null || _a === void 0 ? void 0 : _a.vibeIQApp) !== app_context_1.VibeIQAppType.SHOWCASE || !((_b = context.appContext) === null || _b === void 0 ? void 0 : _b.showcase)) {
29
+ throw new Error('App extension has not been initialized with Showcase context.');
30
+ }
31
+ }
32
+ }
33
+ exports.ShowcaseApp = ShowcaseApp;
@@ -17,7 +17,7 @@ class EntitiesClientUpdateOptions {
17
17
  class EntitiesClient {
18
18
  get(options) {
19
19
  return __awaiter(this, void 0, void 0, function* () {
20
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToEntitiesClient({
20
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToEntitiesClient({
21
21
  method: entities_action_handler_1.EntitiesClientMethods.GET,
22
22
  options,
23
23
  });
@@ -26,7 +26,7 @@ class EntitiesClient {
26
26
  }
27
27
  create(options) {
28
28
  return __awaiter(this, void 0, void 0, function* () {
29
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToEntitiesClient({
29
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToEntitiesClient({
30
30
  method: entities_action_handler_1.EntitiesClientMethods.CREATE,
31
31
  options,
32
32
  });
@@ -35,7 +35,7 @@ class EntitiesClient {
35
35
  }
36
36
  batchCreate(options) {
37
37
  return __awaiter(this, void 0, void 0, function* () {
38
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToEntitiesClient({
38
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToEntitiesClient({
39
39
  method: entities_action_handler_1.EntitiesClientMethods.BATCH_CREATE,
40
40
  options,
41
41
  });
@@ -44,7 +44,7 @@ class EntitiesClient {
44
44
  }
45
45
  update(options) {
46
46
  return __awaiter(this, void 0, void 0, function* () {
47
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToEntitiesClient({
47
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToEntitiesClient({
48
48
  method: entities_action_handler_1.EntitiesClientMethods.UPDATE,
49
49
  options,
50
50
  });
@@ -53,7 +53,7 @@ class EntitiesClient {
53
53
  }
54
54
  batchUpdate(options) {
55
55
  return __awaiter(this, void 0, void 0, function* () {
56
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToEntitiesClient({
56
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToEntitiesClient({
57
57
  method: entities_action_handler_1.EntitiesClientMethods.BATCH_UPDATE,
58
58
  options,
59
59
  });
@@ -62,7 +62,7 @@ class EntitiesClient {
62
62
  }
63
63
  delete(options) {
64
64
  return __awaiter(this, void 0, void 0, function* () {
65
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToEntitiesClient({
65
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToEntitiesClient({
66
66
  method: entities_action_handler_1.EntitiesClientMethods.DELETE,
67
67
  options,
68
68
  });
@@ -71,7 +71,7 @@ class EntitiesClient {
71
71
  }
72
72
  batchDelete(options) {
73
73
  return __awaiter(this, void 0, void 0, function* () {
74
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToEntitiesClient({
74
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToEntitiesClient({
75
75
  method: entities_action_handler_1.EntitiesClientMethods.BATCH_DELETE,
76
76
  options,
77
77
  });
@@ -1,11 +1,6 @@
1
- import { AppContext, AppExtensionEvent } from '../apps/app-context';
1
+ import { AppContext } from '../apps/app-context';
2
2
  import { AppExtensionMessageHandler } from './app-extension-message-handler';
3
- export interface RegisterHostWithAppExtensionOptions {
4
- timeoutMs?: number;
5
- }
6
3
  export declare class AppExtensionHost {
7
- private static appExtension;
8
- static registerHostWithAppExtension(iframe: HTMLIFrameElement, context: AppContext, customMessageHandler: AppExtensionMessageHandler, options?: RegisterHostWithAppExtensionOptions): Promise<void>;
9
- static emitEvent(event: AppExtensionEvent): void;
4
+ static registerHostWithAppExtension(iframe: HTMLIFrameElement, context: AppContext, customMessageHandler: AppExtensionMessageHandler): Promise<void>;
10
5
  private static getExtensionActionsToRunOnHost;
11
6
  }
@@ -13,21 +13,18 @@ exports.AppExtensionHost = void 0;
13
13
  const penpal_1 = require("penpal");
14
14
  const entities_action_handler_1 = require("../entities/entities-action-handler");
15
15
  const types_action_handler_1 = require("../types/types-action-handler");
16
- const connection_timeout_1 = require("./connection-timeout");
17
16
  class AppExtensionHost {
18
- static registerHostWithAppExtension(iframe, context, customMessageHandler, options) {
17
+ static registerHostWithAppExtension(iframe, context, customMessageHandler) {
19
18
  return __awaiter(this, void 0, void 0, function* () {
20
19
  try {
21
20
  const hostActions = AppExtensionHost.getExtensionActionsToRunOnHost(customMessageHandler);
22
21
  const connection = (0, penpal_1.connectToChild)({ iframe, methods: Object.assign({}, hostActions) });
22
+ const iframeWindow = iframe.contentWindow;
23
+ iframeWindow.postMessage({ isConnected: true }, iframe.src);
23
24
  console.log('[HostApp] Awaiting connection to app extension...');
24
- const connectionPromise = (options === null || options === void 0 ? void 0 : options.timeoutMs) !== undefined
25
- ? (0, connection_timeout_1.withConnectionTimeout)(connection.promise, options.timeoutMs, () => new connection_timeout_1.HostConnectionTimeoutError(options.timeoutMs))
26
- : connection.promise;
27
- const appExtension = yield connectionPromise;
25
+ const appExtension = yield connection.promise;
28
26
  console.log('[HostApp] Connected to app extension.');
29
- AppExtensionHost.appExtension = appExtension;
30
- yield appExtension.init(context);
27
+ appExtension.init(context);
31
28
  }
32
29
  catch (error) {
33
30
  console.error('Failed to connect host app with app extension:', error);
@@ -35,13 +32,6 @@ class AppExtensionHost {
35
32
  }
36
33
  });
37
34
  }
38
- static emitEvent(event) {
39
- if (!AppExtensionHost.appExtension) {
40
- console.warn('[HostApp] Cannot emit event: no app extension connected.');
41
- return;
42
- }
43
- AppExtensionHost.appExtension.emitEvent(event);
44
- }
45
35
  static getExtensionActionsToRunOnHost(customMessageHandler) {
46
36
  return {
47
37
  close() {
@@ -60,4 +50,3 @@ class AppExtensionHost {
60
50
  }
61
51
  }
62
52
  exports.AppExtensionHost = AppExtensionHost;
63
- AppExtensionHost.appExtension = null;
@@ -1,15 +1,7 @@
1
- import { AppEventType } from '../apps/app-context';
2
- export interface RegisterAppExtensionOptions {
3
- timeoutMs?: number;
4
- }
5
1
  export declare class AppExtension {
6
2
  private static initPromiseResolver;
7
- private static eventListeners;
8
- static registerAppExtension(options?: RegisterAppExtensionOptions): Promise<void>;
3
+ static registerAppExtension(): Promise<void>;
9
4
  private static getExtensionActions;
10
5
  private static initAppExtension;
11
- private static handleEvent;
12
- private static getCurrentValueForEvent;
13
- static subscribe<T = any>(eventType: AppEventType, listener: (data: T) => void): () => void;
14
6
  static close(): void;
15
7
  }
@@ -13,18 +13,14 @@ exports.AppExtension = void 0;
13
13
  const penpal_1 = require("penpal");
14
14
  const app_context_1 = require("../apps/app-context");
15
15
  const actions_1 = require("../actions/actions");
16
- const connection_timeout_1 = require("./connection-timeout");
17
16
  class AppExtension {
18
- static registerAppExtension(options) {
17
+ static registerAppExtension() {
19
18
  return __awaiter(this, void 0, void 0, function* () {
20
19
  try {
21
20
  const extensionRegistrationActions = AppExtension.getExtensionActions();
22
21
  const connection = (0, penpal_1.connectToParent)({ methods: Object.assign({}, extensionRegistrationActions) });
23
22
  console.log('[AppExtension] Awaiting connection to host app...');
24
- const connectionPromise = (options === null || options === void 0 ? void 0 : options.timeoutMs) !== undefined
25
- ? (0, connection_timeout_1.withConnectionTimeout)(connection.promise, options.timeoutMs, () => new connection_timeout_1.ExtensionConnectionTimeoutError(options.timeoutMs))
26
- : connection.promise;
27
- const hostApp = yield connectionPromise;
23
+ const hostApp = yield connection.promise;
28
24
  console.log('[AppExtension] Connected to host app.');
29
25
  (0, actions_1.setExtensionActions)(hostApp);
30
26
  yield new Promise((resolve) => {
@@ -42,9 +38,6 @@ class AppExtension {
42
38
  init(context) {
43
39
  return AppExtension.initAppExtension(context);
44
40
  },
45
- emitEvent(event) {
46
- return AppExtension.handleEvent(event);
47
- },
48
41
  };
49
42
  }
50
43
  static initAppExtension(context) {
@@ -56,75 +49,9 @@ class AppExtension {
56
49
  }
57
50
  });
58
51
  }
59
- static handleEvent(event) {
60
- var _a, _b, _c, _d, _e, _f, _g;
61
- const existing = (0, app_context_1.getAppContext)();
62
- switch (event.type) {
63
- case app_context_1.AppEventType.SelectedElementsChanged:
64
- (0, app_context_1.setAppContext)(Object.assign(Object.assign({}, existing), { appContext: Object.assign(Object.assign({}, ((_a = existing.appContext) !== null && _a !== void 0 ? _a : {})), { selectedElements: event.data }) }));
65
- break;
66
- case app_context_1.AppEventType.ShowcaseFramesChanged:
67
- (0, app_context_1.setAppContext)(Object.assign(Object.assign({}, existing), { appContext: Object.assign(Object.assign({}, ((_b = existing.appContext) !== null && _b !== void 0 ? _b : {})), { showcase: Object.assign(Object.assign({}, ((_d = (_c = existing.appContext) === null || _c === void 0 ? void 0 : _c.showcase) !== null && _d !== void 0 ? _d : {})), { frames: event.data }) }) }));
68
- break;
69
- case app_context_1.AppEventType.ShowcaseCurrentFrameChanged:
70
- (0, app_context_1.setAppContext)(Object.assign(Object.assign({}, existing), { appContext: Object.assign(Object.assign({}, ((_e = existing.appContext) !== null && _e !== void 0 ? _e : {})), { showcase: Object.assign(Object.assign({}, ((_g = (_f = existing.appContext) === null || _f === void 0 ? void 0 : _f.showcase) !== null && _g !== void 0 ? _g : {})), { currentFrame: event.data }) }) }));
71
- break;
72
- }
73
- const listeners = AppExtension.eventListeners.get(event.type);
74
- if (listeners) {
75
- listeners.forEach((listener) => {
76
- try {
77
- listener(event.data);
78
- }
79
- catch (error) {
80
- console.error(`[AppExtension] Error in listener for event "${event.type}":`, error);
81
- }
82
- });
83
- }
84
- }
85
- static getCurrentValueForEvent(eventType) {
86
- var _a, _b, _c, _d, _e;
87
- const ctx = (0, app_context_1.getAppContext)();
88
- switch (eventType) {
89
- case app_context_1.AppEventType.SelectedElementsChanged:
90
- return (_a = ctx.appContext) === null || _a === void 0 ? void 0 : _a.selectedElements;
91
- case app_context_1.AppEventType.ShowcaseFramesChanged:
92
- return (_c = (_b = ctx.appContext) === null || _b === void 0 ? void 0 : _b.showcase) === null || _c === void 0 ? void 0 : _c.frames;
93
- case app_context_1.AppEventType.ShowcaseCurrentFrameChanged:
94
- return (_e = (_d = ctx.appContext) === null || _d === void 0 ? void 0 : _d.showcase) === null || _e === void 0 ? void 0 : _e.currentFrame;
95
- }
96
- }
97
- static subscribe(eventType, listener) {
98
- if (!AppExtension.eventListeners.has(eventType)) {
99
- AppExtension.eventListeners.set(eventType, []);
100
- }
101
- AppExtension.eventListeners.get(eventType).push(listener);
102
- const currentValue = AppExtension.getCurrentValueForEvent(eventType);
103
- if (currentValue !== undefined) {
104
- try {
105
- listener(currentValue);
106
- }
107
- catch (error) {
108
- console.error(`[AppExtension] Error emitting initial value for event "${eventType}":`, error);
109
- }
110
- }
111
- return () => {
112
- const listeners = AppExtension.eventListeners.get(eventType);
113
- if (listeners) {
114
- const index = listeners.indexOf(listener);
115
- if (index > -1) {
116
- listeners.splice(index, 1);
117
- }
118
- }
119
- };
120
- }
121
52
  static close() {
122
- const actions = (0, actions_1.getExtensionActions)();
123
- if (actions) {
124
- actions.close();
125
- }
53
+ (0, actions_1.getExtensionActions)().close();
126
54
  }
127
55
  }
128
56
  exports.AppExtension = AppExtension;
129
57
  AppExtension.initPromiseResolver = null;
130
- AppExtension.eventListeners = new Map();
@@ -1,4 +1,3 @@
1
1
  export * from './app-extension-host';
2
2
  export * from './app-extension-message-handler';
3
3
  export * from './app-extension';
4
- export * from './connection-timeout';
@@ -17,4 +17,3 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./app-extension-host"), exports);
18
18
  __exportStar(require("./app-extension-message-handler"), exports);
19
19
  __exportStar(require("./app-extension"), exports);
20
- __exportStar(require("./connection-timeout"), exports);
@@ -15,7 +15,7 @@ const types_action_handler_1 = require("./types-action-handler");
15
15
  class TypesClient {
16
16
  getType(options) {
17
17
  return __awaiter(this, void 0, void 0, function* () {
18
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToTypesClient({
18
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToTypesClient({
19
19
  method: types_action_handler_1.TypesClientMethods.GET_TYPE,
20
20
  options,
21
21
  });
@@ -24,7 +24,7 @@ class TypesClient {
24
24
  }
25
25
  getByRootAndPath(options) {
26
26
  return __awaiter(this, void 0, void 0, function* () {
27
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToTypesClient({
27
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToTypesClient({
28
28
  method: types_action_handler_1.TypesClientMethods.GET_BY_ROOT_AND_PATH,
29
29
  options,
30
30
  });
@@ -34,7 +34,7 @@ class TypesClient {
34
34
  getProperty(typeId, propertySlug) {
35
35
  return __awaiter(this, void 0, void 0, function* () {
36
36
  const getPropertyOptions = { typeId, propertySlug };
37
- const results = yield (0, actions_1.getExtensionActionsOrThrow)().sendMessageToTypesClient({
37
+ const results = yield (0, actions_1.getExtensionActions)().sendMessageToTypesClient({
38
38
  method: types_action_handler_1.TypesClientMethods.GET_PROPERTY,
39
39
  options: getPropertyOptions,
40
40
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/extensions-sdk",
3
- "version": "1.0.18-5",
3
+ "version": "1.0.19",
4
4
  "description": "Client library for interfacing with VibeIQ's services and apps from an extension.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,183 +0,0 @@
1
- # Extension SDK Guide
2
-
3
- This guide explains how the Contrail Extensions SDK works, how the **host app** (Boards, Showcase, Plan) establishes the connection and passes context, and how **external developers** write extensions that consume that context.
4
-
5
- ---
6
-
7
- ## How context works
8
-
9
- The host sends the full `AppContext` **once** over the Penpal connection when it calls `init(context)` after the extension registers. The extension receives it during `registerAppExtension()` and can read it at any time via `getAppContext()`. There is no real-time context update mechanism; the extension only has the snapshot provided at initialization.
10
-
11
- ---
12
-
13
- ## For the Host App: Establishing the Connection
14
-
15
- The host app is responsible for loading the extension in an iframe, registering with the SDK, and passing the initial context.
16
-
17
- ### 1. Install and import the SDK
18
-
19
- ```ts
20
- import {
21
- AppExtensionHost,
22
- AppContext,
23
- AppExtensionMessage,
24
- AppExtensionMessageHandler,
25
- } from '@contrail/extensions-sdk'; // or your package name
26
- ```
27
-
28
- ### 2. Create the extension iframe
29
-
30
- Create an `HTMLIFrameElement` that loads your extension's entry URL (e.g. the built extension app). The extension script inside the iframe will call `AppExtension.registerAppExtension()` and use Penpal to connect to the parent.
31
-
32
- ### 3. Implement the message handler
33
-
34
- The host must implement `AppExtensionMessageHandler` to handle commands from the extension (e.g. close, custom commands):
35
-
36
- ```ts
37
- const messageHandler: AppExtensionMessageHandler = {
38
- async handleMessage(message: AppExtensionMessage): Promise<unknown> {
39
- switch (message.command) {
40
- case 'close':
41
- // Close the extension panel or navigate away
42
- break;
43
- // Handle other commands your extension sends via sendMessageToHost()
44
- default:
45
- break;
46
- }
47
- return undefined;
48
- },
49
- };
50
- ```
51
-
52
- ### 4. Build initial context and register with the extension
53
-
54
- Gather the `AppContext` (user, app type, board/plan/showcase snapshot, etc.) and call `registerHostWithAppExtension`. This establishes the Penpal connection and sends the context once via `init(context)`.
55
-
56
- ```ts
57
- const iframe = document.querySelector('iframe#extension') as HTMLIFrameElement;
58
- const context: AppContext = {
59
- user: currentUser,
60
- appContext: {
61
- vibeIQApp: VibeIQAppType.SHOWCASE,
62
- showcase: {
63
- id: showcase.id,
64
- name: showcase.name,
65
- orgId: showcase.orgId,
66
- currentFrame: activeFrame,
67
- frames: showcase.frames,
68
- // ... other fields
69
- },
70
- // board, plan, selectedElements, etc. as needed
71
- },
72
- };
73
-
74
- await AppExtensionHost.registerHostWithAppExtension(iframe, context, messageHandler);
75
- // Connection is ready. The extension has received context via init().
76
- ```
77
-
78
- The extension receives this context once at init. If the host needs to change what the extension sees (e.g. after navigating to a different showcase or frame), it would need to re-load or re-initialize the extension with new context; the SDK does not support pushing context updates after init.
79
-
80
- ---
81
-
82
- ## For Extension Developers: Writing an Extension
83
-
84
- Extensions run inside an iframe loaded by the host. They use the SDK to register with the host and read the context that was passed at initialization.
85
-
86
- ### 1. Install and import the SDK
87
-
88
- ```ts
89
- import {
90
- AppExtension,
91
- getAppContext,
92
- VibeIQAppType,
93
- } from '@contrail/extensions-sdk';
94
- ```
95
-
96
- ### 2. Register the extension on load
97
-
98
- Your extension entry point (e.g. main.tsx or main.ts) must call `AppExtension.registerAppExtension()` and wait for it. This sets up the Penpal connection and waits for the host to call `init(context)`.
99
-
100
- ```ts
101
- // In your extension's bootstrap (e.g. main.tsx)
102
- async function bootstrap() {
103
- await AppExtension.registerAppExtension();
104
- // Connection is ready. Initial context is set.
105
- // Mount your app (e.g. React root).
106
- }
107
-
108
- bootstrap().catch(console.error);
109
- ```
110
-
111
- Do not render your UI or depend on context until `registerAppExtension()` has resolved. The host calls `init(context)` once; that is the only time context is provided.
112
-
113
- ### 3. Read context
114
-
115
- Use `getAppContext()` to read the context snapshot that was passed at init:
116
-
117
- ```ts
118
- const context = getAppContext();
119
-
120
- if (context.appContext?.vibeIQApp === VibeIQAppType.SHOWCASE) {
121
- const showcase = context.appContext.showcase;
122
- const currentFrame = showcase?.currentFrame;
123
- const frames = showcase?.frames;
124
- }
125
-
126
- const selectedElements = context.appContext?.selectedElements ?? [];
127
- ```
128
-
129
- This context does not change after init. The extension only has the snapshot the host provided when the connection was established.
130
-
131
- ### 4. Showcase context
132
-
133
- When the host app is Showcase (or includes showcase in context), the host includes `showcase` in the `AppContext` passed to `registerHostWithAppExtension` / `init(context)`. The extension reads it via `getAppContext().appContext?.showcase` (e.g. `currentFrame`, `frames`). There is no API to receive showcase updates after init; the extension works with the initial snapshot.
134
-
135
- ### 5. Sending messages to the host
136
-
137
- Use the SDK APIs that talk to the host over the Penpal connection:
138
-
139
- ```ts
140
- // Close the extension (host handles in messageHandler)
141
- AppExtension.close();
142
- ```
143
-
144
- For app-specific commands (e.g. Board or Plan actions), use the SDK's app modules (e.g. `Boards`, `Plan`), which send messages to the host internally. Custom commands can be sent via those modules or whatever host-messaging API your SDK exposes; the host receives them in `messageHandler.handleMessage`.
145
-
146
- ### 6. Clipboard: bulk add items
147
-
148
- Extensions can request that the host bulk-add items to the user's clipboard by sending a message with a list of `{ itemId, projectItemId }`. The host dispatches its existing clipboard store action; there is no success or error response unless the host adds one later.
149
-
150
- **Board:** Use `BoardsApp.addItemsToClipboard(clipboardItems)`:
151
-
152
- ```ts
153
- import { BoardsApp } from '@contrail/extensions-sdk';
154
-
155
- BoardsApp.addItemsToClipboard([
156
- { itemId: 'item-1', projectItemId: 'proj-1' },
157
- { itemId: 'item-2', projectItemId: null },
158
- ]);
159
- ```
160
-
161
- You can also send the command explicitly with `command: BoardCommand.ADD_ITEMS_TO_CLIPBOARD` and `data: { clipboardItems }` via your extension's host message channel.
162
-
163
- **Showcase:** Use `ShowcaseApp.addItemsToClipboard(clipboardItems)`:
164
-
165
- ```ts
166
- import { ShowcaseApp } from '@contrail/extensions-sdk';
167
-
168
- ShowcaseApp.addItemsToClipboard([
169
- { itemId: 'item-1', projectItemId: 'proj-1' },
170
- { itemId: 'item-2', projectItemId: null },
171
- ]);
172
- ```
173
-
174
- You can also send the command explicitly with `command: ShowcaseCommand.ADD_ITEMS_TO_CLIPBOARD` and `data: { clipboardItems }` via your extension's host message channel.
175
-
176
- **Payload:** `clipboardItems: Array<{ itemId: string; projectItemId?: string | null }>`. The host normalizes and dispatches its existing clipboard action; no new API or store logic is required on the host.
177
-
178
- ---
179
-
180
- ## Summary
181
-
182
- - **Host:** Load extension iframe → build `AppContext` → call `AppExtensionHost.registerHostWithAppExtension(iframe, context, messageHandler)`. Context is sent once via `init(context)`; there is no API to push context updates after that.
183
- - **Extension:** Call `AppExtension.registerAppExtension()` at startup → use `getAppContext()` to read the context snapshot received at init. Showcase and other app data are available under `getAppContext().appContext` (e.g. `appContext.showcase`).
@@ -1,7 +0,0 @@
1
- export declare class ExtensionConnectionTimeoutError extends Error {
2
- constructor(timeoutMs: number);
3
- }
4
- export declare class HostConnectionTimeoutError extends Error {
5
- constructor(timeoutMs: number);
6
- }
7
- export declare function withConnectionTimeout<T>(promise: Promise<T>, timeoutMs: number, createError: () => Error): Promise<T>;
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withConnectionTimeout = exports.HostConnectionTimeoutError = exports.ExtensionConnectionTimeoutError = void 0;
4
- class ExtensionConnectionTimeoutError extends Error {
5
- constructor(timeoutMs) {
6
- super(`Extension connection to host timed out after ${timeoutMs}ms. Ensure the host calls registerHostWithAppExtension after the extension iframe has loaded.`);
7
- this.name = 'ExtensionConnectionTimeoutError';
8
- Object.setPrototypeOf(this, ExtensionConnectionTimeoutError.prototype);
9
- }
10
- }
11
- exports.ExtensionConnectionTimeoutError = ExtensionConnectionTimeoutError;
12
- class HostConnectionTimeoutError extends Error {
13
- constructor(timeoutMs) {
14
- super(`Host connection to extension timed out after ${timeoutMs}ms. Ensure the extension calls registerAppExtension() on load and the iframe has loaded.`);
15
- this.name = 'HostConnectionTimeoutError';
16
- Object.setPrototypeOf(this, HostConnectionTimeoutError.prototype);
17
- }
18
- }
19
- exports.HostConnectionTimeoutError = HostConnectionTimeoutError;
20
- function withConnectionTimeout(promise, timeoutMs, createError) {
21
- return new Promise((resolve, reject) => {
22
- const timer = setTimeout(() => reject(createError()), timeoutMs);
23
- promise
24
- .then((value) => {
25
- clearTimeout(timer);
26
- resolve(value);
27
- })
28
- .catch((err) => {
29
- clearTimeout(timer);
30
- reject(err);
31
- });
32
- });
33
- }
34
- exports.withConnectionTimeout = withConnectionTimeout;