@get-bb/plugin-sdk 0.4.3

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 ADDED
@@ -0,0 +1,141 @@
1
+ # @get-bb/plugin-sdk
2
+
3
+ The typed facade BB plugin authors compile against. The root preserves the
4
+ complete `BbPluginApi` and `BbSdk` contract; `./app` is the frontend runtime
5
+ that `bb plugin build` replaces with BB's shared implementation.
6
+
7
+ The authoritative contracts are the exported declarations in
8
+ [`src/backend-contract.ts`](src/backend-contract.ts) and
9
+ [`src/app-contract.ts`](src/app-contract.ts). Keep author-facing guidance in
10
+ the built-in `bb-plugin-authoring` skill synchronized with those declarations.
11
+
12
+ ## Composer customization
13
+
14
+ Composer UI extensions register through `app.composer.customize(...)`. A
15
+ `ComposerCustomization` can contribute React action and banner components,
16
+ host-rendered `ComposerPlusMenuItem` rows, and `ComposerRichTextSpec` rules.
17
+ Mounted components use `useComposer()` for writes, effects, and input locking,
18
+ and `useComposerView()` for the reactive scope, layout, draft, and run state.
19
+ Any mounted plugin component can use
20
+ `useBbNavigate().openThreadPanel(...)` to request one of the
21
+ same plugin's registered thread-panel actions; it returns false when the
22
+ current surface has no thread side panel.
23
+
24
+ See the
25
+ [`composer-customization` reference plugin](../../examples/plugins/composer-customization/README.md)
26
+ for every region in one small app. The deprecated pre-1.0
27
+ `app.slots.composerAccessory(...)` footer API has been removed; migrate footer
28
+ controls to actions or the plus menu and larger content to banners.
29
+
30
+ ## Trusted frontend content scripts
31
+
32
+ Use `app.contentScripts.register({ id, mount })` for ordinary
33
+ bundled TypeScript/JavaScript that enhances the bb app shell without rendering
34
+ a React slot. The host supplies `{ pluginId, generation, signal }`, awaits
35
+ mount setup, and owns abort plus exact-once reverse-order disposal across hash
36
+ reload, disable, removal, failed replacement, and app-window teardown. The old
37
+ generation is disposed before candidate mounts, so generations never overlap.
38
+ Content scripts are trusted same-origin page code, not a sandbox.
39
+
40
+ Static styles should stay in the normal imported `app.css`; scripts may own
41
+ dynamic DOM/style nodes when their disposer removes them. See the
42
+ [`content-script` reference plugin](../../examples/plugins/content-script/README.md)
43
+ for a cleanup-safe editor enhancement.
44
+
45
+ ## External plugin tests
46
+
47
+ The packed package includes executable JavaScript and portable declarations
48
+ for `@get-bb/plugin-sdk/testing` and `@get-bb/plugin-sdk/testing/app`; neither subpath
49
+ imports BB workspace packages or source TypeScript at runtime. Install the SDK
50
+ with the test stack used by your plugin (the peer dependencies are optional so
51
+ headless plugins do not install a browser harness):
52
+
53
+ ```sh
54
+ npm install --save-dev @get-bb/plugin-sdk vitest better-sqlite3 zod cron-parser hono
55
+ npm install --save-dev react react-dom @testing-library/react jsdom # frontend tests
56
+ ```
57
+
58
+ Backend example:
59
+
60
+ ```ts
61
+ import { createFakePluginHost } from "@get-bb/plugin-sdk/testing";
62
+ import plugin from "./server.js";
63
+
64
+ const host = createFakePluginHost({ pluginId: "notes" });
65
+ await plugin(host.bb);
66
+
67
+ await host.harness.behavior.callRpc("list", { query: "today" });
68
+ expect(host.harness.inspection.registrations.rpcMethods).toContain("list");
69
+ await host.harness.lifecycle.dispose();
70
+ ```
71
+
72
+ `harness.behavior` contains deterministic host inputs (RPC/HTTP/CLI calls,
73
+ events, settings, tools, interactions, and schedules), `harness.inspection`
74
+ contains registrations and recorded state, and `harness.lifecycle` owns atomic
75
+ reload and disposal. Every pre-existing direct member remains as an alias for
76
+ source compatibility. A successful `reload(factory)` preserves settings, KV,
77
+ and database state and invalidates the old API only after the replacement
78
+ factory succeeds; a failed factory leaves the old load live.
79
+
80
+ Frontend example (`// @vitest-environment jsdom`):
81
+
82
+ ```tsx
83
+ import {
84
+ loadPluginApp,
85
+ mountPluginContentScripts,
86
+ renderSlot,
87
+ } from "@get-bb/plugin-sdk/testing/app";
88
+
89
+ const app = await loadPluginApp(() => import("./app.js"));
90
+ const scripts = await mountPluginContentScripts(app, { pluginId: "notes" });
91
+ const slot = renderSlot(
92
+ app.homepageSections[0]!,
93
+ { projectId: "proj_1" },
94
+ {
95
+ rpc: { list: () => [] },
96
+ context: { projectId: "proj_1", threadId: null },
97
+ },
98
+ );
99
+
100
+ await slot.behavior.emitRealtime("notes-changed", null);
101
+ expect(slot.inspection.rpcCalls).toHaveLength(1);
102
+ slot.lifecycle.unmount();
103
+ await scripts.lifecycle.dispose();
104
+ ```
105
+
106
+ `loadPluginApp` installs the runtime before a thunk import and validates all
107
+ registrations. `mountPluginContentScripts` mirrors the host's ordered mount,
108
+ rollback, independent per-window signal, and exact-once disposal. `renderSlot` supplies
109
+ RPC, realtime, settings, navigation, context, and scoped composer behavior,
110
+ then returns Testing Library queries plus the same behavior/inspection/lifecycle
111
+ split. Use a setup-file `installTestPluginRuntime()` only when a static app
112
+ import is unavoidable.
113
+
114
+ ## Fidelity boundaries
115
+
116
+ The backend fake matches observable schema-RPC validation/errors and strict
117
+ JSON results, additive events, keyed-registration failures, atomic reload,
118
+ settings, KV/database storage, conditional agent configuration, request input,
119
+ and disposal order. HTTP runs through Hono but does not enforce BB's local or
120
+ token authentication. Background services and schedules run only when driven;
121
+ there are no restart timers or cron sweeps. Storage is process-local in a
122
+ temporary directory, secrets are kept in memory, `bb.sdk` is always bound and
123
+ unstubbed calls throw, and cross-plugin/global collision policy is outside one
124
+ fake host.
125
+
126
+ The frontend harness matches registration validation, content-script mount and
127
+ cleanup ordering, RPC/realtime JSON
128
+ boundaries, panel and slot props, navigation recording, and composer text,
129
+ scope, quote, mention, focus, and clear behavior. It does not reproduce BB
130
+ layout, CSS, persistence, routing, host authentication, crash boundaries, or
131
+ multi-plugin arbitration; use a live BB test for those boundaries.
132
+
133
+ ## Declaration surface
134
+
135
+ The complete root declaration flattens the unpublished BB workspace contracts.
136
+ The testing declarations reuse that public `@get-bb/plugin-sdk` root instead of
137
+ embedding a second copy, and no declaration depends on unpublished `@bb/*`
138
+ packages. Genuine npm types (`hono`, `better-sqlite3`, `zod`, React, and Testing
139
+ Library) remain peer imports. Scaffolded plugins still vendor the root/app
140
+ declarations in `types/`; installing this package is needed only when their
141
+ tests import the testing subpaths.