@get-bb/plugin-sdk 0.4.9 → 0.4.10

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.
@@ -251,6 +251,137 @@ function collectComposerCustomization(registration, seenIds, onRejected) {
251
251
  }
252
252
  }
253
253
 
254
+ // src/internal/file-navigation-validation.ts
255
+ var FILE_PATH_MAX_LENGTH = 32768;
256
+ var WINDOWS_DRIVE_ABSOLUTE_PATH = /^[A-Za-z]:[\\/]/u;
257
+ var WINDOWS_UNC_ABSOLUTE_PATH = /^\\\\/u;
258
+ function isJsonObject(value) {
259
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
260
+ return false;
261
+ }
262
+ const prototype = Object.getPrototypeOf(value);
263
+ return prototype === Object.prototype || prototype === null;
264
+ }
265
+ function hasExactKeys(value, keys) {
266
+ const actualKeys = Object.keys(value);
267
+ return actualKeys.length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
268
+ }
269
+ function isNonEmptyIdentity(value) {
270
+ return typeof value === "string" && value.length > 0 && value.length <= FILE_PATH_MAX_LENGTH && value.trim() === value;
271
+ }
272
+ function hasControlCharacter(value) {
273
+ for (const character of value) {
274
+ const codePoint = character.codePointAt(0);
275
+ if (codePoint !== void 0 && codePoint < 32) return true;
276
+ }
277
+ return false;
278
+ }
279
+ function hasUnpairedSurrogate(value) {
280
+ for (let index = 0; index < value.length; index += 1) {
281
+ const codeUnit = value.charCodeAt(index);
282
+ if (codeUnit >= 55296 && codeUnit <= 56319) {
283
+ if (index + 1 >= value.length) return true;
284
+ const nextCodeUnit = value.charCodeAt(index + 1);
285
+ if (nextCodeUnit < 56320 || nextCodeUnit > 57343) return true;
286
+ index += 1;
287
+ } else if (codeUnit >= 56320 && codeUnit <= 57343) {
288
+ return true;
289
+ }
290
+ }
291
+ return false;
292
+ }
293
+ function isValidPathSegment(segment) {
294
+ return segment.length > 0 && segment !== "." && segment !== "..";
295
+ }
296
+ function isPositiveSafeInteger(value) {
297
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
298
+ }
299
+ function isValidRelativeFilePath(value) {
300
+ if (typeof value !== "string" || value.length === 0 || value.length > FILE_PATH_MAX_LENGTH || value.trim() !== value || value.includes("\\") || hasControlCharacter(value) || hasUnpairedSurrogate(value)) {
301
+ return false;
302
+ }
303
+ return value.split("/").every(isValidPathSegment);
304
+ }
305
+ function isValidAbsoluteHostFilePath(value) {
306
+ if (typeof value !== "string" || value.length === 0 || value.length > FILE_PATH_MAX_LENGTH || value.trim() !== value || hasControlCharacter(value) || hasUnpairedSurrogate(value)) {
307
+ return false;
308
+ }
309
+ if (value.startsWith("/") && !value.startsWith("//")) {
310
+ const segments = value.slice(1).split("/");
311
+ return segments.length > 0 && segments.every(isValidPathSegment);
312
+ }
313
+ if (WINDOWS_DRIVE_ABSOLUTE_PATH.test(value)) {
314
+ const segments = value.slice(3).split(/[\\/]/u);
315
+ return segments.length > 0 && segments.every(isValidPathSegment);
316
+ }
317
+ if (WINDOWS_UNC_ABSOLUTE_PATH.test(value)) {
318
+ const segments = value.slice(2).split(/[\\/]/u);
319
+ return segments.length >= 3 && segments.every(isValidPathSegment);
320
+ }
321
+ return false;
322
+ }
323
+ function normalizeExperimentalLiveFileTarget(value) {
324
+ if (!isJsonObject(value) || typeof value.kind !== "string") return null;
325
+ switch (value.kind) {
326
+ case "workspace":
327
+ if (!hasExactKeys(value, ["kind", "environmentId", "path"]) || !isNonEmptyIdentity(value.environmentId) || !isValidRelativeFilePath(value.path)) {
328
+ return null;
329
+ }
330
+ return {
331
+ kind: value.kind,
332
+ environmentId: value.environmentId,
333
+ path: value.path
334
+ };
335
+ case "host":
336
+ if (!hasExactKeys(value, ["kind", "hostId", "path"]) || !isNonEmptyIdentity(value.hostId) || !isValidAbsoluteHostFilePath(value.path)) {
337
+ return null;
338
+ }
339
+ return { kind: value.kind, hostId: value.hostId, path: value.path };
340
+ case "thread-storage":
341
+ if (!hasExactKeys(value, ["kind", "threadId", "path"]) || !isNonEmptyIdentity(value.threadId) || !isValidRelativeFilePath(value.path)) {
342
+ return null;
343
+ }
344
+ return { kind: value.kind, threadId: value.threadId, path: value.path };
345
+ default:
346
+ return null;
347
+ }
348
+ }
349
+ function normalizeExperimentalFileLocation(value) {
350
+ if (value === null) return null;
351
+ if (!isJsonObject(value) || typeof value.kind !== "string") return void 0;
352
+ switch (value.kind) {
353
+ case "line":
354
+ if (!hasExactKeys(value, ["kind", "line", "column"]) || !isPositiveSafeInteger(value.line) || value.column !== null && !isPositiveSafeInteger(value.column)) {
355
+ return void 0;
356
+ }
357
+ return {
358
+ kind: value.kind,
359
+ line: value.line,
360
+ column: value.column
361
+ };
362
+ case "range":
363
+ if (!hasExactKeys(value, ["kind", "startLine", "endLine"]) || !isPositiveSafeInteger(value.startLine) || !isPositiveSafeInteger(value.endLine) || value.endLine < value.startLine) {
364
+ return void 0;
365
+ }
366
+ return {
367
+ kind: value.kind,
368
+ startLine: value.startLine,
369
+ endLine: value.endLine
370
+ };
371
+ default:
372
+ return void 0;
373
+ }
374
+ }
375
+ function normalizeExperimentalFileOpenOptions(value) {
376
+ if (!isJsonObject(value) || !hasExactKeys(value, ["target", "location"])) {
377
+ return null;
378
+ }
379
+ const target = normalizeExperimentalLiveFileTarget(value.target);
380
+ const location = normalizeExperimentalFileLocation(value.location);
381
+ if (target === null || location === void 0) return null;
382
+ return { target, location };
383
+ }
384
+
254
385
  // src/internal/plugin-app-collector.ts
