@get-bb/plugin-sdk 0.4.8 → 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.
- package/README.md +43 -2
- package/bundled-types/bb-plugin-sdk-app.d.ts +319 -27
- package/bundled-types/bb-plugin-sdk-internal-file-navigation-validation.d.ts +42 -0
- package/bundled-types/bb-plugin-sdk-internal-host-policy.d.ts +45 -8
- package/bundled-types/bb-plugin-sdk-internal-plugin-app-collector.d.ts +3 -1
- package/bundled-types/bb-plugin-sdk-provider-bridge.d.ts +2791 -4936
- package/bundled-types/bb-plugin-sdk-testing-app.d.ts +35 -2
- package/bundled-types/bb-plugin-sdk.d.ts +1341 -429
- package/dist/app.js +12 -0
- package/dist/internal/file-navigation-validation.js +135 -0
- package/dist/internal/host-policy.js +104 -0
- package/dist/internal/plugin-app-collector.js +58 -1
- package/dist/provider-bridge.js +3477 -4881
- package/dist/testing/app.js +403 -1
- package/dist/testing/index.js +104 -1
- package/package.json +11 -4
package/dist/testing/app.js
CHANGED
|
@@ -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 = {
|
|
@@ -265,6 +396,8 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
265
396
|
threadLists: [],
|
|
266
397
|
threadHeaderActions: [],
|
|
267
398
|
fileOpeners: [],
|
|
399
|
+
sourceCodeRenderers: [],
|
|
400
|
+
diffRenderers: [],
|
|
268
401
|
messageDirectives: [],
|
|
269
402
|
messageActions: [],
|
|
270
403
|
providerIcons: [],
|
|
@@ -282,6 +415,8 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
282
415
|
threadList: /* @__PURE__ */ new Set(),
|
|
283
416
|
threadHeaderAction: /* @__PURE__ */ new Set(),
|
|
284
417
|
fileOpener: /* @__PURE__ */ new Set(),
|
|
418
|
+
sourceCodeRenderer: /* @__PURE__ */ new Set(),
|
|
419
|
+
diffRenderer: /* @__PURE__ */ new Set(),
|
|
285
420
|
messageDirective: /* @__PURE__ */ new Set(),
|
|
286
421
|
messageAction: /* @__PURE__ */ new Set(),
|
|
287
422
|
providerIcon: /* @__PURE__ */ new Set(),
|
|
@@ -320,6 +455,7 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
320
455
|
const kind = "slots.navPanel";
|
|
321
456
|
const id = requireSlotId(kind, registration?.id);
|
|
322
457
|
requireUniqueId(kind, seenIds.navPanel, id);
|
|
458
|
+
const panelId = id;
|
|
323
459
|
const path = requireNonEmptyString(kind, "path", registration.path);
|
|
324
460
|
if (!PLUGIN_SLOT_ID_PATTERN.test(path)) {
|
|
325
461
|
throw new Error(
|
|
@@ -355,8 +491,25 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
355
491
|
`${fixedTabKind}: "layout" must be "padded" or "flush" when set`
|
|
356
492
|
);
|
|
357
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
|
+
}
|
|
358
510
|
return {
|
|
359
511
|
id: id2,
|
|
512
|
+
panelId: fixedTabPanelId,
|
|
360
513
|
title: requireNonEmptyString(
|
|
361
514
|
fixedTabKind,
|
|
362
515
|
"title",
|
|
@@ -368,7 +521,10 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
368
521
|
fixedTab?.icon
|
|
369
522
|
),
|
|
370
523
|
component: requireComponent(fixedTabKind, fixedTab?.component),
|
|
371
|
-
...layout === void 0 ? {} : { layout }
|
|
524
|
+
...layout === void 0 ? {} : { layout },
|
|
525
|
+
...experimentalTarget === void 0 ? {} : {
|
|
526
|
+
experimental_target: experimentalTarget
|
|
527
|
+
}
|
|
372
528
|
};
|
|
373
529
|
});
|
|
374
530
|
})();
|
|
@@ -501,6 +657,38 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
501
657
|
component: requireComponent(kind, registration.component)
|
|
502
658
|
});
|
|
503
659
|
},
|
|
660
|
+
experimental_sourceCodeRenderer(registration) {
|
|
661
|
+
const kind = "slots.experimental_sourceCodeRenderer";
|
|
662
|
+
const id = requireSlotId(kind, registration?.id);
|
|
663
|
+
requireUniqueId(kind, seenIds.sourceCodeRenderer, id);
|
|
664
|
+
const description = requireOptionalString(
|
|
665
|
+
kind,
|
|
666
|
+
"description",
|
|
667
|
+
registration.description
|
|
668
|
+
);
|
|
669
|
+
collected.sourceCodeRenderers.push({
|
|
670
|
+
id,
|
|
671
|
+
title: requireNonEmptyString(kind, "title", registration.title),
|
|
672
|
+
...description !== void 0 ? { description } : {},
|
|
673
|
+
component: requireComponent(kind, registration.component)
|
|
674
|
+
});
|
|
675
|
+
},
|
|
676
|
+
experimental_diffRenderer(registration) {
|
|
677
|
+
const kind = "slots.experimental_diffRenderer";
|
|
678
|
+
const id = requireSlotId(kind, registration?.id);
|
|
679
|
+
requireUniqueId(kind, seenIds.diffRenderer, id);
|
|
680
|
+
const description = requireOptionalString(
|
|
681
|
+
kind,
|
|
682
|
+
"description",
|
|
683
|
+
registration.description
|
|
684
|
+
);
|
|
685
|
+
collected.diffRenderers.push({
|
|
686
|
+
id,
|
|
687
|
+
title: requireNonEmptyString(kind, "title", registration.title),
|
|
688
|
+
...description !== void 0 ? { description } : {},
|
|
689
|
+
component: requireComponent(kind, registration.component)
|
|
690
|
+
});
|
|
691
|
+
},
|
|
504
692
|
messageDirective(registration) {
|
|
505
693
|
const kind = "slots.messageDirective";
|
|
506
694
|
const id = requireMessageDirectiveId(kind, registration?.id);
|
|
@@ -643,6 +831,65 @@ function TestThreadChat({
|
|
|
643
831
|
function TestMarkdown({ content, className }) {
|
|
644
832
|
return /* @__PURE__ */ jsx("div", { "data-testid": "bb-markdown", className, children: content });
|
|
645
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
|
+
}
|
|
646
893
|
function TestNewThreadComposer({
|
|
647
894
|
defaultProjectId,
|
|
648
895
|
defaultProviderId,
|
|
@@ -710,6 +957,46 @@ function TestNewThreadComposer({
|
|
|
710
957
|
}
|
|
711
958
|
);
|
|
712
959
|
}
|
|
960
|
+
function TestSourceCode({
|
|
961
|
+
content,
|
|
962
|
+
path,
|
|
963
|
+
overflow = "scroll",
|
|
964
|
+
highlightedLines = null,
|
|
965
|
+
className
|
|
966
|
+
}) {
|
|
967
|
+
return /* @__PURE__ */ jsx(
|
|
968
|
+
"pre",
|
|
969
|
+
{
|
|
970
|
+
"data-testid": "bb-source-code",
|
|
971
|
+
"data-path": path,
|
|
972
|
+
"data-overflow": overflow,
|
|
973
|
+
"data-highlighted-lines": highlightedLines === null ? "" : `${highlightedLines.start}-${highlightedLines.end}`,
|
|
974
|
+
className,
|
|
975
|
+
children: content
|
|
976
|
+
}
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
function TestDiff({
|
|
980
|
+
patch,
|
|
981
|
+
path,
|
|
982
|
+
view = "unified",
|
|
983
|
+
overflow = "scroll",
|
|
984
|
+
showLineNumbers = true,
|
|
985
|
+
className
|
|
986
|
+
}) {
|
|
987
|
+
return /* @__PURE__ */ jsx(
|
|
988
|
+
"pre",
|
|
989
|
+
{
|
|
990
|
+
"data-testid": "bb-diff",
|
|
991
|
+
"data-path": path,
|
|
992
|
+
"data-view": view,
|
|
993
|
+
"data-overflow": overflow,
|
|
994
|
+
"data-show-line-numbers": showLineNumbers ? "true" : "false",
|
|
995
|
+
className,
|
|
996
|
+
children: patch
|
|
997
|
+
}
|
|
998
|
+
);
|
|
999
|
+
}
|
|
713
1000
|
var testPluginSdkApp = {
|
|
714
1001
|
definePluginApp,
|
|
715
1002
|
useRpc() {
|
|
@@ -753,6 +1040,30 @@ var testPluginSdkApp = {
|
|
|
753
1040
|
useBbNavigate() {
|
|
754
1041
|
return useSlotEnv("useBbNavigate").navigate;
|
|
755
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
|
+
},
|
|
756
1067
|
useComposer() {
|
|
757
1068
|
const composer = useSlotEnv("useComposer").composer;
|
|
758
1069
|
const version = useSyncExternalStore(
|
|
@@ -771,7 +1082,11 @@ var testPluginSdkApp = {
|
|
|
771
1082
|
},
|
|
772
1083
|
ThreadChat: TestThreadChat,
|
|
773
1084
|
Markdown: TestMarkdown,
|
|
1085
|
+
experimental_FileLink: TestFileLink,
|
|
1086
|
+
experimental_UrlLink: TestUrlLink,
|
|
774
1087
|
experimental_NewThreadComposer: TestNewThreadComposer,
|
|
1088
|
+
experimental_SourceCode: TestSourceCode,
|
|
1089
|
+
experimental_Diff: TestDiff,
|
|
775
1090
|
experimental_useSidebarThreads() {
|
|
776
1091
|
return useSlotEnv("experimental_useSidebarThreads").sidebarThreads;
|
|
777
1092
|
},
|
|
@@ -1008,6 +1323,70 @@ function renderSlot(registration, props, options = {}) {
|
|
|
1008
1323
|
}
|
|
1009
1324
|
};
|
|
1010
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
|
+
};
|
|
1011
1390
|
const sidebarActionCalls = [];
|
|
1012
1391
|
const sidebarPullRequests = new Map(
|
|
1013
1392
|
Object.entries(options.sidebarPullRequests ?? {})
|
|
@@ -1073,6 +1452,24 @@ function renderSlot(registration, props, options = {}) {
|
|
|
1073
1452
|
options: panelOptions
|
|
1074
1453
|
});
|
|
1075
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;
|
|
1076
1473
|
}
|
|
1077
1474
|
};
|
|
1078
1475
|
const projectId = options.context?.projectId ?? null;
|
|
@@ -1174,6 +1571,9 @@ ${block}
|
|
|
1174
1571
|
bbContext: { projectId, threadId },
|
|
1175
1572
|
navigate,
|
|
1176
1573
|
navigateCalls,
|
|
1574
|
+
appPanel,
|
|
1575
|
+
experimental_fixedTabOpenCalls,
|
|
1576
|
+
fixedTabTarget,
|
|
1177
1577
|
composer,
|
|
1178
1578
|
composerLog,
|
|
1179
1579
|
sidebarThreads,
|
|
@@ -1229,6 +1629,7 @@ ${block}
|
|
|
1229
1629
|
setComposerText,
|
|
1230
1630
|
setComposerScope,
|
|
1231
1631
|
navigateCalls,
|
|
1632
|
+
experimental_fixedTabOpenCalls,
|
|
1232
1633
|
sidebarActionCalls,
|
|
1233
1634
|
composer: composerLog,
|
|
1234
1635
|
behavior: {
|
|
@@ -1240,6 +1641,7 @@ ${block}
|
|
|
1240
1641
|
inspection: {
|
|
1241
1642
|
rpcCalls,
|
|
1242
1643
|
navigateCalls,
|
|
1644
|
+
experimental_fixedTabOpenCalls,
|
|
1243
1645
|
sidebarActionCalls,
|
|
1244
1646
|
composer: composerLog
|
|
1245
1647
|
},
|
package/dist/testing/index.js
CHANGED
|
@@ -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
|
});
|
|
@@ -1047,7 +1150,7 @@ function createFakePluginHostInternal(options, sharedState) {
|
|
|
1047
1150
|
kv,
|
|
1048
1151
|
database() {
|
|
1049
1152
|
assertLive();
|
|
1050
|
-
if (!databaseHandle) {
|
|
1153
|
+
if (!databaseHandle?.open) {
|
|
1051
1154
|
databaseHandle = new Database(join(storageRoot, "data.db"));
|
|
1052
1155
|
databaseHandle.pragma("busy_timeout = 5000");
|
|
1053
1156
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@get-bb/plugin-sdk",
|
|
3
|
-
"version": "0.4.
|
|
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",
|
|
@@ -89,12 +95,13 @@
|
|
|
89
95
|
},
|
|
90
96
|
"types": "./bundled-types/bb-plugin-sdk.d.ts",
|
|
91
97
|
"scripts": {
|
|
92
|
-
"build": "node scripts/build-
|
|
98
|
+
"build": "node scripts/build-runtime.mjs",
|
|
93
99
|
"build:runtime": "node scripts/build-runtime.mjs",
|
|
94
|
-
"
|
|
100
|
+
"build:types": "node scripts/build-bundled-dts.mjs",
|
|
101
|
+
"clean": "rimraf bundled-types dist tsconfig.tsbuildinfo",
|
|
95
102
|
"prepack": "node scripts/build-bundled-dts.mjs && node scripts/build-runtime.mjs",
|
|
96
103
|
"test": "vitest run --config vitest.config.ts",
|
|
97
|
-
"typecheck": "
|
|
104
|
+
"typecheck": "tsc --noEmit"
|
|
98
105
|
},
|
|
99
106
|
"devDependencies": {
|
|
100
107
|
"@bb/domain": "workspace:*",
|