@get-bb/plugin-sdk 0.4.6 → 0.4.9

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.
@@ -57,6 +57,14 @@ function requireSlotId(kind, value) {
57
57
  }
58
58
  return value;
59
59
  }
60
+ function requireProviderId(kind, value) {
61
+ if (typeof value !== "string" || !PLUGIN_SLOT_ID_PATTERN.test(value)) {
62
+ throw new Error(
63
+ `${kind}: "providerId" must match ${String(PLUGIN_SLOT_ID_PATTERN)}, got ${JSON.stringify(value)}`
64
+ );
65
+ }
66
+ return value;
67
+ }
60
68
  function requireMessageDirectiveId(kind, value) {
61
69
  if (typeof value !== "string" || !PLUGIN_MESSAGE_DIRECTIVE_ID_PATTERN.test(value)) {
62
70
  throw new Error(
@@ -243,6 +251,354 @@ function collectComposerCustomization(registration, seenIds, onRejected) {
243
251
  }
244
252
  }
245
253
 
254
+ // src/internal/plugin-app-collector.ts
255
+ function collectPluginAppRegistrations(definition, onComposerCustomizationRejected = (reason) => console.warn(reason)) {
256
+ const collected = {
257
+ homepageSections: [],
258
+ settingsSections: [],
259
+ navPanels: [],
260
+ threadPanelActions: [],
261
+ newThreadPanelActions: [],
262
+ composerCustomizations: [],
263
+ pendingInteractions: [],
264
+ sidebarFooterActions: [],
265
+ threadLists: [],
266
+ threadHeaderActions: [],
267
+ fileOpeners: [],
268
+ sourceCodeRenderers: [],
269
+ diffRenderers: [],
270
+ messageDirectives: [],
271
+ messageActions: [],
272
+ providerIcons: [],
273
+ contentScripts: []
274
+ };
275
+ const seenIds = {
276
+ homepageSection: /* @__PURE__ */ new Set(),
277
+ settingsSection: /* @__PURE__ */ new Set(),
278
+ navPanel: /* @__PURE__ */ new Set(),
279
+ threadPanelAction: /* @__PURE__ */ new Set(),
280
+ newThreadPanelAction: /* @__PURE__ */ new Set(),
281
+ composerCustomization: /* @__PURE__ */ new Set(),
282
+ pendingInteraction: /* @__PURE__ */ new Set(),
283
+ sidebarFooterAction: /* @__PURE__ */ new Set(),
284
+ threadList: /* @__PURE__ */ new Set(),
285
+ threadHeaderAction: /* @__PURE__ */ new Set(),
286
+ fileOpener: /* @__PURE__ */ new Set(),
287
+ sourceCodeRenderer: /* @__PURE__ */ new Set(),
288
+ diffRenderer: /* @__PURE__ */ new Set(),
289
+ messageDirective: /* @__PURE__ */ new Set(),
290
+ messageAction: /* @__PURE__ */ new Set(),
291
+ providerIcon: /* @__PURE__ */ new Set(),
292
+ contentScript: /* @__PURE__ */ new Set()
293
+ };
294
+ definition.setup({
295
+ slots: {
296
+ homepageSection(registration) {
297
+ const kind = "slots.homepageSection";
298
+ const id = requireSlotId(kind, registration?.id);
299
+ requireUniqueId(kind, seenIds.homepageSection, id);
300
+ collected.homepageSections.push({
301
+ id,
302
+ title: requireNonEmptyString(kind, "title", registration.title),
303
+ component: requireComponent(kind, registration.component)
304
+ });
305
+ },
306
+ settingsSection(registration) {
307
+ const kind = "slots.settingsSection";
308
+ const id = requireSlotId(kind, registration?.id);
309
+ requireUniqueId(kind, seenIds.settingsSection, id);
310
+ const title = requireOptionalString(kind, "title", registration.title);
311
+ const description = requireOptionalString(
312
+ kind,
313
+ "description",
314
+ registration.description
315
+ );
316
+ collected.settingsSections.push({
317
+ id,
318
+ ...title !== void 0 ? { title } : {},
319
+ ...description !== void 0 ? { description } : {},
320
+ component: requireComponent(kind, registration.component)
321
+ });
322
+ },
323
+ navPanel(registration) {
324
+ const kind = "slots.navPanel";
325
+ const id = requireSlotId(kind, registration?.id);
326
+ requireUniqueId(kind, seenIds.navPanel, id);
327
+ const path = requireNonEmptyString(kind, "path", registration.path);
328
+ if (!PLUGIN_SLOT_ID_PATTERN.test(path)) {
329
+ throw new Error(
330
+ `${kind}: "path" must match ${String(PLUGIN_SLOT_ID_PATTERN)} (it becomes a URL segment), got ${JSON.stringify(path)}`
331
+ );
332
+ }
333
+ if (registration.headerContent !== void 0 && typeof registration.headerContent !== "function") {
334
+ throw new Error(
335
+ `${kind}: "headerContent" must be a React component function when set`
336
+ );
337
+ }
338
+ if (registration.experimental_sidebarAccessory !== void 0 && typeof registration.experimental_sidebarAccessory !== "function") {
339
+ throw new Error(
340
+ `${kind}: "experimental_sidebarAccessory" must be a React component function when set`
341
+ );
342
+ }
343
+ const experimentalFixedTabs = (() => {
344
+ if (registration.experimental_fixedTabs === void 0) return [];
345
+ if (!Array.isArray(registration.experimental_fixedTabs)) {
346
+ throw new Error(
347
+ `${kind}: "experimental_fixedTabs" must be an array when set`
348
+ );
349
+ }
350
+ const seenFixedTabIds = /* @__PURE__ */ new Set();
351
+ return registration.experimental_fixedTabs.map((value, index) => {
352
+ const fixedTabKind = `${kind}.experimental_fixedTabs[${index}]`;
353
+ const fixedTab = value;
354
+ const id2 = requireSlotId(fixedTabKind, fixedTab?.id);
355
+ requireUniqueId(fixedTabKind, seenFixedTabIds, id2);
356
+ const layout = fixedTab?.layout;
357
+ if (layout !== void 0 && layout !== "padded" && layout !== "flush") {
358
+ throw new Error(
359
+ `${fixedTabKind}: "layout" must be "padded" or "flush" when set`
360
+ );
361
+ }
362
+ return {
363
+ id: id2,
364
+ title: requireNonEmptyString(
365
+ fixedTabKind,
366
+ "title",
367
+ fixedTab?.title
368
+ ),
369
+ icon: requireNonEmptyString(
370
+ fixedTabKind,
371
+ "icon",
372
+ fixedTab?.icon
373
+ ),
374
+ component: requireComponent(fixedTabKind, fixedTab?.component),
375
+ ...layout === void 0 ? {} : { layout }
376
+ };
377
+ });
378
+ })();
379
+ collected.navPanels.push({
380
+ id,
381
+ title: requireNonEmptyString(kind, "title", registration.title),
382
+ icon: requireNonEmptyString(kind, "icon", registration.icon),
383
+ path,
384
+ component: requireComponent(kind, registration.component),
385
+ ...experimentalFixedTabs.length > 0 ? { experimental_fixedTabs: experimentalFixedTabs } : {},
386
+ ...registration.experimental_sidebarAccessory !== void 0 ? {
387
+ experimental_sidebarAccessory: registration.experimental_sidebarAccessory
388
+ } : {},
389
+ ...registration.headerContent !== void 0 ? { headerContent: registration.headerContent } : {}
390
+ });
391
+ },
392
+ threadPanelAction(registration) {
393
+ const kind = "slots.threadPanelAction";
394
+ const id = requireSlotId(kind, registration?.id);
395
+ requireUniqueId(kind, seenIds.threadPanelAction, id);
396
+ if (registration.run !== void 0 && typeof registration.run !== "function") {
397
+ throw new Error(`${kind}: "run" must be a function when set`);
398
+ }
399
+ if (registration.layout !== void 0 && registration.layout !== "padded" && registration.layout !== "flush") {
400
+ throw new Error(`${kind}: "layout" must be "padded" or "flush"`);
401
+ }
402
+ collected.threadPanelActions.push({
403
+ id,
404
+ title: requireNonEmptyString(kind, "title", registration.title),
405
+ ...registration.icon !== void 0 ? {
406
+ icon: requireNonEmptyString(kind, "icon", registration.icon)
407
+ } : {},
408
+ component: requireComponent(kind, registration.component),
409
+ ...registration.layout !== void 0 ? { layout: registration.layout } : {},
410
+ ...registration.run !== void 0 ? { run: registration.run } : {}
411
+ });
412
+ },
413
+ experimental_newThreadPanelAction(registration) {
414
+ const kind = "slots.experimental_newThreadPanelAction";
415
+ const id = requireSlotId(kind, registration?.id);
416
+ requireUniqueId(kind, seenIds.newThreadPanelAction, id);
417
+ if (registration.run !== void 0 && typeof registration.run !== "function") {
418
+ throw new Error(`${kind}: "run" must be a function when set`);
419
+ }
420
+ if (registration.layout !== void 0 && registration.layout !== "padded" && registration.layout !== "flush") {
421
+ throw new Error(`${kind}: "layout" must be "padded" or "flush"`);
422
+ }
423
+ collected.newThreadPanelActions.push({
424
+ id,
425
+ title: requireNonEmptyString(kind, "title", registration.title),
426
+ ...registration.icon !== void 0 ? {
427
+ icon: requireNonEmptyString(kind, "icon", registration.icon)
428
+ } : {},
429
+ component: requireComponent(kind, registration.component),
430
+ ...registration.layout !== void 0 ? { layout: registration.layout } : {},
431
+ ...registration.run !== void 0 ? { run: registration.run } : {}
432
+ });
433
+ },
434
+ pendingInteraction(registration) {
435
+ const kind = "slots.pendingInteraction";
436
+ const id = requireSlotId(kind, registration?.id);
437
+ requireUniqueId(kind, seenIds.pendingInteraction, id);
438
+ collected.pendingInteractions.push({
439
+ id,
440
+ component: requireComponent(kind, registration.component)
441
+ });
442
+ },
443
+ sidebarFooterAction(registration) {
444
+ const kind = "slots.sidebarFooterAction";
445
+ const id = requireSlotId(kind, registration?.id);
446
+ requireUniqueId(kind, seenIds.sidebarFooterAction, id);
447
+ if (typeof registration.run !== "function") {
448
+ throw new Error(`${kind}: "run" must be a function`);
449
+ }
450
+ collected.sidebarFooterActions.push({
451
+ id,
452
+ title: requireNonEmptyString(kind, "title", registration.title),
453
+ icon: requireNonEmptyString(kind, "icon", registration.icon),
454
+ run: registration.run
455
+ });
456
+ },
457
+ experimental_threadList(registration) {
458
+ const kind = "slots.experimental_threadList";
459
+ const id = requireSlotId(kind, registration?.id);
460
+ requireUniqueId(kind, seenIds.threadList, id);
461
+ const description = requireOptionalString(
462
+ kind,
463
+ "description",
464
+ registration.description
465
+ );
466
+ collected.threadLists.push({
467
+ id,
468
+ title: requireNonEmptyString(kind, "title", registration.title),
469
+ ...description !== void 0 ? { description } : {},
470
+ component: requireComponent(kind, registration.component)
471
+ });
472
+ },
473
+ experimental_threadHeaderAction(registration) {
474
+ const kind = "slots.experimental_threadHeaderAction";
475
+ const id = requireSlotId(kind, registration?.id);
476
+ requireUniqueId(kind, seenIds.threadHeaderAction, id);
477
+ collected.threadHeaderActions.push({
478
+ id,
479
+ title: requireNonEmptyString(kind, "title", registration.title),
480
+ component: requireComponent(kind, registration.component)
481
+ });
482
+ },
483
+ fileOpener(registration) {
484
+ const kind = "slots.fileOpener";
485
+ const id = requireSlotId(kind, registration?.id);
486
+ requireUniqueId(kind, seenIds.fileOpener, id);
487
+ const rawExtensions = registration?.extensions;
488
+ if (!Array.isArray(rawExtensions) || rawExtensions.length === 0) {
489
+ throw new Error(
490
+ `${kind}: "extensions" must be a non-empty array of lowercase extensions without the dot`
491
+ );
492
+ }
493
+ const extensions = rawExtensions.map((extension) => {
494
+ if (typeof extension !== "string" || !/^[a-z0-9]+$/.test(extension)) {
495
+ throw new Error(
496
+ `${kind}: extensions must be lowercase alphanumerics without the dot, got ${JSON.stringify(extension)}`
497
+ );
498
+ }
499
+ return extension;
500
+ });
501
+ collected.fileOpeners.push({
502
+ id,
503
+ title: requireNonEmptyString(kind, "title", registration.title),
504
+ extensions,
505
+ component: requireComponent(kind, registration.component)
506
+ });
507
+ },
508
+ experimental_sourceCodeRenderer(registration) {
509
+ const kind = "slots.experimental_sourceCodeRenderer";
510
+ const id = requireSlotId(kind, registration?.id);
511
+ requireUniqueId(kind, seenIds.sourceCodeRenderer, id);
512
+ const description = requireOptionalString(
513
+ kind,
514
+ "description",
515
+ registration.description
516
+ );
517
+ collected.sourceCodeRenderers.push({
518
+ id,
519
+ title: requireNonEmptyString(kind, "title", registration.title),
520
+ ...description !== void 0 ? { description } : {},
521
+ component: requireComponent(kind, registration.component)
522
+ });
523
+ },
524
+ experimental_diffRenderer(registration) {
525
+ const kind = "slots.experimental_diffRenderer";
526
+ const id = requireSlotId(kind, registration?.id);
527
+ requireUniqueId(kind, seenIds.diffRenderer, id);
528
+ const description = requireOptionalString(
529
+ kind,
530
+ "description",
531
+ registration.description
532
+ );
533
+ collected.diffRenderers.push({
534
+ id,
535
+ title: requireNonEmptyString(kind, "title", registration.title),
536
+ ...description !== void 0 ? { description } : {},
537
+ component: requireComponent(kind, registration.component)
538
+ });
539
+ },
540
+ messageDirective(registration) {
541
+ const kind = "slots.messageDirective";
542
+ const id = requireMessageDirectiveId(kind, registration?.id);
543
+ requireUniqueId(kind, seenIds.messageDirective, id);
544
+ collected.messageDirectives.push({
545
+ id,
546
+ component: requireComponent(kind, registration.component)
547
+ });
548
+ },
549
+ messageAction(registration) {
550
+ const kind = "slots.messageAction";
551
+ const id = requireSlotId(kind, registration?.id);
552
+ requireUniqueId(kind, seenIds.messageAction, id);
553
+ if (typeof registration.run !== "function") {
554
+ throw new Error(`${kind}: "run" must be a function`);
555
+ }
556
+ collected.messageActions.push({
557
+ id,
558
+ title: requireNonEmptyString(kind, "title", registration.title),
559
+ ...registration.icon !== void 0 ? {
560
+ icon: requireNonEmptyString(kind, "icon", registration.icon)
561
+ } : {},
562
+ run: registration.run
563
+ });
564
+ },
565
+ experimental_providerIcon(registration) {
566
+ const kind = "slots.experimental_providerIcon";
567
+ const providerId = requireProviderId(kind, registration?.providerId);
568
+ requireUniqueId(kind, seenIds.providerIcon, providerId);
569
+ collected.providerIcons.push({
570
+ providerId,
571
+ icon: requireComponent(kind, registration.icon)
572
+ });
573
+ }
574
+ },
575
+ composer: {
576
+ customize(registration) {
577
+ const customization = collectComposerCustomization(
578
+ registration,
579
+ seenIds.composerCustomization,
580
+ onComposerCustomizationRejected
581
+ );
582
+ if (customization !== null) {
583
+ collected.composerCustomizations.push(customization);
584
+ }
585
+ }
586
+ },
587
+ contentScripts: {
588
+ register(registration) {
589
+ const kind = "contentScripts.register";
590
+ const id = requireSlotId(kind, registration?.id);
591
+ requireUniqueId(kind, seenIds.contentScript, id);
592
+ if (typeof registration.mount !== "function") {
593
+ throw new Error(`${kind}: "mount" must be a function`);
594
+ }
595
+ collected.contentScripts.push({ id, mount: registration.mount });
596
+ }
597
+ }
598
+ });
599
+ return collected;
600
+ }
601
+
246
602
  // src/testing/app.tsx
247
603
  import { jsx, jsxs } from "react/jsx-runtime";
248
604
  function SlotLifecycleGuard({
@@ -390,6 +746,46 @@ function TestNewThreadComposer({
390
746
  }
391
747
  );
392
748
  }
749
+ function TestSourceCode({
750
+ content,
751
+ path,
752
+ overflow = "scroll",
753
+ highlightedLines = null,
754
+ className
755
+ }) {
756
+ return /* @__PURE__ */ jsx(
757
+ "pre",
758
+ {
759
+ "data-testid": "bb-source-code",
760
+ "data-path": path,
761
+ "data-overflow": overflow,
762
+ "data-highlighted-lines": highlightedLines === null ? "" : `${highlightedLines.start}-${highlightedLines.end}`,
763
+ className,
764
+ children: content
765
+ }
766
+ );
767
+ }
768
+ function TestDiff({
769
+ patch,
770
+ path,
771
+ view = "unified",
772
+ overflow = "scroll",
773
+ showLineNumbers = true,
774
+ className
775
+ }) {
776
+ return /* @__PURE__ */ jsx(
777
+ "pre",
778
+ {
779
+ "data-testid": "bb-diff",
780
+ "data-path": path,
781
+ "data-view": view,
782
+ "data-overflow": overflow,
783
+ "data-show-line-numbers": showLineNumbers ? "true" : "false",
784
+ className,
785
+ children: patch
786
+ }
787
+ );
788
+ }
393
789
  var testPluginSdkApp = {
394
790
  definePluginApp,
395
791
  useRpc() {
@@ -452,6 +848,8 @@ var testPluginSdkApp = {
452
848
  ThreadChat: TestThreadChat,
453
849
  Markdown: TestMarkdown,
454
850
  experimental_NewThreadComposer: TestNewThreadComposer,
851
+ experimental_SourceCode: TestSourceCode,
852
+ experimental_Diff: TestDiff,
455
853
  experimental_useSidebarThreads() {
456
854
  return useSlotEnv("experimental_useSidebarThreads").sidebarThreads;
457
855
  },
@@ -513,262 +911,6 @@ function installTestPluginRuntime() {
513
911
  pluginSdkApp: testPluginSdkApp
514
912
  };
515
913
  }
516
- function collectRegistrations(definition) {
517
- const captured = {
518
- homepageSections: [],
519
- settingsSections: [],
520
- navPanels: [],
521
- threadPanelActions: [],
522
- newThreadPanelActions: [],
523
- composerCustomizations: [],
524
- pendingInteractions: [],
525
- sidebarFooterActions: [],
526
- threadLists: [],
527
- threadHeaderActions: [],
528
- fileOpeners: [],
529
- messageDirectives: [],
530
- messageActions: [],
531
- contentScripts: []
532
- };
533
- const seenIds = {
534
- homepageSection: /* @__PURE__ */ new Set(),
535
- settingsSection: /* @__PURE__ */ new Set(),
536
- navPanel: /* @__PURE__ */ new Set(),
537
- threadPanelAction: /* @__PURE__ */ new Set(),
538
- newThreadPanelAction: /* @__PURE__ */ new Set(),
539
- composerCustomization: /* @__PURE__ */ new Set(),
540
- pendingInteraction: /* @__PURE__ */ new Set(),
541
- sidebarFooterAction: /* @__PURE__ */ new Set(),
542
- threadList: /* @__PURE__ */ new Set(),
543
- threadHeaderAction: /* @__PURE__ */ new Set(),
544
- fileOpener: /* @__PURE__ */ new Set(),
545
- messageDirective: /* @__PURE__ */ new Set(),
546
- messageAction: /* @__PURE__ */ new Set(),
547
- contentScript: /* @__PURE__ */ new Set()
548
- };
549
- definition.setup({
550
- slots: {
551
- homepageSection(registration) {
552
- const kind = "slots.homepageSection";
553
- const id = requireSlotId(kind, registration?.id);
554
- requireUniqueId(kind, seenIds.homepageSection, id);
555
- captured.homepageSections.push({
556
- id,
557
- title: requireNonEmptyString(kind, "title", registration.title),
558
- component: requireComponent(kind, registration.component)
559
- });
560
- },
561
- settingsSection(registration) {
562
- const kind = "slots.settingsSection";
563
- const id = requireSlotId(kind, registration?.id);
564
- requireUniqueId(kind, seenIds.settingsSection, id);
565
- const title = requireOptionalString(kind, "title", registration.title);
566
- const description = requireOptionalString(
567
- kind,
568
- "description",
569
- registration.description
570
- );
571
- captured.settingsSections.push({
572
- id,
573
- ...title !== void 0 ? { title } : {},
574
- ...description !== void 0 ? { description } : {},
575
- component: requireComponent(kind, registration.component)
576
- });
577
- },
578
- navPanel(registration) {
579
- const kind = "slots.navPanel";
580
- const id = requireSlotId(kind, registration?.id);
581
- requireUniqueId(kind, seenIds.navPanel, id);
582
- const path = requireNonEmptyString(kind, "path", registration.path);
583
- if (!PLUGIN_SLOT_ID_PATTERN.test(path)) {
584
- throw new Error(
585
- `${kind}: "path" must match ${String(PLUGIN_SLOT_ID_PATTERN)} (it becomes a URL segment), got ${JSON.stringify(path)}`
586
- );
587
- }
588
- if (registration.headerContent !== void 0 && typeof registration.headerContent !== "function") {
589
- throw new Error(
590
- `${kind}: "headerContent" must be a React component function when set`
591
- );
592
- }
593
- if (registration.experimental_sidebarAccessory !== void 0 && typeof registration.experimental_sidebarAccessory !== "function") {
594
- throw new Error(
595
- `${kind}: "experimental_sidebarAccessory" must be a React component function when set`
596
- );
597
- }
598
- captured.navPanels.push({
599
- id,
600
- title: requireNonEmptyString(kind, "title", registration.title),
601
- icon: requireNonEmptyString(kind, "icon", registration.icon),
602
- path,
603
- component: requireComponent(kind, registration.component),
604
- ...registration.experimental_sidebarAccessory !== void 0 ? {
605
- experimental_sidebarAccessory: registration.experimental_sidebarAccessory
606
- } : {},
607
- ...registration.headerContent !== void 0 ? { headerContent: registration.headerContent } : {}
608
- });
609
- },
610
- threadPanelAction(registration) {
611
- const kind = "slots.threadPanelAction";
612
- const id = requireSlotId(kind, registration?.id);
613
- requireUniqueId(kind, seenIds.threadPanelAction, id);
614
- if (registration.run !== void 0 && typeof registration.run !== "function") {
615
- throw new Error(`${kind}: "run" must be a function when set`);
616
- }
617
- if (registration.layout !== void 0 && registration.layout !== "padded" && registration.layout !== "flush") {
618
- throw new Error(`${kind}: "layout" must be "padded" or "flush"`);
619
- }
620
- captured.threadPanelActions.push({
621
- id,
622
- title: requireNonEmptyString(kind, "title", registration.title),
623
- ...registration.icon !== void 0 ? { icon: requireNonEmptyString(kind, "icon", registration.icon) } : {},
624
- component: requireComponent(kind, registration.component),
625
- ...registration.layout !== void 0 ? { layout: registration.layout } : {},
626
- ...registration.run !== void 0 ? { run: registration.run } : {}
627
- });
628
- },
629
- experimental_newThreadPanelAction(registration) {
630
- const kind = "slots.experimental_newThreadPanelAction";
631
- const id = requireSlotId(kind, registration?.id);
632
- requireUniqueId(kind, seenIds.newThreadPanelAction, id);
633
- if (registration.run !== void 0 && typeof registration.run !== "function") {
634
- throw new Error(`${kind}: "run" must be a function when set`);
635
- }
636
- if (registration.layout !== void 0 && registration.layout !== "padded" && registration.layout !== "flush") {
637
- throw new Error(`${kind}: "layout" must be "padded" or "flush"`);
638
- }
639
- captured.newThreadPanelActions.push({
640
- id,
641
- title: requireNonEmptyString(kind, "title", registration.title),
642
- ...registration.icon !== void 0 ? { icon: requireNonEmptyString(kind, "icon", registration.icon) } : {},
643
- component: requireComponent(kind, registration.component),
644
- ...registration.layout !== void 0 ? { layout: registration.layout } : {},
645
- ...registration.run !== void 0 ? { run: registration.run } : {}
646
- });
647
- },
648
- pendingInteraction(registration) {
649
- const kind = "slots.pendingInteraction";
650
- const id = requireSlotId(kind, registration?.id);
651
- requireUniqueId(kind, seenIds.pendingInteraction, id);
652
- captured.pendingInteractions.push({
653
- id,
654
- component: requireComponent(kind, registration.component)
655
- });
656
- },
657
- sidebarFooterAction(registration) {
658
- const kind = "slots.sidebarFooterAction";
659
- const id = requireSlotId(kind, registration?.id);
660
- requireUniqueId(kind, seenIds.sidebarFooterAction, id);
661
- if (typeof registration.run !== "function") {
662
- throw new Error(`${kind}: "run" must be a function`);
663
- }
664
- captured.sidebarFooterActions.push({
665
- id,
666
- title: requireNonEmptyString(kind, "title", registration.title),
667
- icon: requireNonEmptyString(kind, "icon", registration.icon),
668
- run: registration.run
669
- });
670
- },
671
- experimental_threadList(registration) {
672
- const kind = "slots.experimental_threadList";
673
- const id = requireSlotId(kind, registration?.id);
674
- requireUniqueId(kind, seenIds.threadList, id);
675
- const description = requireOptionalString(
676
- kind,
677
- "description",
678
- registration.description
679
- );
680
- captured.threadLists.push({
681
- id,
682
- title: requireNonEmptyString(kind, "title", registration.title),
683
- ...description !== void 0 ? { description } : {},
684
- component: requireComponent(kind, registration.component)
685
- });
686
- },
687
- experimental_threadHeaderAction(registration) {
688
- const kind = "slots.experimental_threadHeaderAction";
689
- const id = requireSlotId(kind, registration?.id);
690
- requireUniqueId(kind, seenIds.threadHeaderAction, id);
691
- captured.threadHeaderActions.push({
692
- id,
693
- title: requireNonEmptyString(kind, "title", registration.title),
694
- component: requireComponent(kind, registration.component)
695
- });
696
- },
697
- fileOpener(registration) {
698
- const kind = "slots.fileOpener";
699
- const id = requireSlotId(kind, registration?.id);
700
- requireUniqueId(kind, seenIds.fileOpener, id);
701
- const rawExtensions = registration?.extensions;
702
- if (!Array.isArray(rawExtensions) || rawExtensions.length === 0) {
703
- throw new Error(
704
- `${kind}: "extensions" must be a non-empty array of lowercase extensions without the dot`
705
- );
706
- }
707
- const extensions = rawExtensions.map((extension) => {
708
- if (typeof extension !== "string" || !/^[a-z0-9]+$/.test(extension)) {
709
- throw new Error(
710
- `${kind}: extensions must be lowercase alphanumerics without the dot, got ${JSON.stringify(extension)}`
711
- );
712
- }
713
- return extension;
714
- });
715
- captured.fileOpeners.push({
716
- id,
717
- title: requireNonEmptyString(kind, "title", registration.title),
718
- extensions,
719
- component: requireComponent(kind, registration.component)
720
- });
721
- },
722
- messageDirective(registration) {
723
- const kind = "slots.messageDirective";
724
- const id = requireMessageDirectiveId(kind, registration?.id);
725
- requireUniqueId(kind, seenIds.messageDirective, id);
726
- captured.messageDirectives.push({
727
- id,
728
- component: requireComponent(kind, registration.component)
729
- });
730
- },
731
- messageAction(registration) {
732
- const kind = "slots.messageAction";
733
- const id = requireSlotId(kind, registration?.id);
734
- requireUniqueId(kind, seenIds.messageAction, id);
735
- if (typeof registration.run !== "function") {
736
- throw new Error(`${kind}: "run" must be a function`);
737
- }
738
- captured.messageActions.push({
739
- id,
740
- title: requireNonEmptyString(kind, "title", registration.title),
741
- ...registration.icon !== void 0 ? { icon: requireNonEmptyString(kind, "icon", registration.icon) } : {},
742
- run: registration.run
743
- });
744
- }
745
- },
746
- composer: {
747
- customize(registration) {
748
- const customization = collectComposerCustomization(
749
- registration,
750
- seenIds.composerCustomization,
751
- (reason) => console.warn(reason)
752
- );
753
- if (customization !== null) {
754
- captured.composerCustomizations.push(customization);
755
- }
756
- }
757
- },
758
- contentScripts: {
759
- register(registration) {
760
- const kind = "contentScripts.register";
761
- const id = requireSlotId(kind, registration?.id);
762
- requireUniqueId(kind, seenIds.contentScript, id);
763
- if (typeof registration.mount !== "function") {
764
- throw new Error(`${kind}: "mount" must be a function`);
765
- }
766
- captured.contentScripts.push({ id, mount: registration.mount });
767
- }
768
- }
769
- });
770
- return captured;
771
- }
772
914
  async function loadPluginApp(source) {
773
915
  installTestPluginRuntime();
774
916
  const resolved = typeof source === "function" ? await source() : source;
@@ -778,7 +920,7 @@ async function loadPluginApp(source) {
778
920
  "the bundle's default export is not definePluginApp(...) from @get-bb/plugin-sdk/app"
779
921
  );
780
922
  }
781
- return collectRegistrations(definition);
923
+ return collectPluginAppRegistrations(definition);
782
924
  }
783
925
  async function mountPluginContentScripts(app, options) {
784
926
  const controller = new AbortController();