255
386
  function collectPluginAppRegistrations(definition, onComposerCustomizationRejected = (reason) => console.warn(reason)) {
256
387
  const collected = {
@@ -324,6 +455,7 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
324
455
  const kind = "slots.navPanel";
325
456
  const id = requireSlotId(kind, registration?.id);
326
457
  requireUniqueId(kind, seenIds.navPanel, id);
458
+ const panelId = id;
327
459
  const path = requireNonEmptyString(kind, "path", registration.path);
328
460
  if (!PLUGIN_SLOT_ID_PATTERN.test(path)) {
329
461
  throw new Error(
@@ -359,8 +491,25 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
359
491
  `${fixedTabKind}: "layout" must be "padded" or "flush" when set`
360
492
  );
361
493
  }
494
+ const fixedTabPanelId = requireNonEmptyString(
495
+ fixedTabKind,
496
+ "panelId",
497
+ fixedTab?.panelId
498
+ );
499
+ if (fixedTabPanelId !== panelId) {
500
+ throw new Error(
501
+ `${fixedTabKind}: "panelId" must match its containing navPanel id ${JSON.stringify(panelId)}`
502
+ );
503
+ }
504
+ const experimentalTarget = fixedTab?.experimental_target;
505
+ if (experimentalTarget !== void 0 && (typeof experimentalTarget !== "object" || experimentalTarget === null || typeof Reflect.get(experimentalTarget, "validate") !== "function")) {
506
+ throw new Error(
507
+ `${fixedTabKind}: "experimental_target.validate" must be a function when set`
508
+ );
509
+ }
362
510
  return {
363
511
  id: id2,
512
+ panelId: fixedTabPanelId,
364
513
  title: requireNonEmptyString(
365
514
  fixedTabKind,
366
515
  "title",
@@ -372,7 +521,10 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
372
521
  fixedTab?.icon
373
522
  ),
374
523
  component: requireComponent(fixedTabKind, fixedTab?.component),
375
- ...layout === void 0 ? {} : { layout }
524
+ ...layout === void 0 ? {} : { layout },
525
+ ...experimentalTarget === void 0 ? {} : {
526
+ experimental_target: experimentalTarget
527
+ }
376
528
  };
377
529
  });
