@forge/bridge

5.15.2-next.0-experimental-5b726e65.16.0-next.1
~

Modified (49 files)

Index: package/out/view/close.js
===================================================================
--- package/out/view/close.js
+++ package/out/view/close.js
@@ -6,8 +6,9 @@
 const callBridge = (0, bridge_1.getCallBridge)();
 const close = async (payload) => {
     try {
         const success = await callBridge('close', payload);
+        // TODO: Modify @atlassian/forge-ui to throw if close fails instead of returning boolean DEVO-680
         if (success === false) {
             throw new errors_1.BridgeAPIError("this resource's view is not closable.");
         }
     }
Index: package/out/object-store/deleteObjects.js
===================================================================
--- package/out/object-store/deleteObjects.js
+++ package/out/object-store/deleteObjects.js
@@ -5,8 +5,16 @@
 const errors_1 = require("../errors");
 const utils_1 = require("./utils");
 const bridge_1 = require("../bridge");
 const callBridge = (0, bridge_1.getCallBridge)();
+/**
+ * Delete multiple objects
+ *
+ * @param functionKey - Configuration object containing the backend function key to delete objects
+ * @param keys - Array of object keys to delete
+ * @returns Promise<void>
+ * @throws {BridgeAPIError} When deleting fails
+ */
 const deleteObjects = async ({ functionKey, keys }) => {
     await (0, utils_1.checkRestrictedEnvironment)();
     void callBridge('trackObjectStoreAction', { action: 'delete' });
     if (!functionKey || functionKey.length === 0) {
Index: package/out/object-store/download.js
===================================================================
--- package/out/object-store/download.js
+++ package/out/object-store/download.js
@@ -5,8 +5,16 @@
 const errors_1 = require("../errors");
 const utils_1 = require("./utils");
 const bridge_1 = require("../bridge");
 const callBridge = (0, bridge_1.getCallBridge)();
+/**
+ * Download multiple objects from pre-signed URLs
+ *
+ * @param functionKey - Configuration object containing the backend function key to filter and generate download URLs
+ * @param keys - Array of object keys to download
+ * @returns Promise resolving to DownloadResult[]
+ * @throws {BridgeAPIError} When filtering fails or download encounters an error
+ */
 const download = async ({ functionKey, keys }) => {
     await (0, utils_1.checkRestrictedEnvironment)();
     void callBridge('trackObjectStoreAction', { action: 'download' });
     if (!functionKey || functionKey.length === 0) {
Index: package/out/view/emitReadyEvent.js
===================================================================
--- package/out/view/emitReadyEvent.js
+++ package/out/view/emitReadyEvent.js
@@ -6,15 +6,20 @@
 const bridge_1 = require("../bridge");
 const callBridge = (0, bridge_1.getCallBridge)();
 const EXTENSION_READY = 'EXTENSION_READY';
 const emitReadyEvent = async () => {
+    // Confluence export relies on events instead of `callBridge`
     const context = await view_1.view.getContext();
+    // dispatches event 'forge.bridge.EXTENSION_READY' on product window
     await events_1.events.emit(EXTENSION_READY, {
         localId: context.localId
     });
+    // TODO: Consider using the above event for static macros as well for to avoid two calls
+    // `callBridge` is used for static macros in XEP to signal that the forgeDoc is ready to be snapshotted
     try {
         await callBridge('emitReadyEvent');
     }
     catch {
+        // Silently ignore the error as this app may be calling this method in Confluence view mode where the method is not supported.
     }
 };
 exports.emitReadyEvent = emitReadyEvent;
Index: package/out/featureFlags/evaluator.js
===================================================================
--- package/out/featureFlags/evaluator.js
+++ package/out/featureFlags/evaluator.js
@@ -6,48 +6,57 @@
         this.results = results;
     }
     checkFlag(flagName, defaultValue) {
         if (!this.results || !this.results.feature_flags) {
+            // fallback to default value
             return defaultValue;
         }
         const featureFlags = this.results.feature_flags;
         let hashedValue = '';
         try {
             hashedValue = this.getHashedValue(flagName);
         }
         catch (err) {
+            // eslint-disable-next-line no-console
             console.error('Unexpected error occurred while evaluating flag ', err);
+            // If hashing fails for any reason, do not throw; safely return default
             return defaultValue;
         }
         if (!hashedValue) {
             return defaultValue;
         }
         const evaluatedFlag = featureFlags[hashedValue];
         if (evaluatedFlag) {
+            // if flag being checked is disabled
             if (evaluatedFlag.disabled) {
                 return false;
             }
             return evaluatedFlag.value;
         }
+        // fallback to default value
         return defaultValue;
     }
     shutDown() {
         this.results = undefined;
     }
+    // To be Updated with proper hashing function
     getHashedValue(flagName) {
+        // Defensive: ensure stable, non-throwing behavior for unexpected inputs
         if (typeof flagName !== 'string') {
             return '';
         }
         const input = flagName.trim();
         if (input.length === 0) {
             return '';
         }
+        // djb2 hashing (unsigned 32-bit), matches server-side encoding
         let hash = 5381;
         for (let i = 0; i < input.length; i += 1) {
             const charCode = input.charCodeAt(i);
-            hash = (hash << 5) + hash + charCode;
-            hash |= 0;
+            hash = (hash << 5) + hash + charCode; // hash * 33 + c
+            hash |= 0; // force 32-bit signed int
         }
+        // Convert to unsigned 32-bit and return as a decimal string
         return (hash >>> 0).toString();
     }
 }
 exports.Evaluator = Evaluator;
Index: package/out/featureFlags/featureFlags.js
===================================================================
--- package/out/featureFlags/featureFlags.js
+++ package/out/featureFlags/featureFlags.js
@@ -9,8 +9,11 @@
     constructor() {
         this.initialized = false;
         this.eventProps = {};
     }
+    /**
+     * Initialize the feature flags client
+     */
     async initialize(user, config = { environment: 'development' }) {
         if (this.isInitialized()) {
             return;
         }
@@ -21,16 +24,22 @@
         const result = await (0, initFeatureFlags_1.initFeatureFlags)({ user, config });
         this.initialized = true;
         this.evaluator = new evaluator_1.Evaluator(result);
     }
+    /**
+     * Check if a feature flag is enabled for the user
+     */
     checkFlag(flagName, defaultValue = false) {
         if (!this.isInitialized() || !this.evaluator) {
             this.sendCheckFlagEvent(flagName, false);
             throw new Error('FeatureFlags not initialized. Call initialize() first.');
         }
         this.sendCheckFlagEvent(flagName, true);
         return this.evaluator.checkFlag(flagName, defaultValue);
     }
+    /**
+     * Shutdown the feature flags client
+     */
     shutdown() {
         if (!this.isInitialized()) {
             return;
         }
Index: package/out/fetch/fetch.js
===================================================================
--- package/out/fetch/fetch.js
+++ package/out/fetch/fetch.js
@@ -4,13 +4,20 @@
 const blobParser_1 = require("../utils/blobParser");
 const parseFormData = async (form, matchFilePrefix = false) => {
     const parsed = {};
     for (const [key, value] of form.entries()) {
+        // Jira and Confluence REST APIs requires that the field containing
+        // the file MUST be named exactly 'file'
         const isFileKey = matchFilePrefix ? key.startsWith('file') : key === 'file';
         if (isFileKey) {
             const fileName = value.name;
             const fileType = value.type;
+            // we parse the file as a base64 string so it
+            // can be properly transfered over the bridge.
+            // we'll parse it back to Blob in the host product
             parsed[key] = await (0, blobParser_1.blobToBase64)(value);
+            // we also need to pass the file name and type,
+            // as those will be lost in the base64 conversion
             parsed[`__${key}Name`] = fileName;
             parsed[`__${key}Type`] = fileType;
         }
         else {
@@ -23,9 +30,11 @@
     if (!init) {
         return init;
     }
     if ('signal' in init) {
+        // eslint-disable-next-line @typescript-eslint/no-unused-vars
         const { signal: _signal, ...rest } = init;
+        // eslint-disable-next-line no-console
         console.error('Signal is not supported in @forge/bridge and was removed from fetch options. Please use the fetch method from @forge/api for signal support.');
         return rest;
     }
     return init;
@@ -33,8 +42,10 @@
 const parseRequest = async (fetchType, init) => {
     const isFormData = (init === null || init === void 0 ? void 0 : init.body) instanceof FormData ? true : false;
     const requestBody = isFormData ? await parseFormData(init === null || init === void 0 ? void 0 : init.body, fetchType === 'remote') : init === null || init === void 0 ? void 0 : init.body;
     const req = new Request('', { body: requestBody, method: init === null || init === void 0 ? void 0 : init.method, headers: init === null || init === void 0 ? void 0 : init.headers });
+    // If `init.body` is a `FormData`, a `content-type` header required for the body to be parsed by the
+    // API receiving this request will be generated in req.headers.
     const headers = Object.fromEntries(req.headers.entries());
     const body = req.method !== 'GET' ? await req.text() : null;
     return {
         body,
@@ -63,8 +74,9 @@
 const productFetchApi = (callBridge) => {
     const fetch = async (product, restPath, init) => {
         const validatedInit = validateFetchOptions(init);
         const { body: requestBody, headers: requestHeaders, isMultipartFormData } = await parseRequest('product', validatedInit);
+        // https://ecosystem.atlassian.net/browse/BUILD-1118 Skip xsrf check for oauth request
         if (!requestHeaders.has('X-Atlassian-Token')) {
             requestHeaders.set('X-Atlassian-Token', 'no-check');
         }
         const fetchPayload = {
Index: package/out/view/getContext.js
===================================================================
--- package/out/view/getContext.js
+++ package/out/view/getContext.js
@@ -8,8 +8,10 @@
     var _a;
     const context = await callBridge('getContext');
     const locale = context === null || context === void 0 ? void 0 : context.locale;
     if (locale) {
+        // still fallback to the locale if it is not supported,
+        // this is to avoid breaking changes
         context.locale = (_a = (0, i18n_1.ensureLocale)(locale)) !== null && _a !== void 0 ? _a : locale;
     }
     return context;
 };
Index: package/out/object-store/getMetadata.js
===================================================================
--- package/out/object-store/getMetadata.js
+++ package/out/object-store/getMetadata.js
@@ -5,8 +5,16 @@
 const errors_1 = require("../errors");
 const utils_1 = require("./utils");
 const bridge_1 = require("../bridge");
 const callBridge = (0, bridge_1.getCallBridge)();
+/**
+ * Get metadata for multiple objectIds
+ *
+ * @param functionKey - Configuration object containing the backend function key to filter and generate object metadata
+ * @param keys - Array of object keys to get metadata for
+ * @returns Promise resolving to GetMetadataResult[]
+ * @throws {BridgeAPIError} When filtering fails or download encounters an error
+ */
 const getMetadata = async ({ functionKey, keys }) => {
     await (0, utils_1.checkRestrictedEnvironment)();
     void callBridge('trackObjectStoreAction', { action: 'getMetadata' });
     if (!functionKey || functionKey.length === 0) {
Index: package/out/i18n/index.js
===================================================================
--- package/out/i18n/index.js
+++ package/out/i18n/index.js
@@ -35,8 +35,15 @@
     }
     return await translationsGetter.getTranslations(targetLocale, options);
 };
 exports.getTranslations = getTranslations;
+/**
+ * Creates a translation function (i.e. `t`) for the given locale.
+ * If no locale is provided, the locale from the current view context is used.
+ *
+ * @param locale The locale to create the translation function for.
+ * @returns The translation function.
+ */
 const createTranslationFunction = async (locale = null) => {
     let targetLocale = locale;
     if (!targetLocale) {
         const context = await view_1.view.getContext();
Index: package/out/invoke/invoke.js
===================================================================
--- package/out/invoke/invoke.js
+++ package/out/invoke/invoke.js
@@ -18,9 +18,21 @@
     }
     validatePayload(payload);
     return callBridge('invoke', { functionKey, payload });
 };
-exports.invoke = (0, utils_1.withRateLimiter)(_invoke, 500, 1000 * 25, 'Resolver calls are rate limited at 500req/25s');
+const limitedInvoke = (0, utils_1.withRateLimiter)(_invoke, 500, 1000 * 25, 'Resolver calls are rate limited at 500req/25s');
+/**
+ * Calls a backend resolver function by key.
+ */
+function invoke(functionKey, payload) {
+    return limitedInvoke(functionKey, payload);
+}
+exports.invoke = invoke;
+/**
+ * Specialises the invoke function to a given Definitions type.
+ *
+ * @returns An invoke function that can be used to call backend functions.
+ */
 function makeInvoke() {
-    return exports.invoke;
+    return invoke;
 }
 exports.makeInvoke = makeInvoke;
Index: package/out/rovo/isEnabled.js
===================================================================
--- package/out/rovo/isEnabled.js
+++ package/out/rovo/isEnabled.js
@@ -2,8 +2,13 @@
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.isEnabled = void 0;
 const bridge_1 = require("../bridge");
 const callBridge = (0, bridge_1.getCallBridge)();
+/**
+ * Checks if Rovo is enabled for the current tenant.
+ *
+ * @returns boolean
+ */
 const isEnabled = () => {
     return callBridge('isRovoEnabled');
 };
 exports.isEnabled = isEnabled;
Index: package/out/modal/modal.js
===================================================================
--- package/out/modal/modal.js
+++ package/out/modal/modal.js
@@ -3,8 +3,9 @@
 exports.Modal = void 0;
 const bridge_1 = require("../bridge");
 const errors_1 = require("../errors");
 const callBridge = (0, bridge_1.getCallBridge)();
+// eslint-disable-next-line @typescript-eslint/no-empty-function
 const noop = () => { };
 class Modal {
     constructor(opts) {
         var _a, _b;
Index: package/out/rovo/open.js
===================================================================
--- package/out/rovo/open.js
+++ package/out/rovo/open.js
@@ -22,8 +22,17 @@
         default:
             return { prompt: openRovoPayload.prompt };
     }
 };
+/**
+ * Opens Rovo chat from the given Rovo agent name.
+ *
+ * @param {OpenRovoPayload} [openRovoPayload] - Payload to open chat.
+ *   Can be one of:
+ *     - ForgeAgentPayload: { agentName: string, agentKey: string, prompt?: string }
+ *     - AtlassianAgentPayload: { agentName: string, prompt?: string }
+ *     - DefaultAgentPayload: { prompt?: string }
+ */
 const open = async (openRovoPayload) => {
     if (openRovoPayload.type === 'forge') {
         if (openRovoPayload.agentName.length > MAX_AGENT_LENGTH) {
             throw new Error('rovo agent name too long');
Index: package/out/view/open.js
===================================================================
--- package/out/view/open.js
+++ package/out/view/open.js
@@ -6,8 +6,9 @@
 const callBridge = (0, bridge_1.getCallBridge)();
 const open = async () => {
     try {
         const success = await callBridge('open');
+        // TODO: Modify @atlassian/forge-ui to throw if close fails instead of returning boolean DEVO-680
         if (success === false) {
             throw new errors_1.BridgeAPIError("this resource's view is not openable.");
         }
     }
Index: package/out/permissions/permissionsUtil.js
===================================================================
--- package/out/permissions/permissionsUtil.js
+++ package/out/permissions/permissionsUtil.js
@@ -2,8 +2,12 @@
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.checkPermissions = exports.createPermissionUtils = void 0;
 const egress_1 = require("@forge/egress");
 const view_1 = require("../view");
+/**
+ * Helper function to extract URL string from external URL permissions.
+ * Matches the implementation in @forge/api for consistency.
+ */
 function extractUrlString(url) {
     if (typeof url === 'string') {
         return url;
     }
@@ -11,10 +15,21 @@
         return url.address;
     }
     return url.remote || '';
 }
+/**
+ * Resource types that can be loaded externally
+ */
 const RESOURCE_TYPES = ['fonts', 'styles', 'frames', 'images', 'media', 'scripts'];
+/**
+ * Fetch types for external requests
+ */
 const FETCH_TYPES = ['backend', 'client'];
+/**
+ * Create permission utilities from runtime permissions
+ * @param runtimePermissions - The runtime permissions from context
+ * @returns Permission utilities or null if permissions are not available
+ */
 function createPermissionUtils(runtimePermissions) {
     if (!runtimePermissions) {
         return null;
     }
@@ -26,23 +41,29 @@
             var _a;
             const fetchUrls = (_a = external.fetch) === null || _a === void 0 ? void 0 : _a[type];
             if (!(fetchUrls === null || fetchUrls === void 0 ? void 0 : fetchUrls.length))
                 return false;
+            // Extract URLs and create egress filter
             const allowList = fetchUrls.map(extractUrlString).filter((u) => u.length > 0);
             if (allowList.length === 0)
                 return false;
             const egressFilter = new egress_1.EgressFilteringService(allowList);
+            // Backend: hostname-only matching, Client: CSP validation (includes paths)
+            // Type assertion needed because node_modules may have outdated types
             const egressFilterWithCSP = egressFilter;
             return type === 'client' ? egressFilterWithCSP.isValidUrlCSP(url) : egressFilter.isValidUrl(url);
         },
         canLoadResource: (type, url) => {
             const resourceUrls = external[type];
             if (!(resourceUrls === null || resourceUrls === void 0 ? void 0 : resourceUrls.length))
                 return false;
+            // Extract URLs and create egress filter
             const allowList = resourceUrls.map(extractUrlString).filter((u) => u.length > 0);
             if (allowList.length === 0)
                 return false;
             const egressFilter = new egress_1.EgressFilteringService(allowList);
+            // All resources use CSP validation (checks protocol + hostname + paths)
+            // Type assertion needed because node_modules may have outdated types
             const egressFilterWithCSP = egressFilter;
             return egressFilterWithCSP.isValidUrlCSP(url);
         },
         getScopes: () => scopeArray,
@@ -50,15 +71,21 @@
         hasAnyPermissions: () => scopeArray.length > 0 || Object.keys(external).length > 0
     };
 }
 exports.createPermissionUtils = createPermissionUtils;
+/**
+ * Check if required scopes are granted
+ */
 function checkScopes(requiredScopes, permissionUtils) {
     if (!(requiredScopes === null || requiredScopes === void 0 ? void 0 : requiredScopes.length)) {
         return undefined;
     }
     const missingScopes = requiredScopes.filter((scope) => !permissionUtils.hasScope(scope));
     return missingScopes.length > 0 ? missingScopes : undefined;
 }
+/**
+ * Check if required fetch permissions are granted
+ */
 function checkFetchPermissions(requiredFetch, permissionUtils) {
     if (!(requiredFetch === null || requiredFetch === void 0 ? void 0 : requiredFetch.fetch)) {
         return undefined;
     }
@@ -74,8 +101,11 @@
         }
     });
     return Object.keys(missingFetch).length > 0 ? missingFetch : undefined;
 }
+/**
+ * Check if required resource permissions are granted
+ */
 function checkResourcePermissions(requiredExternal, permissionUtils) {
     const missingResources = {};
     RESOURCE_TYPES.forEach((type) => {
         const requiredUrls = requiredExternal === null || requiredExternal === void 0 ? void 0 : requiredExternal[type];
@@ -87,8 +117,11 @@
         }
     });
     return Object.keys(missingResources).length > 0 ? missingResources : undefined;
 }
+/**
+ * Check if required external permissions are granted
+ */
 function checkExternalPermissions(requiredExternal, permissionUtils) {
     if (!requiredExternal) {
         return undefined;
     }
@@ -105,20 +138,26 @@
         Object.assign(missingExternal, missingResources);
     }
     return missingExternal;
 }
+// Throws if a field that should be a plain object but is something else.
+// Only used for external and external.fetch, which are guarded above.
 function validateObjectField(value, fieldPath) {
     if (value !== undefined) {
         if (typeof value !== 'object' || value === null || Array.isArray(value)) {
             throw new TypeError(`${fieldPath} should be an object, not ${Array.isArray(value) ? 'an array' : `a ${typeof value}`}`);
         }
     }
 }
+// Throws if a field that should be an array but is something else.
 function validateArrayField(value, fieldPath) {
     if (value !== undefined && !Array.isArray(value)) {
         throw new TypeError(`${fieldPath} should be an array, not a ${typeof value}`);
     }
 }
+/**
+ * Validates that fields in requiredPermissions have the correct types.
+ */
 function validatePermissionShape(requiredPermissions) {
     validateArrayField(requiredPermissions.scopes, 'scopes');
     const external = requiredPermissions.external;
     if (external === undefined)
@@ -133,38 +172,51 @@
     for (const type of RESOURCE_TYPES) {
         validateArrayField(external[type], `external.${type}`);
     }
 }
+/**
+ * Check if required permissions are granted
+ * @param requiredPermissions - The permissions required to check
+ * @param runtimePermissions - The runtime permissions from context (optional, will be fetched from view.getContext() if not provided)
+ * @returns Promise resolving to permission check result with granted status and missing permissions
+ */
 async function checkPermissions(requiredPermissions, runtimePermissions) {
     var _a;
+    // Early return for falsy required permissions
     if (!requiredPermissions) {
         return { granted: false, missing: null };
     }
     validatePermissionShape(requiredPermissions);
+    // Early return for empty permissions (no requirements = all granted)
     if (!((_a = requiredPermissions.scopes) === null || _a === void 0 ? void 0 : _a.length) && !requiredPermissions.external) {
         return { granted: true, missing: null };
     }
+    // If runtimePermissions is not provided, fetch it from context
     let permissionsToCheck = runtimePermissions;
     if (!permissionsToCheck) {
         const context = await view_1.view.getContext();
         permissionsToCheck = context.permissions;
     }
     const permissionUtils = createPermissionUtils(permissionsToCheck);
     if (!permissionUtils) {
+        // If permissions are not available, return not granted
         return { granted: false, missing: null };
     }
     const missing = {};
     let hasAllRequiredPermissions = true;
+    // Check scopes
     const missingScopes = checkScopes(requiredPermissions.scopes, permissionUtils);
     if (missingScopes) {
         missing.scopes = missingScopes;
         hasAllRequiredPermissions = false;
     }
+    // Check external permissions
     const missingExternal = checkExternalPermissions(requiredPermissions.external, permissionUtils);
     if (missingExternal) {
         missing.external = missingExternal;
         hasAllRequiredPermissions = false;
     }
+    // Note: Content permissions are not supported in the current RuntimePermissions type
     return {
         granted: hasAllRequiredPermissions,
         missing: hasAllRequiredPermissions ? null : missing
     };
Index: package/out/realtime/productContext.js
===================================================================
--- package/out/realtime/productContext.js
+++ package/out/realtime/productContext.js
@@ -1,5 +1,7 @@
 "use strict";
+// These enum values directly map to properties in the context.extension object in a product's FCT.
+// It allows a subset of properties to be specified for Realtime channel authorization.
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.Bitbucket = exports.Confluence = exports.Jira = void 0;
 var Jira;
 (function (Jira) {
Index: package/out/router/router.js
===================================================================
--- package/out/router/router.js
+++ package/out/router/router.js
@@ -2,8 +2,14 @@
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.router = void 0;
 const bridge_1 = require("../bridge");
 const callBridge = (0, bridge_1.getCallBridge)();
+/**
+ * Returns a URL object for a given location.
+ *
+ * @param location - The location to get the URL for
+ * @returns A URL object for the given location
+ */
 const getUrl = async (location) => {
     if (!(location === null || location === void 0 ? void 0 : location.target)) {
         throw new Error('target is required for getUrl');
     }
@@ -17,8 +23,14 @@
     catch (error) {
         throw new Error(`Failed to parse URL: ${url} (${error})`);
     }
 };
+/**
+ * Navigates to a location within the same tab.
+ *
+ * @param location URL or `NavigationLocation` object
+ * @returns a promise that resolves when navigation is complete
+ */
 const navigate = (location) => {
     if (typeof location === 'string') {
         return callBridge('navigate', { url: location, type: 'same-tab' });
     }
@@ -28,8 +40,14 @@
         }
         return callBridge('navigate', { ...location, type: 'same-tab' });
     }
 };
+/**
+ * Opens a new tab in the browser and navigates to the specified location.
+ *
+ * @param location URL or `NavigationLocation` object
+ * @returns a promise that resolves when navigation is complete
+ */
 const open = (location) => {
     if (typeof location === 'string') {
         return callBridge('navigate', { url: location, type: 'new-tab' });
     }
@@ -39,8 +57,13 @@
         }
         return callBridge('navigate', { ...location, type: 'new-tab' });
     }
 };
+/**
+ * Reloads the page.
+ *
+ * @returns a promise that resolves to void when the page is reloaded
+ */
 const reload = async () => callBridge('reload');
 exports.router = {
     getUrl,
     navigate,
Index: package/out/events/serialiseBlob.js
===================================================================
--- package/out/events/serialiseBlob.js
+++ package/out/events/serialiseBlob.js
@@ -1,8 +1,9 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.containsSerialisedBlobs = exports.containsBlobs = exports.deserialiseBlobsInPayload = exports.serialiseBlobsInPayload = void 0;
 const blobParser_1 = require("../utils/blobParser");
+// Copied from https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore?tab=readme-ov-file#_isplainobject
 const isPlainObject = (value) => {
     if (typeof value !== 'object' || value === null)
         return false;
     if (Object.prototype.toString.call(value) !== '[object Object]')
@@ -22,40 +23,50 @@
         type: blob.type
     };
 };
 const base64WithMetadataToBlob = (metadata) => {
+    // typecasting as Blob since base64ToBlob returns Blob | null but this is only null if data is undefined which it won't be here
     return (0, blobParser_1.base64ToBlob)(metadata.data, metadata.type);
 };
+// Recursively converts all Blobs in an object or array to base64 with metadata
 const serialiseBlobsInPayload = async (payload) => {
     if (payload instanceof Blob) {
+        // Convert blob to base64 with metadata and mark it as a serialized blob
         const blobData = await blobToBase64WithMetadata(payload);
         return {
             ...blobData,
+            // Custom property used to identify serialized blob data for deserialization
             __isBlobData: true
         };
     }
     if (Array.isArray(payload)) {
+        // Recursively process array elements
         return Promise.all(payload.map((item) => (0, exports.serialiseBlobsInPayload)(item)));
     }
     if (payload && isPlainObject(payload)) {
+        // Recursively process object properties
         const entries = await Promise.all(Object.entries(payload).map(async ([key, value]) => [key, await (0, exports.serialiseBlobsInPayload)(value)]));
         return Object.fromEntries(entries);
     }
     return payload;
 };
 exports.serialiseBlobsInPayload = serialiseBlobsInPayload;
+// Recursively converts all serialized blob data back to Blobs in an object or array
 const deserialiseBlobsInPayload = (payload) => {
     if (payload && isPlainObject(payload) && '__isBlobData' in payload) {
         const typedData = payload;
+        // Convert serialized blob data back to Blob
         return base64WithMetadataToBlob({
             data: typedData.data,
             type: typedData.type
         });
     }
     if (Array.isArray(payload)) {
+        // Recursively process array elements
         return payload.map((item) => (0, exports.deserialiseBlobsInPayload)(item));
     }
     if (payload && isPlainObject(payload)) {
+        // Recursively process object properties
         const result = {};
         for (const [key, value] of Object.entries(payload)) {
             result[key] = (0, exports.deserialiseBlobsInPayload)(value);
         }
Index: package/out/router/targets.js
===================================================================
--- package/out/router/targets.js
+++ package/out/router/targets.js
@@ -1,14 +1,42 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.NavigationTarget = void 0;
+// Constants for developer convenience. The JS Docs below will also be displayed in the IDE when the developer is using this object.
 exports.NavigationTarget = {
+    /**
+     * The view page for pages, blogs and custom content. Takes a contentId to identify the content.
+     */
     ContentView: 'contentView',
+    /**
+     * The edit page for pages, blogs and custom content. Takes a contentId to identify the content.
+     */
     ContentEdit: 'contentEdit',
+    /**
+     * The list/collector page for pages, blogs and custom content contained in a space. Takes a spaceKey and a contentType to identify the content type for pages and blogs. Takes a moduleKey for custom content.
+     */
     ContentList: 'contentList',
+    /**
+     * The space view page. Takes a spaceKey to identify the space.
+     */
     SpaceView: 'spaceView',
+    /**
+     * The page within a specific module. Takes a moduleKey to identify the correct module.
+     */
     Module: 'module',
+    /**
+     * The profile page for a specific user. Takes an accountId to identify the user.
+     */
     UserProfile: 'userProfile',
+    /**
+     * A dashboard in Jira. Takes a dashboardId to identify the dashboard.
+     */
     Dashboard: 'dashboard',
+    /**
+     * An issue in Jira. Takes an issueKey to identify the issue.
+     */
     Issue: 'issue',
+    /**
+     * The project settings details of a Jira project. Takes a projectKey to identify the project. Only accessible to administrators.
+     */
     ProjectSettingsDetails: 'projectSettingsDetails'
 };
Index: package/out/object-store/upload.js
===================================================================
--- package/out/object-store/upload.js
+++ package/out/object-store/upload.js
@@ -5,8 +5,11 @@
 const errors_1 = require("../errors");
 const utils_1 = require("./utils");
 const bridge_1 = require("../bridge");
 const callBridge = (0, bridge_1.getCallBridge)();
+/**
+ * Convert base64 string to Blob
+ */
 const base64ToBlob = (base64, mimeType) => {
     const byteCharacters = atob(base64);
     const byteNumbers = new Array(byteCharacters.length);
     for (let i = 0; i < byteCharacters.length; i++) {
@@ -14,28 +17,43 @@
     }
     const byteArray = new Uint8Array(byteNumbers);
     return new Blob([byteArray], { type: mimeType || 'application/octet-stream' });
 };
+/**
+ * Get metadata for a blob object including size and checksum
+ */
 const getObjectMetadata = async (blob) => {
     const length = blob.size;
+    // Calculate checksum using SubtleCrypto API
     const arrayBuffer = await blob.arrayBuffer();
     const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
     const hashArray = new Uint8Array(hashBuffer);
+    // Convert to base64 format (required by OS API)
     const checksum = btoa(String.fromCharCode(...hashArray));
     const checksumType = 'SHA256';
     return {
         length,
         checksum,
         checksumType
     };
 };
+/**
+ * Start individual upload operations and return array of promises
+ * This allows tracking per-file upload progress
+ *
+ * @param functionKey - Configuration object containing the backend function key to filter and generate presigned URLs
+ * @param objects - Array of Blob objects or Base64Object (with data and optional mimeType) to upload
+ * @returns Array of individual upload promises with their associated index
+ * @throws {BridgeAPIError} When filtering fails or upload encounters an error
+ */
 const createUploadPromises = async ({ functionKey, objects }) => {
     if (!functionKey || functionKey.length === 0) {
         throw new errors_1.BridgeAPIError('functionKey is required to filter and generate presigned URLs');
     }
     if (!Array.isArray(objects) || objects.length === 0) {
         throw new errors_1.BridgeAPIError('objects array is required and must not be empty');
     }
+    // Validate and convert objects to Blobs in a single pass
     const blobs = objects.map((obj, index) => {
         if (obj instanceof Blob) {
             return obj;
         }
@@ -118,8 +136,16 @@
     });
     return uploadPromises;
 };
 exports.createUploadPromises = createUploadPromises;
+/**
+ * Upload multiple objects to pre-signed URLs
+ *
+ * @param functionKey - Configuration object containing the backend function key to filter and generate presigned URLs
+ * @param objects - Array of Blob objects or Base64Object (with data and optional mimeType) to upload
+ * @returns Promise resolving to UploadResult[]
+ * @throws {BridgeAPIError} When filtering fails or upload encounters an error
+ */
 const upload = async ({ functionKey, objects }) => {
     await (0, utils_1.checkRestrictedEnvironment)();
     void callBridge('trackObjectStoreAction', { action: 'upload' });
     const uploadPromises = await (0, exports.createUploadPromises)({ functionKey, objects });
Index: package/package.json
===================================================================
--- package/package.json
+++ package/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@forge/bridge",
-  "version": "5.15.2-next.0-experimental-5b726e6",
+  "version": "5.16.0-next.1",
   "description": "Forge bridge API for custom UI apps",
   "author": "Atlassian",
   "license": "SEE LICENSE IN LICENSE.txt",
   "main": "out/index.js",
@@ -16,11 +16,11 @@
     "@atlaskit/adf-schema": "^48.0.0",
     "@atlaskit/tokens": "^1.58.0",
     "@forge/egress": "^2.3.2",
     "@forge/i18n": "0.0.7",
-    "@forge/resolver": "1.7.1",
+    "@forge/resolver": "1.8.0-next.0",
     "@types/history": "^4.7.11",
-    "@forge/manifest": "12.6.0-next.0-experimental-5b726e6",
+    "@forge/manifest": "12.6.0-next.0",
     "@types/iframe-resizer": "^3.5.8",
     "iframe-resizer": "^4.4.5",
     "uuid": "^9.0.1"
   },
Index: package/out/object-store/deleteObjects.d.ts.map
===================================================================
--- package/out/object-store/deleteObjects.d.ts.map
+++ package/out/object-store/deleteObjects.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"deleteObjects.d.ts","sourceRoot":"","sources":["../../src/object-store/deleteObjects.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,aAAa;iBAAgD,MAAM;UAAQ,MAAM,EAAE;MAAK,QAAQ,IAAI,CAkBhH,CAAC"}
\ No newline at end of file
+{"version":3,"file":"deleteObjects.d.ts","sourceRoot":"","sources":["../../src/object-store/deleteObjects.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;iBAAgD,MAAM;UAAQ,MAAM,EAAE;MAAK,QAAQ,IAAI,CAkBhH,CAAC"}
\ No newline at end of file
Index: package/out/object-store/download.d.ts.map
===================================================================
--- package/out/object-store/download.d.ts.map
+++ package/out/object-store/download.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/object-store/download.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,SAAS,CAAC;AAclE,eAAO,MAAM,QAAQ;iBAIN,MAAM;UACb,MAAM,EAAE;MACZ,QAAQ,cAAc,EAAE,CAyD3B,CAAC"}
\ No newline at end of file
+{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/object-store/download.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,SAAS,CAAC;AAMlE;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;iBAIN,MAAM;UACb,MAAM,EAAE;MACZ,QAAQ,cAAc,EAAE,CAyD3B,CAAC"}
\ No newline at end of file
Index: package/out/featureFlags/featureFlags.d.ts.map
===================================================================
--- package/out/featureFlags/featureFlags.d.ts.map
+++ package/out/featureFlags/featureFlags.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"featureFlags.d.ts","sourceRoot":"","sources":["../../src/featureFlags/featureFlags.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,eAAe,EACf,sBAAsB,EAEvB,MAAM,SAAS,CAAC;AAEjB,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,UAAU,CAAkC;IAKvC,UAAU,CACrB,IAAI,EAAE,eAAe,EACrB,MAAM,GAAE,sBAAuD,GAC9D,OAAO,CAAC,IAAI,CAAC;IAoBT,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,OAAO;IAa1D,QAAQ;IAQR,aAAa;IAIpB,OAAO,CAAC,kBAAkB;CAa3B"}
\ No newline at end of file
+{"version":3,"file":"featureFlags.d.ts","sourceRoot":"","sources":["../../src/featureFlags/featureFlags.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,eAAe,EACf,sBAAsB,EAEvB,MAAM,SAAS,CAAC;AAEjB,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,UAAU,CAAkC;IAEpD;;OAEG;IACU,UAAU,CACrB,IAAI,EAAE,eAAe,EACrB,MAAM,GAAE,sBAAuD,GAC9D,OAAO,CAAC,IAAI,CAAC;IAiBhB;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,OAAO;IAUjE;;OAEG;IACI,QAAQ;IAQR,aAAa;IAIpB,OAAO,CAAC,kBAAkB;CAa3B"}
\ No newline at end of file
Index: package/out/flag/flag.d.ts.map
===================================================================
--- package/out/flag/flag.d.ts.map
+++ package/out/flag/flag.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"flag.d.ts","sourceRoot":"","sources":["../../src/flag/flag.ts"],"names":[],"mappings":"AAGA,oBAAY,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AACtE,oBAAY,QAAQ,GAAG,cAAc,CAAC;AAEtC,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IAKrB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAIhB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAID,eAAO,MAAM,QAAQ,YAAa,WAAW,KAAG,IAgB/C,CAAC"}
\ No newline at end of file
+{"version":3,"file":"flag.d.ts","sourceRoot":"","sources":["../../src/flag/flag.ts"],"names":[],"mappings":"AAGA,oBAAY,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AACtE,oBAAY,QAAQ,GAAG,cAAc,CAAC;AAEtC,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;OAEG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAID,eAAO,MAAM,QAAQ,YAAa,WAAW,KAAG,IAgB/C,CAAC"}
\ No newline at end of file
Index: package/out/object-store/getMetadata.d.ts.map
===================================================================
--- package/out/object-store/getMetadata.d.ts.map
+++ package/out/object-store/getMetadata.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"getMetadata.d.ts","sourceRoot":"","sources":["../../src/object-store/getMetadata.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAcjD,eAAO,MAAM,WAAW;iBAIT,MAAM;UACb,MAAM,EAAE;MACZ,QAAQ,iBAAiB,EAAE,CA6B9B,CAAC"}
\ No newline at end of file
+{"version":3,"file":"getMetadata.d.ts","sourceRoot":"","sources":["../../src/object-store/getMetadata.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAMjD;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;iBAIT,MAAM;UACb,MAAM,EAAE;MACZ,QAAQ,iBAAiB,EAAE,CA6B9B,CAAC"}
\ No newline at end of file
Index: package/out/i18n/index.d.ts.map
===================================================================
--- package/out/i18n/index.d.ts.map
+++ package/out/i18n/index.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAM3B,MAAM,aAAa,CAAC;AAuBrB,eAAO,MAAM,sBAAsB,QAAO,IAEzC,CAAC;AAEF,eAAO,MAAM,eAAe,YAClB,wBAAwB,GAAG,IAAI,YAC9B,sBAAsB,KAG9B,QAAQ,qBAAqB,CAO/B,CAAC;AAUF,oBAAY,mBAAmB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AASrF,eAAO,MAAM,yBAAyB,YAC5B,wBAAwB,GAAG,IAAI,KACtC,QAAQ,mBAAmB,CAS7B,CAAC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAM3B,MAAM,aAAa,CAAC;AAuBrB,eAAO,MAAM,sBAAsB,QAAO,IAEzC,CAAC;AAEF,eAAO,MAAM,eAAe,YAClB,wBAAwB,GAAG,IAAI,YAC9B,sBAAsB,KAG9B,QAAQ,qBAAqB,CAO/B,CAAC;AAEF;;;;;;;GAOG;AACH,oBAAY,mBAAmB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAErF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,YAC5B,wBAAwB,GAAG,IAAI,KACtC,QAAQ,mBAAmB,CAS7B,CAAC"}
\ No newline at end of file
Index: package/out/invoke/invoke.d.ts.map
===================================================================
--- package/out/invoke/invoke.d.ts.map
+++ package/out/invoke/invoke.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../src/invoke/invoke.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAInF,OAAO,EAAE,aAAa,EAAkB,MAAM,UAAU,CAAC;AAuBzD,eAAO,MAAM,MAAM,6EAA4F,CAAC;AAEhH,aAAK,MAAM,CAAC,IAAI,SAAS,WAAW,IAAI,CAAC,GAAG,SAAS,MAAM,IAAI,GAAG,MAAM,EACtE,IAAI,EAAE,GAAG,EACT,GAAG,GAAG,EAAE,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,KAC5B,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAOnC,wBAAgB,UAAU,CAAC,IAAI,SAAS,WAAW,KAAK,MAAM,CAAC,IAAI,CAAC,CAEnE"}
\ No newline at end of file
+{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../src/invoke/invoke.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAInF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AA+BzD;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,OAAO,SAAS,aAAa,EAAE,CAAC,GAAG,cAAc,EACtE,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,CAAC,CAAC,CAAC;AAEd;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,CAAC,GAAG,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AASrG,aAAK,MAAM,CAAC,IAAI,SAAS,WAAW,IAAI,CAAC,GAAG,SAAS,MAAM,IAAI,GAAG,MAAM,EACtE,IAAI,EAAE,GAAG,EACT,GAAG,GAAG,EAAE,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,KAC5B,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,SAAS,WAAW,KAAK,MAAM,CAAC,IAAI,CAAC,CAEnE"}
\ No newline at end of file
Index: package/out/rovo/isEnabled.d.ts.map
===================================================================
--- package/out/rovo/isEnabled.d.ts.map
+++ package/out/rovo/isEnabled.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"isEnabled.d.ts","sourceRoot":"","sources":["../../src/rovo/isEnabled.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,SAAS,wBAErB,CAAC"}
\ No newline at end of file
+{"version":3,"file":"isEnabled.d.ts","sourceRoot":"","sources":["../../src/rovo/isEnabled.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,SAAS,wBAErB,CAAC"}
\ No newline at end of file
Index: package/out/rovo/open.d.ts.map
===================================================================
--- package/out/rovo/open.d.ts.map
+++ package/out/rovo/open.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/rovo/open.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAO1C,eAAO,MAAM,8BAA8B,uPAC2M,CAAC;AA4BvP,eAAO,MAAM,IAAI,oBAA2B,eAAe,kBAiB1D,CAAC"}
\ No newline at end of file
+{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/rovo/open.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAO1C,eAAO,MAAM,8BAA8B,uPAC2M,CAAC;AAmBvP;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI,oBAA2B,eAAe,kBAiB1D,CAAC"}
\ No newline at end of file
Index: package/out/permissions/permissionsUtil.d.ts.map
===================================================================
--- package/out/permissions/permissionsUtil.d.ts.map
+++ package/out/permissions/permissionsUtil.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"permissionsUtil.d.ts","sourceRoot":"","sources":["../../src/permissions/permissionsUtil.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAS,MAAM,iBAAiB,CAAC;AAEvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAoBnD,QAAA,MAAM,cAAc,sEAAuE,CAAC;AAC5F,oBAAY,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAK3D,QAAA,MAAM,WAAW,gCAAiC,CAAC;AACnD,oBAAY,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAKrD,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE;YACN,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;SACnB,CAAC;QACF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAKD,oBAAY,kBAAkB,GAAG,sBAAsB,CAAC;AAKxD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACpC;AAKD,MAAM,WAAW,eAAe;IAI9B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAOrC,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAOxD,eAAe,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAK9D,SAAS,EAAE,MAAM,MAAM,EAAE,CAAC;IAK1B,sBAAsB,EAAE,MAAM,QAAQ,GAAG,SAAS,CAAC;IAKnD,iBAAiB,EAAE,MAAM,OAAO,CAAC;CAClC;AAOD,wBAAgB,qBAAqB,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,GAAG,eAAe,GAAG,IAAI,CAmDhH;AA+ID,wBAAsB,gBAAgB,CACpC,mBAAmB,EAAE,sBAAsB,EAC3C,kBAAkB,CAAC,EAAE,kBAAkB,GAAG,SAAS,GAClD,OAAO,CAAC,qBAAqB,CAAC,CAiDhC"}
\ No newline at end of file
+{"version":3,"file":"permissionsUtil.d.ts","sourceRoot":"","sources":["../../src/permissions/permissionsUtil.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAS,MAAM,iBAAiB,CAAC;AAEvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAiBnD;;GAEG;AACH,QAAA,MAAM,cAAc,sEAAuE,CAAC;AAC5F,oBAAY,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;GAEG;AACH,QAAA,MAAM,WAAW,gCAAiC,CAAC;AACnD,oBAAY,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE;YACN,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;SACnB,CAAC;QACF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,oBAAY,kBAAkB,GAAG,sBAAsB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAErC;;;;OAIG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAExD;;;;OAIG;IACH,eAAe,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAE9D;;OAEG;IACH,SAAS,EAAE,MAAM,MAAM,EAAE,CAAC;IAE1B;;OAEG;IACH,sBAAsB,EAAE,MAAM,QAAQ,GAAG,SAAS,CAAC;IAEnD;;OAEG;IACH,iBAAiB,EAAE,MAAM,OAAO,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,GAAG,eAAe,GAAG,IAAI,CAmDhH;AAyID;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,mBAAmB,EAAE,sBAAsB,EAC3C,kBAAkB,CAAC,EAAE,kBAAkB,GAAG,SAAS,GAClD,OAAO,CAAC,qBAAqB,CAAC,CAiDhC"}
\ No newline at end of file
Index: package/out/router/targets.d.ts.map
===================================================================
--- package/out/router/targets.d.ts.map
+++ package/out/router/targets.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../../src/router/targets.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB;;;;;;;;;;CAqCnB,CAAC"}
\ No newline at end of file
+{"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../../src/router/targets.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB;IAC3B;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;CAEK,CAAC"}
\ No newline at end of file
Index: package/out/types.d.ts.map
===================================================================
--- package/out/types.d.ts.map
+++ package/out/types.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,oBAAY,aAAa,GAAG;KACzB,GAAG,IAAI,MAAM,GAAG,MAAM,GAAG,GAAG;CAC9B,CAAC;AAEF,oBAAY,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;AAExD,aAAK,aAAa,GAAG;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,YAAY,CAAC,CAAC;AAEhD,oBAAY,oBAAoB,GAAG,aAAa,CAAC;AACjD,oBAAY,mBAAmB,GAAG,aAAa,CAAC;AAEhD,oBAAY,oBAAoB;IAC9B,WAAW,gBAAgB;IAC3B,OAAO,YAAY;IACnB,UAAU,eAAe;CAC1B;AAED,aAAK,iBAAiB,GAAG;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,oBAAY,eAAe,GAAG,MAAM,OAAO,oBAAoB,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,aAAa,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,wBAAwB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AACD,UAAU,aAAa;IACrB,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CAClB;AAED,aAAK,oBAAoB;IACvB,WAAW,gBAAgB;CAC5B;AACD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;CACtC;AAED,oBAAY,YAAY,GAAG;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AAQF,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAGD,oBAAY,gBAAgB,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAE3D,oBAAY,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC"}
\ No newline at end of file
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,oBAAY,aAAa,GAAG;KACzB,GAAG,IAAI,MAAM,GAAG,MAAM,GAAG,GAAG;CAC9B,CAAC;AAEF,oBAAY,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;AAExD,aAAK,aAAa,GAAG;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,YAAY,CAAC,CAAC;AAEhD,oBAAY,oBAAoB,GAAG,aAAa,CAAC;AACjD,oBAAY,mBAAmB,GAAG,aAAa,CAAC;AAEhD,oBAAY,oBAAoB;IAC9B,WAAW,gBAAgB;IAC3B,OAAO,YAAY;IACnB,UAAU,eAAe;CAC1B;AAED,aAAK,iBAAiB,GAAG;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,oBAAY,eAAe,GAAG,MAAM,OAAO,oBAAoB,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,aAAa,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,wBAAwB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AACD,UAAU,aAAa;IACrB,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CAClB;AAED,aAAK,oBAAoB;IACvB,WAAW,gBAAgB;CAC5B;AACD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;CACtC;AAED,oBAAY,YAAY,GAAG;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAGD,oBAAY,gBAAgB,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAE3D,oBAAY,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC"}
\ No newline at end of file
Index: package/out/object-store/upload.d.ts.map
===================================================================
--- package/out/object-store/upload.d.ts.map
+++ package/out/object-store/upload.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/object-store/upload.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAuC,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAiDlH,eAAO,MAAM,oBAAoB;iBAIlB,MAAM;aACV,YAAY,EAAE;MACrB,QAAQ,iBAAiB,EAAE,CA0G9B,CAAC;AAUF,eAAO,MAAM,MAAM;iBAIJ,MAAM;aACV,YAAY,EAAE;MACrB,QAAQ,YAAY,EAAE,CASzB,CAAC"}
\ No newline at end of file
+{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/object-store/upload.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAuC,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAwClH;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB;iBAIlB,MAAM;aACV,YAAY,EAAE;MACrB,QAAQ,iBAAiB,EAAE,CA0G9B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM;iBAIJ,MAAM;aACV,YAAY,EAAE;MACrB,QAAQ,YAAY,EAAE,CASzB,CAAC"}
\ No newline at end of file
Index: package/CHANGELOG.md
===================================================================
--- package/CHANGELOG.md
+++ package/CHANGELOG.md
@@ -1,13 +1,16 @@
 # @forge/bridge
 
-## 5.15.2-next.0-experimental-5b726e6
+## 5.16.0-next.1
 
+### Minor Changes
+
+- fd8e87e: Add payload generic and jsdoc to invoke
+
 ### Patch Changes
 
-- Updated dependencies [5b726e6]
-- Updated dependencies [399ae7d]
-  - @forge/[email protected]
+- Updated dependencies [6b848cf]
+  - @forge/[email protected]
 
 ## 5.15.2-next.0
 
 ### Patch Changes
Index: package/out/object-store/deleteObjects.d.ts
===================================================================
--- package/out/object-store/deleteObjects.d.ts
+++ package/out/object-store/deleteObjects.d.ts
@@ -1,4 +1,12 @@
+/**
+ * Delete multiple objects
+ *
+ * @param functionKey - Configuration object containing the backend function key to delete objects
+ * @param keys - Array of object keys to delete
+ * @returns Promise<void>
+ * @throws {BridgeAPIError} When deleting fails
+ */
 export declare const deleteObjects: ({ functionKey, keys }: {
     functionKey: string;
     keys: string[];
 }) => Promise<void>;
Index: package/out/object-store/download.d.ts
===================================================================
--- package/out/object-store/download.d.ts
+++ package/out/object-store/download.d.ts
@@ -1,5 +1,13 @@
 import type { DownloadResult } from './types';
+/**
+ * Download multiple objects from pre-signed URLs
+ *
+ * @param functionKey - Configuration object containing the backend function key to filter and generate download URLs
+ * @param keys - Array of object keys to download
+ * @returns Promise resolving to DownloadResult[]
+ * @throws {BridgeAPIError} When filtering fails or download encounters an error
+ */
 export declare const download: ({ functionKey, keys }: {
     functionKey: string;
     keys: string[];
 }) => Promise<DownloadResult[]>;
Index: package/out/featureFlags/featureFlags.d.ts
===================================================================
--- package/out/featureFlags/featureFlags.d.ts
+++ package/out/featureFlags/featureFlags.d.ts
@@ -2,10 +2,19 @@
 export declare class FeatureFlags {
     private initialized;
     private evaluator;
     private eventProps;
+    /**
+     * Initialize the feature flags client
+     */
     initialize(user: FeatureFlagUser, config?: ForgeFeatureFlagConfig): Promise<void>;
+    /**
+     * Check if a feature flag is enabled for the user
+     */
     checkFlag(flagName: string, defaultValue?: boolean): boolean;
+    /**
+     * Shutdown the feature flags client
+     */
     shutdown(): void;
     isInitialized(): boolean;
     private sendCheckFlagEvent;
 }
Index: package/out/flag/flag.d.ts
===================================================================
--- package/out/flag/flag.d.ts
+++ package/out/flag/flag.d.ts
@@ -10,9 +10,16 @@
 export interface FlagOptions {
     id: number | string;
     title?: string;
     description?: string;
+    /**
+     * `type` is used to determine the icon of Flag.
+     * If `appearance` is given, `type` is overriden to equal `appearance`.
+     */
     type?: FlagType;
+    /**
+     * When `appearance` is given, the Flag is bold. If not, it's a normal Flag.
+     */
     appearance?: FlagAppearance;
     actions?: FlagAction[];
     isAutoDismiss?: boolean;
 }
Index: package/out/object-store/getMetadata.d.ts
===================================================================
--- package/out/object-store/getMetadata.d.ts
+++ package/out/object-store/getMetadata.d.ts
@@ -1,5 +1,13 @@
 import type { GetMetadataResult } from './types';
+/**
+ * Get metadata for multiple objectIds
+ *
+ * @param functionKey - Configuration object containing the backend function key to filter and generate object metadata
+ * @param keys - Array of object keys to get metadata for
+ * @returns Promise resolving to GetMetadataResult[]
+ * @throws {BridgeAPIError} When filtering fails or download encounters an error
+ */
 export declare const getMetadata: ({ functionKey, keys }: {
     functionKey: string;
     keys: string[];
 }) => Promise<GetMetadataResult[]>;
Index: package/out/i18n/index.d.ts
===================================================================
--- package/out/i18n/index.d.ts
+++ package/out/i18n/index.d.ts
@@ -1,6 +1,21 @@
 import { type ForgeSupportedLocaleCode, type GetTranslationsOptions, type GetTranslationsResult } from '@forge/i18n';
 export declare const resetTranslationsCache: () => void;
 export declare const getTranslations: (locale?: ForgeSupportedLocaleCode | null, options?: GetTranslationsOptions) => Promise<GetTranslationsResult>;
+/**
+ * A function that translates an i18n key to a string.
+ *
+ * @param i18nKey The i18n key to translate.
+ * @param defaultValue The default value to return if the i18n key is not found.
+ *    If not provided, the i18n key is returned.
+ * @returns The translated string.
+ */
 export declare type TranslationFunction = (i18nKey: string, defaultValue?: string) => string;
+/**
+ * Creates a translation function (i.e. `t`) for the given locale.
+ * If no locale is provided, the locale from the current view context is used.
+ *
+ * @param locale The locale to create the translation function for.
+ * @returns The translation function.
+ */
 export declare const createTranslationFunction: (locale?: ForgeSupportedLocaleCode | null) => Promise<TranslationFunction>;
 //# sourceMappingURL=index.d.ts.map
\ No newline at end of file
Index: package/out/invoke/invoke.d.ts
===================================================================
--- package/out/invoke/invoke.d.ts
+++ package/out/invoke/invoke.d.ts
@@ -1,7 +1,30 @@
 import type { Definitions, DefArguments, DefResult } from '@forge/resolver/shared';
-import { InvokePayload } from '../types';
-export declare const invoke: <T>(functionKey: string, payload?: InvokePayload | undefined) => Promise<T>;
+import { InvokePayload, InvokeResponse } from '../types';
+/**
+ * Calls a resolver function with explicit payload and return types.
+ *
+ * The first generic is the payload shape and the second generic is the return type.
+ * When this overload is used, payload is required.
+ *
+ * @example
+ * type Payload = { name: string };
+ * type Response = { message: string };
+ * const response = await invoke<Payload, Response>('greet', { name: 'Taylor' });
+ */
+export declare function invoke<Payload extends InvokePayload, T = InvokeResponse>(functionKey: string, payload: Payload): Promise<T>;
+/**
+ * Calls a resolver function using the legacy generic form where the type argument is the return type.
+ *
+ * @example
+ * const response = await invoke<{ message: string }>('greet', { name: 'Taylor' });
+ */
+export declare function invoke<T = InvokeResponse>(functionKey: string, payload?: InvokePayload): Promise<T>;
 declare type Invoke<Defs extends Definitions> = <Def extends keyof Defs & string>(call: Def, ...arg: DefArguments<Defs, Def>) => Promise<DefResult<Defs, Def>>;
+/**
+ * Specialises the invoke function to a given Definitions type.
+ *
+ * @returns An invoke function that can be used to call backend functions.
+ */
 export declare function makeInvoke<Defs extends Definitions>(): Invoke<Defs>;
 export {};
 //# sourceMappingURL=invoke.d.ts.map
\ No newline at end of file
Index: package/out/rovo/isEnabled.d.ts
===================================================================
--- package/out/rovo/isEnabled.d.ts
+++ package/out/rovo/isEnabled.d.ts
@@ -1,2 +1,7 @@
+/**
+ * Checks if Rovo is enabled for the current tenant.
+ *
+ * @returns boolean
+ */
 export declare const isEnabled: () => Promise<boolean>;
 //# sourceMappingURL=isEnabled.d.ts.map
\ No newline at end of file
Index: package/out/rovo/open.d.ts
===================================================================
--- package/out/rovo/open.d.ts
+++ package/out/rovo/open.d.ts
@@ -1,4 +1,13 @@
 import { OpenRovoPayload } from './types';
 export declare const OPEN_ROVO_BRIDGE_ERROR_MESSAGE = "Unable to open Rovo Chat due to usage in an unsupported product. Only Confluence, Jira and some Jira Service Management modules are supported at this point. See https://developer.atlassian.com/platform/forge/apis-reference/ui-api-bridge/rovo/";
+/**
+ * Opens Rovo chat from the given Rovo agent name.
+ *
+ * @param {OpenRovoPayload} [openRovoPayload] - Payload to open chat.
+ *   Can be one of:
+ *     - ForgeAgentPayload: { agentName: string, agentKey: string, prompt?: string }
+ *     - AtlassianAgentPayload: { agentName: string, prompt?: string }
+ *     - DefaultAgentPayload: { prompt?: string }
+ */
 export declare const open: (openRovoPayload: OpenRovoPayload) => Promise<void>;
 //# sourceMappingURL=open.d.ts.map
\ No newline at end of file
Index: package/out/permissions/permissionsUtil.d.ts
===================================================================
--- package/out/permissions/permissionsUtil.d.ts
+++ package/out/permissions/permissionsUtil.d.ts
@@ -1,10 +1,19 @@
 import type { External } from '@forge/manifest';
 import type { RuntimePermissions } from '../types';
+/**
+ * Resource types that can be loaded externally
+ */
 declare const RESOURCE_TYPES: readonly ["fonts", "styles", "frames", "images", "media", "scripts"];
 export declare type ResourceType = (typeof RESOURCE_TYPES)[number];
+/**
+ * Fetch types for external requests
+ */
 declare const FETCH_TYPES: readonly ["backend", "client"];
 export declare type FetchType = (typeof FETCH_TYPES)[number];
+/**
+ * Required permissions for checking
+ */
 export interface PermissionRequirements {
     scopes?: string[];
     external?: {
         fetch?: {
@@ -19,21 +28,63 @@
         scripts?: string[];
     };
     content?: Record<string, unknown>;
 }
+/**
+ * Missing permissions information
+ */
 export declare type MissingPermissions = PermissionRequirements;
+/**
+ * Permission check result
+ */
 export interface PermissionCheckResult {
     granted: boolean;
     missing: MissingPermissions | null;
 }
+/**
+ * Permission utilities for checking runtime permissions
+ */
 export interface PermissionUtils {
+    /**
+     * Check if a specific scope is granted
+     */
     hasScope: (scope: string) => boolean;
+    /**
+     * Check if fetching from a URL is allowed for the given fetch type
+     * @param type - 'backend' (hostname-only matching) or 'client' (CSP validation with paths)
+     * @param url - The URL to check
+     */
     canFetchFrom: (type: FetchType, url: string) => boolean;
+    /**
+     * Check if loading a resource from a URL is allowed
+     * @param type - The resource type (fonts, styles, frames, images, media, scripts)
+     * @param url - The URL to check
+     */
     canLoadResource: (type: ResourceType, url: string) => boolean;
+    /**
+     * Get all granted scopes
+     */
     getScopes: () => string[];
+    /**
+     * Get all external permissions
+     */
     getExternalPermissions: () => External | undefined;
+    /**
+     * Check if any permissions are granted
+     */
     hasAnyPermissions: () => boolean;
 }
+/**
+ * Create permission utilities from runtime permissions
+ * @param runtimePermissions - The runtime permissions from context
+ * @returns Permission utilities or null if permissions are not available
+ */
 export declare function createPermissionUtils(runtimePermissions: RuntimePermissions | undefined): PermissionUtils | null;
+/**
+ * Check if required permissions are granted
+ * @param requiredPermissions - The permissions required to check
+ * @param runtimePermissions - The runtime permissions from context (optional, will be fetched from view.getContext() if not provided)
+ * @returns Promise resolving to permission check result with granted status and missing permissions
+ */
 export declare function checkPermissions(requiredPermissions: PermissionRequirements, runtimePermissions?: RuntimePermissions | undefined): Promise<PermissionCheckResult>;
 export {};
 //# sourceMappingURL=permissionsUtil.d.ts.map
\ No newline at end of file
Index: package/out/router/targets.d.ts
===================================================================
--- package/out/router/targets.d.ts
+++ package/out/router/targets.d.ts
@@ -1,12 +1,39 @@
 export declare const NavigationTarget: {
+    /**
+     * The view page for pages, blogs and custom content. Takes a contentId to identify the content.
+     */
     readonly ContentView: "contentView";
+    /**
+     * The edit page for pages, blogs and custom content. Takes a contentId to identify the content.
+     */
     readonly ContentEdit: "contentEdit";
+    /**
+     * The list/collector page for pages, blogs and custom content contained in a space. Takes a spaceKey and a contentType to identify the content type for pages and blogs. Takes a moduleKey for custom content.
+     */
     readonly ContentList: "contentList";
+    /**
+     * The space view page. Takes a spaceKey to identify the space.
+     */
     readonly SpaceView: "spaceView";
+    /**
+     * The page within a specific module. Takes a moduleKey to identify the correct module.
+     */
     readonly Module: "module";
+    /**
+     * The profile page for a specific user. Takes an accountId to identify the user.
+     */
     readonly UserProfile: "userProfile";
+    /**
+     * A dashboard in Jira. Takes a dashboardId to identify the dashboard.
+     */
     readonly Dashboard: "dashboard";
+    /**
+     * An issue in Jira. Takes an issueKey to identify the issue.
+     */
     readonly Issue: "issue";
+    /**
+     * The project settings details of a Jira project. Takes a projectKey to identify the project. Only accessible to administrators.
+     */
     readonly ProjectSettingsDetails: "projectSettingsDetails";
 };
 //# sourceMappingURL=targets.d.ts.map
\ No newline at end of file
Index: package/out/types.d.ts
===================================================================
--- package/out/types.d.ts
+++ package/out/types.d.ts
@@ -62,8 +62,14 @@
 }
 export declare type Subscription = {
     unsubscribe: () => void;
 };
+/**
+ * Runtime representation of app permissions that extends the manifest schema.
+ *
+ * This type represents the scopes and external permissions that the app
+ * has been granted, as configured in the manifest.yml file.
+ */
 export interface RuntimePermissions {
     scopes?: Scopes;
     external?: External;
 }
Index: package/out/object-store/upload.d.ts
===================================================================
--- package/out/object-store/upload.d.ts
+++ package/out/object-store/upload.d.ts
@@ -1,9 +1,26 @@
 import type { UploadResult, UploadObject, UploadPromiseItem } from './types';
+/**
+ * Start individual upload operations and return array of promises
+ * This allows tracking per-file upload progress
+ *
+ * @param functionKey - Configuration object containing the backend function key to filter and generate presigned URLs
+ * @param objects - Array of Blob objects or Base64Object (with data and optional mimeType) to upload
+ * @returns Array of individual upload promises with their associated index
+ * @throws {BridgeAPIError} When filtering fails or upload encounters an error
+ */
 export declare const createUploadPromises: ({ functionKey, objects }: {
     functionKey: string;
     objects: UploadObject[];
 }) => Promise<UploadPromiseItem[]>;
+/**
+ * Upload multiple objects to pre-signed URLs
+ *
+ * @param functionKey - Configuration object containing the backend function key to filter and generate presigned URLs
+ * @param objects - Array of Blob objects or Base64Object (with data and optional mimeType) to upload
+ * @returns Promise resolving to UploadResult[]
+ * @throws {BridgeAPIError} When filtering fails or upload encounters an error
+ */
 export declare const upload: ({ functionKey, objects }: {
     functionKey: string;
     objects: UploadObject[];
 }) => Promise<UploadResult[]>;