378
530
  })();
@@ -679,6 +831,65 @@ function TestThreadChat({
679
831
  function TestMarkdown({ content, className }) {
680
832
  return /* @__PURE__ */ jsx("div", { "data-testid": "bb-markdown", className, children: content });
681
833
  }
834
+ function TestUrlLink({
835
+ href,
836
+ onClick,
837
+ rel,
838
+ target,
839
+ ...anchorProps
840
+ }) {
841
+ const navigate = useSlotEnv("experimental_UrlLink").navigate;
842
+ const normalizedTarget = target?.toLowerCase();
843
+ const opensNewBrowsingContext = normalizedTarget !== void 0 && normalizedTarget !== "" && normalizedTarget !== "_self" && normalizedTarget !== "_parent" && normalizedTarget !== "_top" && normalizedTarget !== "_unfencedtop";
844
+ const relTokens = rel?.split(/\s+/u).filter(Boolean) ?? [];
845
+ const normalizedRelTokens = relTokens.map((token) => token.toLowerCase());
846
+ const resolvedRel = opensNewBrowsingContext && !normalizedRelTokens.includes("opener") ? [
847
+ ...relTokens,
848
+ ...normalizedRelTokens.includes("noopener") ? [] : ["noopener"],
849
+ ...normalizedRelTokens.includes("noreferrer") ? [] : ["noreferrer"]
850
+ ].join(" ") : rel;
851
+ return /* @__PURE__ */ jsx(
852
+ "a",
853
+ {
854
+ ...anchorProps,
855
+ href,
856
+ target,
857
+ rel: resolvedRel,
858
+ onClick: (event) => {
859
+ onClick?.(event);
860
+ if (event.defaultPrevented || event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.currentTarget.hasAttribute("download") || event.currentTarget.hasAttribute("target")) {
861
+ return;
862
+ }
863
+ if (navigate.experimental_openUrl(href)) event.preventDefault();
864
+ }
865
+ }
866
+ );
867
+ }
868
+ function TestFileLink({
869
+ target,
870
+ location = null,
871
+ onClick,
872
+ ...anchorProps
873
+ }) {
874
+ const navigate = useSlotEnv("experimental_FileLink").navigate;
875
+ const options = normalizeExperimentalFileOpenOptions({ target, location });
876
+ const href = options === null ? void 0 : `./${encodeURIComponent(options.target.path)}`;
877
+ return /* @__PURE__ */ jsx(
878
+ "a",
879
+ {
880
+ ...anchorProps,
881
+ href,
882
+ onClick: (event) => {
883
+ onClick?.(event);
884
+ if (options === null || event.defaultPrevented || event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.currentTarget.hasAttribute("download")) {
885
+ return;
886
+ }
887
+ event.preventDefault();
888
+ navigate.experimental_openFilePreview(options);
889
+ }
890
+ }
891
+ );
892
+ }
682
893
  function TestNewThreadComposer({
683
894
  defaultProjectId,
684
895
  defaultProviderId,
@@ -829,6 +1040,30 @@ var testPluginSdkApp = {
829
1040
  useBbNavigate() {
830
1041
  return useSlotEnv("useBbNavigate").navigate;
831
1042
  },
1043
+ experimental_useAppPanel() {
1044
+ return useSlotEnv("experimental_useAppPanel").appPanel;
1045
+ },
1046
+ experimental_useFixedTabTarget(tab) {
1047
+ const store = useSlotEnv("experimental_useFixedTabTarget").fixedTabTarget;
1048
+ const state = useSyncExternalStore(
1049
+ store.subscribe,
1050
+ store.getSnapshot,
1051
+ store.getSnapshot
1052
+ );
1053
+ if (state === null || state.panelId !== tab.panelId || state.tabId !== tab.id || tab.experimental_target === void 0) {
1054
+ return null;
1055
+ }
1056
+ try {
1057
+ if (!tab.experimental_target.validate(state.target)) return null;
1058
+ } catch {
1059
+ return null;
1060
+ }
1061
+ return {
1062
+ clear: () => store.clear(state.sequence),
1063
+ sequence: state.sequence,
1064
+ target: state.target
1065
+ };
1066
+ },
832
1067
  useComposer() {
833
1068
  const composer = useSlotEnv("useComposer").composer;
834
1069
  const version = useSyncExternalStore(
@@ -847,6 +1082,8 @@ var testPluginSdkApp = {
847
1082
  },
848
1083
  ThreadChat: TestThreadChat,
849
1084
  Markdown: TestMarkdown,
1085
+ experimental_FileLink: TestFileLink,
1086
+ experimental_UrlLink: TestUrlLink,
850
1087
  experimental_NewThreadComposer: TestNewThreadComposer,
851
1088
  experimental_SourceCode: TestSourceCode,
852
1089
  experimental_Diff: TestDiff,
@@ -1086,6 +1323,70 @@ function renderSlot(registration, props, options = {}) {
1086
1323
  }
1087
1324
  };
1088
1325
  const navigateCalls = [];
1326
+ const experimental_fixedTabOpenCalls = [];
1327
+ let fixedTabTargetSnapshot = options.experimental_fixedTabTarget === void 0 ? null : {
1328
+ panelId: options.experimental_fixedTabTarget.panelId,
1329
+ sequence: 1,
1330
+ tabId: options.experimental_fixedTabTarget.tabId,
1331
+ target: strictJsonRoundTrip(
1332
+ options.experimental_fixedTabTarget.target,
1333
+ "fixed tab target"
1334
+ )
1335
+ };
1336
+ const fixedTabTargetListeners = /* @__PURE__ */ new Set();
1337
+ const fixedTabTarget = {
1338
+ getSnapshot: () => fixedTabTargetSnapshot,
1339
+ subscribe(listener) {
1340
+ fixedTabTargetListeners.add(listener);
1341
+ return () => fixedTabTargetListeners.delete(listener);
1342
+ },
1343
+ clear(sequence) {
1344
+ if (fixedTabTargetSnapshot?.sequence !== sequence) return;
1345
+ fixedTabTargetSnapshot = null;
1346
+ for (const listener of fixedTabTargetListeners) listener();
1347
+ }
1348
+ };
1349
+ const appPanel = {
1350
+ openFixedTab(panelOptions) {
1351
+ let target;
1352
+ if (panelOptions.target !== void 0) {
1353
+ try {
1354
+ target = strictJsonRoundTrip(
1355
+ panelOptions.target,
1356
+ "fixed tab open target"
1357
+ );
1358
+ } catch {
1359
+ return false;
1360
+ }
1361
+ if (panelOptions.tab.experimental_target === void 0) return false;
1362
+ try {
1363
+ if (!panelOptions.tab.experimental_target.validate(target)) {
1364
+ return false;
1365
+ }
1366
+ } catch {
1367
+ return false;
1368
+ }
1369
+ }
1370
+ const call = {
1371
+ surface: panelOptions.surface,
1372
+ panelId: panelOptions.tab.panelId,
1373
+ tabId: panelOptions.tab.id,
1374
+ ...target === void 0 ? {} : { target }
1375
+ };
1376
+ experimental_fixedTabOpenCalls.push(call);
1377
+ const accepted = options.experimental_openFixedTab?.(call) ?? false;
1378
+ if (accepted && target !== void 0) {
1379
+ fixedTabTargetSnapshot = {
1380
+ panelId: panelOptions.tab.panelId,
1381
+ sequence: (fixedTabTargetSnapshot?.sequence ?? 0) + 1,
1382
+ tabId: panelOptions.tab.id,
1383
+ target
1384
+ };
1385
+ for (const listener of fixedTabTargetListeners) listener();
1386
+ }
1387
+ return accepted;
1388
+ }
1389
+ };
1089
1390
  const sidebarActionCalls = [];
1090
1391
  const sidebarPullRequests = new Map(
1091
1392
  Object.entries(options.sidebarPullRequests ?? {})
@@ -1151,6 +1452,24 @@ function renderSlot(registration, props, options = {}) {
1151
1452
  options: panelOptions
1152
1453
  });
1153
1454
  return options.openThreadPanel?.(panelOptions) ?? false;
1455
+ },
1456
+ experimental_openUrl(url) {
1457
+ navigateCalls.push({ method: "experimental_openUrl", url });
1458
+ return options.openUrl?.(url) ?? false;
1459
+ },
1460
+ experimental_openFilePreview(fileOptions) {
1461
+ navigateCalls.push({
1462
+ method: "experimental_openFilePreview",
1463
+ options: fileOptions
1464
+ });
1465
+ return options.openFilePreview?.(fileOptions) ?? false;
1466
+ },
1467
+ experimental_openFileExternally(fileOptions) {
1468
+ navigateCalls.push({
1469
+ method: "experimental_openFileExternally",
1470
+ options: fileOptions
1471
+ });
1472
+ return options.openFileExternally?.(fileOptions) ?? false;
1154
1473
  }
1155
1474
  };
1156
1475
  const projectId = options.context?.projectId ?? null;
@@ -1252,6 +1571,9 @@ ${block}
1252
1571
  bbContext: { projectId, threadId },
1253
1572
  navigate,
1254
1573
  navigateCalls,
1574
+ appPanel,
1575
+ experimental_fixedTabOpenCalls,
1576
+ fixedTabTarget,
1255
1577
  composer,
1256
1578
  composerLog,
1257
1579
  sidebarThreads,
@@ -1307,6 +1629,7 @@ ${block}
1307
1629
  setComposerText,
1308
1630
  setComposerScope,
1309
1631
  navigateCalls,
1632
+ experimental_fixedTabOpenCalls,
1310
1633
  sidebarActionCalls,
1311
1634
  composer: composerLog,
1312
1635
  behavior: {
@@ -1318,6 +1641,7 @@ ${block}
1318
1641
  inspection: {
1319
1642
  rpcCalls,
1320
1643
  navigateCalls,
1644
+ experimental_fixedTabOpenCalls,
1321
1645
  sidebarActionCalls,
1322
1646
  composer: composerLog
1323
1647
  },
@@ -66,6 +66,7 @@ var PLUGIN_AGENT_DYNAMIC_INSTRUCTIONS_MAX_CHARS = 4096;
66
66
  var PLUGIN_AGENT_TOOL_PARAMETERS_MAX_BYTES = 128 * 1024;
67
67
  var MENTION_PROVIDER_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
68
68
  var PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
69
+ var PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES = 64 * 1024;
69
70
  var SETTING_KEY_PATTERN = /^[a-zA-Z0-9_-]+$/;
70
71
  var settingsBaseFields = {
71
72
  label: z2.string().min(1),
@@ -267,6 +268,70 @@ function validateProviderLiteralArray(args) {
267
268
  }
268
269
  return Object.freeze(normalized);
269
270
  }
271
+ function normalizeProviderBridgeOptions(providerId, value) {
272
+ const active = /* @__PURE__ */ new Set();
273
+ function visit(current, path) {
274
+ if (current === null || typeof current === "string" || typeof current === "boolean") {
275
+ return current;
276
+ }
277
+ if (typeof current === "number") {
278
+ if (!Number.isFinite(current)) {
279
+ throw new Error(
280
+ `provider "${providerId}" experimental_bridgeOptions${path} must be finite JSON`
281
+ );
282
+ }
283
+ return current;
284
+ }
285
+ if (typeof current !== "object") {
286
+ throw new Error(
287
+ `provider "${providerId}" experimental_bridgeOptions${path} must be JSON`
288
+ );
289
+ }
290
+ if (active.has(current)) {
291
+ throw new Error(
292
+ `provider "${providerId}" experimental_bridgeOptions must not contain cycles`
293
+ );
294
+ }
295
+ active.add(current);
296
+ try {
297
+ if (Array.isArray(current)) {
298
+ const normalized3 = current.map(
299
+ (entry, index) => visit(entry, `${path}[${index}]`)
300
+ );
301
+ Object.freeze(normalized3);
302
+ return normalized3;
303
+ }
304
+ const prototype = Object.getPrototypeOf(current);
305
+ if (prototype !== Object.prototype && prototype !== null) {
306
+ throw new Error(
307
+ `provider "${providerId}" experimental_bridgeOptions${path} must contain only plain JSON objects`
308
+ );
309
+ }
310
+ const normalized2 = Object.fromEntries(
311
+ Object.entries(current).map(([key, entry]) => [
312
+ key,
313
+ visit(entry, `${path}.${key}`)
314
+ ])
315
+ );
316
+ Object.freeze(normalized2);
317
+ return normalized2;
318
+ } finally {
319
+ active.delete(current);
320
+ }
321
+ }
322
+ const normalized = visit(value, "");
323
+ if (normalized === null || Array.isArray(normalized) || typeof normalized !== "object") {
324
+ throw new Error(
325
+ `provider "${providerId}" experimental_bridgeOptions must be an object`
326
+ );
327
+ }
328
+ if (Buffer.byteLength(JSON.stringify(normalized), "utf8") > PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES) {
329
+ throw new Error(
330
+ `provider "${providerId}" experimental_bridgeOptions exceeds ${PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES} bytes`
331
+ );
332
+ }
333
+ return normalized;
334
+ }
270
335
  function validatePluginProviderDeclaration(declaration) {
271
336
  if (typeof declaration !== "object" || declaration === null) {
272
337
  throw new Error("provider declaration must be an object");
@@ -304,6 +369,24 @@ function validatePluginProviderDeclaration(declaration) {
304
369
  if (typeof capabilities !== "object" || capabilities === null) {
305
370
  throw new Error(`provider "${id}" capabilities must be an object`);
306
371
  }
372
+ const experimentalProviderHealth = capabilities.experimental_providerHealth ?? false;
373
+ const experimentalProviderUsage = capabilities.experimental_providerUsage ?? false;
374
+ const experimentalProviderInstallation = capabilities.experimental_providerInstallation ?? false;
375
+ if (typeof experimentalProviderHealth !== "boolean") {
376
+ throw new Error(
377
+ `provider "${id}" capabilities.experimental_providerHealth must be a boolean`
378
+ );
379
+ }
380
+ if (typeof experimentalProviderUsage !== "boolean") {
381
+ throw new Error(
382
+ `provider "${id}" capabilities.experimental_providerUsage must be a boolean`
383
+ );
384
+ }
385
+ if (typeof experimentalProviderInstallation !== "boolean") {
386
+ throw new Error(
387
+ `provider "${id}" capabilities.experimental_providerInstallation must be a boolean`
388
+ );
389
+ }
307
390
  const booleanCapabilityFields = [
308
391
  "supportsServiceTier",
309
392
  "supportsNativeUserQuestion",
@@ -325,6 +408,9 @@ function validatePluginProviderDeclaration(declaration) {
325
408
  );
326
409
  }
327
410
  const normalizedCapabilities = Object.freeze({
411
+ experimental_providerHealth: experimentalProviderHealth,
412
+ experimental_providerUsage: experimentalProviderUsage,
413
+ experimental_providerInstallation: experimentalProviderInstallation,
328
414
  supportsServiceTier: capabilities.supportsServiceTier,
329
415
  supportsNativeUserQuestion: capabilities.supportsNativeUserQuestion,
330
416
  fork: capabilities.fork,
@@ -354,10 +440,27 @@ function validatePluginProviderDeclaration(declaration) {
354
440
  allowed: PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES,
355
441
  requireNonEmpty: false
356
442
  });
443
+ const bridgeOptions = declaration.experimental_bridgeOptions === void 0 ? void 0 : normalizeProviderBridgeOptions(
444
+ id,
445
+ declaration.experimental_bridgeOptions
446
+ );
447
+ const visibility = declaration.experimental_visibility ?? "always";
448
+ if (visibility !== "always" && visibility !== "installed") {
449
+ throw new Error(
450
+ `provider "${id}" experimental_visibility must be "always" or "installed"`
451
+ );
452
+ }
453
+ if (visibility === "installed" && !normalizedCapabilities.experimental_providerHealth) {
454
+ throw new Error(
455
+ `provider "${id}" experimental_visibility "installed" requires experimental_providerHealth`
456
+ );
457
+ }
357
458
  return Object.freeze({
358
459
  id,
359
460
  displayName,
360
461
  ...icon === void 0 ? {} : { icon },
462
+ ...bridgeOptions === void 0 ? {} : { experimental_bridgeOptions: bridgeOptions },
463
+ experimental_visibility: visibility,
361
464
  capabilities: normalizedCapabilities,
362
465
  composerActions
363
466
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@get-bb/plugin-sdk",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
4
4
  "homepage": "https://github.com/get-bb/bb#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/get-bb/bb/issues"
@@ -56,6 +56,12 @@
56
56
  "import": "./dist/internal/composer-view.js",
57
57
  "default": "./dist/internal/composer-view.js"
58
58
  },
59
+ "./internal/file-navigation-validation": {
60
+ "source": "./src/internal/file-navigation-validation.ts",
61
+ "types": "./bundled-types/bb-plugin-sdk-internal-file-navigation-validation.d.ts",
62
+ "import": "./dist/internal/file-navigation-validation.js",
63
+ "default": "./dist/internal/file-navigation-validation.js"
64
+ },
59
65
  "./internal/host-policy": {
60
66
  "source": "./src/internal/host-policy.ts",
61
67
  "types": "./bundled-types/bb-plugin-sdk-internal-host-policy.d.ts",