@makinbakin/sdk 0.0.1-rc.2 → 0.0.1-rc.21
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 +1 -0
- package/_internal/app/components/agent-select.d.ts +16 -2
- package/_internal/app/components/asset-picker.d.ts +25 -0
- package/_internal/app/components/charts/bar-chart.d.ts +16 -0
- package/_internal/app/components/charts/chart-data-table.d.ts +31 -0
- package/_internal/app/components/charts/chart-explainer.d.ts +9 -0
- package/_internal/app/components/charts/chart-tooltip.d.ts +8 -0
- package/_internal/app/components/charts/line-chart.d.ts +13 -0
- package/_internal/app/components/charts/palette.d.ts +24 -0
- package/_internal/app/components/charts/sparkline.d.ts +12 -0
- package/_internal/app/components/charts/stacked-column-chart.d.ts +40 -0
- package/_internal/app/components/confirm-dialog.d.ts +54 -0
- package/_internal/app/components/conversation/activity-group.d.ts +15 -0
- package/_internal/app/components/conversation/agent-turn.d.ts +36 -0
- package/_internal/app/components/conversation/attention.d.ts +56 -0
- package/_internal/app/components/conversation/composer.d.ts +35 -0
- package/_internal/app/components/conversation/conversation-empty-state.d.ts +8 -0
- package/_internal/app/components/conversation/conversation-panel.d.ts +33 -0
- package/_internal/app/components/conversation/conversation.d.ts +14 -0
- package/_internal/app/components/conversation/fold.d.ts +129 -0
- package/_internal/app/components/conversation/notification-sound.d.ts +8 -0
- package/_internal/app/components/conversation/relative-time.d.ts +8 -0
- package/_internal/app/components/conversation/reply-toast.d.ts +15 -0
- package/_internal/app/components/conversation/thread-id.d.ts +8 -0
- package/_internal/app/components/conversation/tool-call-drawer.d.ts +7 -0
- package/_internal/app/components/conversation/turn-recorder.d.ts +37 -0
- package/_internal/app/components/conversation/use-conversation-attention.d.ts +64 -0
- package/_internal/app/components/conversation/use-conversation-thread.d.ts +55 -0
- package/_internal/app/components/conversation/user-message.d.ts +11 -0
- package/_internal/app/components/danger-zone.d.ts +28 -0
- package/_internal/app/components/empty-state.d.ts +18 -6
- package/_internal/app/components/plugin-header.d.ts +4 -3
- package/_internal/app/components/save-bar.d.ts +32 -0
- package/_internal/app/components/score-overlay.d.ts +33 -0
- package/_internal/app/components/search-degraded-chip.d.ts +4 -0
- package/_internal/app/components/search-partial-chip.d.ts +13 -0
- package/_internal/app/components/search-unavailable.d.ts +12 -0
- package/_internal/app/components/section-card.d.ts +22 -0
- package/_internal/app/components/segmented-control.d.ts +28 -0
- package/_internal/app/components/stat-tile.d.ts +26 -0
- package/_internal/app/components/status-badge.d.ts +19 -0
- package/_internal/app/components/turn-output-view.d.ts +57 -0
- package/_internal/app/components/ui/badge.d.ts +1 -1
- package/_internal/app/components/ui/button.d.ts +1 -1
- package/_internal/app/components/underline-tabs.d.ts +6 -2
- package/_internal/app/components/unsaved-changes-guard.d.ts +34 -0
- package/_internal/app/core/conversation-turns.d.ts +148 -0
- package/_internal/app/core/logger.d.ts +1 -0
- package/_internal/app/hooks/use-available-models.d.ts +5 -0
- package/_internal/app/hooks/use-content-store.d.ts +0 -9
- package/_internal/app/hooks/use-file-drop.d.ts +25 -0
- package/_internal/app/hooks/use-history-back.d.ts +1 -0
- package/_internal/app/hooks/use-horizontal-resize.d.ts +22 -0
- package/_internal/app/hooks/use-json-fetch.d.ts +26 -0
- package/_internal/app/hooks/use-nav-badge.d.ts +13 -0
- package/_internal/app/hooks/use-plugin-event.d.ts +19 -0
- package/_internal/app/hooks/use-query-state.d.ts +2 -0
- package/_internal/app/hooks/use-resizable-pane.d.ts +35 -0
- package/_internal/app/hooks/use-schedule.d.ts +47 -1
- package/_internal/app/hooks/use-search.d.ts +27 -8
- package/_internal/app/hooks/use-task-run-history.d.ts +26 -0
- package/_internal/app/hooks/use-toast.d.ts +3 -3
- package/_internal/app/hooks/use-vertical-resize.d.ts +6 -7
- package/_internal/app/lib/browser-notify.d.ts +12 -1
- package/_internal/app/types/index.d.ts +1 -105
- package/_internal/core/adapters/runtime/concepts.d.ts +671 -99
- package/_internal/core/adapters/runtime/errors.d.ts +98 -0
- package/_internal/core/adapters/runtime/helpers.d.ts +19 -0
- package/_internal/core/adapters/runtime/index.d.ts +7 -2
- package/_internal/core/adapters/runtime/testing.d.ts +79 -0
- package/_internal/core/adapters/runtime/turn-activity.d.ts +23 -0
- package/_internal/core/adapters/shared.d.ts +122 -12
- package/_internal/core/content-dir.d.ts +9 -0
- package/_internal/core/docs/route.d.ts +1 -1
- package/_internal/core/format/structured.d.ts +44 -0
- package/_internal/core/format.d.ts +13 -0
- package/_internal/core/media/downscale.d.ts +18 -0
- package/_internal/core/media/sharp-loader.d.ts +36 -0
- package/_internal/core/plugin-types.d.ts +216 -499
- package/_internal/core/routing/define.d.ts +26 -3
- package/_internal/core/routing/dispatcher.d.ts +15 -6
- package/_internal/core/routing/index.d.ts +1 -1
- package/_internal/core/routing/types.d.ts +13 -70
- package/_internal/core/storage/atomic-write.d.ts +11 -0
- package/_internal/core/tasks/store.d.ts +129 -0
- package/_internal/core/tasks/testing.d.ts +2 -0
- package/_internal/{plugins/workflows/lib → core/workflows}/notification-channel-registry.d.ts +1 -1
- package/_internal/plugins/team/types.d.ts +61 -4
- package/_internal/plugins/workflows/hooks/use-notification-channels.d.ts +1 -1
- package/components/index.d.ts +111 -5
- package/components/index.js +61815 -30060
- package/hooks/index.d.ts +90 -9
- package/hooks/index.js +15181 -685
- package/hooks/router.d.ts +20 -4
- package/index.d.ts +20 -3
- package/index.js +297 -259
- package/internal/index.d.ts +70 -0
- package/internal/index.js +409 -0
- package/metadata/index.d.ts +14 -1
- package/package.json +9 -2
- package/register.d.ts +63 -9
- package/routing/index.d.ts +6 -1
- package/routing/index.js +187 -147
- package/slots/index.d.ts +16 -24
- package/slots/index.js +215 -30
- package/testing/index.d.ts +122 -0
- package/testing/index.js +21874 -0
- package/types/index.d.ts +19 -919
- package/types/index.js +16 -0
- package/ui/index.js +2 -1
- package/utils/index.d.ts +46 -6
- package/utils/index.js +322 -249
- package/_internal/app/components/integrated-brainstorm/activity.d.ts +0 -23
- package/_internal/app/components/integrated-brainstorm/collapsed-header.d.ts +0 -14
- package/_internal/app/components/integrated-brainstorm/empty-state.d.ts +0 -5
- package/_internal/app/components/integrated-brainstorm/index.d.ts +0 -8
- package/_internal/app/components/integrated-brainstorm/input-row.d.ts +0 -16
- package/_internal/app/components/integrated-brainstorm/message-list.d.ts +0 -8
- package/_internal/app/components/integrated-brainstorm/session.d.ts +0 -16
- package/_internal/app/components/integrated-brainstorm/sse.d.ts +0 -8
- package/_internal/app/components/integrated-brainstorm/thinking-indicator.d.ts +0 -19
- package/_internal/app/components/integrated-brainstorm/types.d.ts +0 -63
- package/_internal/app/components/integrated-brainstorm/use-auto-grow.d.ts +0 -6
- package/_internal/app/components/integrated-brainstorm/use-brainstorm-state.d.ts +0 -21
- package/_internal/app/hooks/use-assets.d.ts +0 -25
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@makinbakin/sdk/internal` — host-shell plumbing. NOT plugin-author API.
|
|
3
|
+
*
|
|
4
|
+
* Everything here exists so the Bakin host shell (PluginHost, sidebar,
|
|
5
|
+
* router catch-all, ⌘K overlay, drift checker) can drive the shared
|
|
6
|
+
* client-side registries that `registerPlugin` populates. Plugin authors
|
|
7
|
+
* never import this entry; the author surface is the root barrel plus the
|
|
8
|
+
* documented sub-paths. Excluded from the generated SDK reference docs.
|
|
9
|
+
*
|
|
10
|
+
* Breaking changes here only ever affect the host shell, which ships in
|
|
11
|
+
* lockstep with the SDK — there is no compatibility contract on this entry.
|
|
12
|
+
*/
|
|
13
|
+
/** Tear down all registrations owned by a plugin (dev hot-swap teardown). */
|
|
14
|
+
export { unregisterPlugin } from '../register';
|
|
15
|
+
/** Current registry version — bumps on every mutation (for useSyncExternalStore). */
|
|
16
|
+
export { getRegistryVersion } from '../register';
|
|
17
|
+
/** Subscribe to registry-version changes. */
|
|
18
|
+
export { subscribeRegistry } from '../register';
|
|
19
|
+
/** Get every registered nav item across all plugins. */
|
|
20
|
+
export { getAllNavItems } from '../register';
|
|
21
|
+
/** Get a snapshot of the current nav items (non-subscribing). */
|
|
22
|
+
export { getNavItemsSnapshot } from '../register';
|
|
23
|
+
/** Get nav items contributed by a specific plugin. */
|
|
24
|
+
export { getPluginNavItems } from '../register';
|
|
25
|
+
/** Stable snapshot of every active nav badge keyed by navItemId. */
|
|
26
|
+
export { getNavBadgesSnapshot } from '../register';
|
|
27
|
+
/** Subscribe to nav-badge mutations (separate channel from `subscribeRegistry`). */
|
|
28
|
+
export { subscribeNavBadges } from '../register';
|
|
29
|
+
/** Look up a specific client route by plugin id + path. */
|
|
30
|
+
export { getPluginRoute } from '../register';
|
|
31
|
+
/** Get all registered client routes (across all plugins). */
|
|
32
|
+
export { getPluginRoutes } from '../register';
|
|
33
|
+
/** Seed a plugin's declarative nav from its manifest (survives unregisterPlugin). */
|
|
34
|
+
export { setManifestNav } from '../register';
|
|
35
|
+
/** Read the manifest nav currently seeded for a plugin (drift validation). */
|
|
36
|
+
export { getManifestNav } from '../register';
|
|
37
|
+
/** Look up the ⌘K hit renderer registered for a content type. */
|
|
38
|
+
export { getSearchHitRenderer } from '../register';
|
|
39
|
+
/** Stable snapshot of all registered hit renderers keyed by content type. */
|
|
40
|
+
export { getSearchHitRenderersSnapshot } from '../register';
|
|
41
|
+
/** Subscribe to hit-renderer mutations (own channel). */
|
|
42
|
+
export { subscribeSearchHitRenderers } from '../register';
|
|
43
|
+
export type { ClientRouteEntry, MatchedPluginRoute } from '../register';
|
|
44
|
+
/** Install the manifest-derived slot/route ownership index for lazy loading. */
|
|
45
|
+
export { configureLazyPlugins } from '../lazy/index';
|
|
46
|
+
/** Install the demand loader that imports a plugin's client bundle. */
|
|
47
|
+
export { setLazyPluginLoader } from '../lazy/index';
|
|
48
|
+
/** Report a plugin client's load progress: idle → loading → loaded | error. */
|
|
49
|
+
export { setPluginLoadState } from '../lazy/index';
|
|
50
|
+
/** Current load state for a plugin client. Unknown plugins report 'idle'. */
|
|
51
|
+
export { getPluginLoadState } from '../lazy/index';
|
|
52
|
+
/** Last load error message for a plugin whose state is 'error', if any. */
|
|
53
|
+
export { getPluginLoadError } from '../lazy/index';
|
|
54
|
+
/** Plugins whose manifests declare the given slot in `contributes.slots`. */
|
|
55
|
+
export { getSlotOwners } from '../lazy/index';
|
|
56
|
+
/** Plugins whose manifest `contributes.routes` patterns match a pathname. */
|
|
57
|
+
export { getRouteOwners } from '../lazy/index';
|
|
58
|
+
/** Ask the host to lazy-load every idle plugin that fills the named slot. */
|
|
59
|
+
export { requestSlotPlugins } from '../lazy/index';
|
|
60
|
+
/** Ask the host to lazy-load every idle plugin whose route patterns match the pathname. */
|
|
61
|
+
export { requestRoutePlugins } from '../lazy/index';
|
|
62
|
+
/** Ask the host to lazy-load every idle plugin — cross-plugin surfaces (⌘K search). */
|
|
63
|
+
export { requestAllPlugins } from '../lazy/index';
|
|
64
|
+
/** Reset a failed plugin to idle and re-request its client bundle. */
|
|
65
|
+
export { retryPluginLoad } from '../lazy/index';
|
|
66
|
+
/** Monotonic lazy-store version for useSyncExternalStore consumers. */
|
|
67
|
+
export { getLazyPluginsVersion } from '../lazy/index';
|
|
68
|
+
/** Subscribe to lazy-plugin store mutations. Returns an unsubscribe fn. */
|
|
69
|
+
export { subscribeLazyPlugins } from '../lazy/index';
|
|
70
|
+
export type { LazyPluginIndex, PluginLoadState } from '../lazy/index';
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/sdk/src/slots/registry.ts
|
|
3
|
+
function getRegistry() {
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
if (!g.__bakinSlotRegistry) {
|
|
6
|
+
g.__bakinSlotRegistry = new Map;
|
|
7
|
+
}
|
|
8
|
+
return g.__bakinSlotRegistry;
|
|
9
|
+
}
|
|
10
|
+
function clearSlotsOwnedBy(pluginId) {
|
|
11
|
+
const reg = getRegistry();
|
|
12
|
+
for (const [name, entries] of reg.entries()) {
|
|
13
|
+
const filtered = entries.filter((e) => e.owner !== pluginId);
|
|
14
|
+
if (filtered.length === 0)
|
|
15
|
+
reg.delete(name);
|
|
16
|
+
else
|
|
17
|
+
reg.set(name, filtered);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// packages/sdk/src/register.ts
|
|
22
|
+
"use client";
|
|
23
|
+
function buildNavItemsSnapshot(registry) {
|
|
24
|
+
const items = [];
|
|
25
|
+
for (const [pluginId, navItems] of registry.manifestNavByPlugin.entries()) {
|
|
26
|
+
if (registry.navByPlugin.has(pluginId))
|
|
27
|
+
continue;
|
|
28
|
+
items.push(...navItems);
|
|
29
|
+
}
|
|
30
|
+
for (const navItems of registry.navByPlugin.values()) {
|
|
31
|
+
items.push(...navItems);
|
|
32
|
+
}
|
|
33
|
+
return items.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
34
|
+
}
|
|
35
|
+
function getRegistry2() {
|
|
36
|
+
const g = globalThis;
|
|
37
|
+
if (!g.__bakinClientRegistry) {
|
|
38
|
+
g.__bakinClientRegistry = {
|
|
39
|
+
navByPlugin: new Map,
|
|
40
|
+
manifestNavByPlugin: new Map,
|
|
41
|
+
routesByPlugin: new Map,
|
|
42
|
+
cleanupByPlugin: new Map,
|
|
43
|
+
navItemsSnapshot: [],
|
|
44
|
+
version: 0,
|
|
45
|
+
listeners: new Set,
|
|
46
|
+
badgesByPlugin: new Map,
|
|
47
|
+
badgesSnapshot: new Map,
|
|
48
|
+
badgeListeners: new Set,
|
|
49
|
+
hitRenderersByPlugin: new Map,
|
|
50
|
+
hitRenderersSnapshot: new Map,
|
|
51
|
+
hitRendererListeners: new Set
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const registry = g.__bakinClientRegistry;
|
|
55
|
+
if (!registry.manifestNavByPlugin)
|
|
56
|
+
registry.manifestNavByPlugin = new Map;
|
|
57
|
+
if (!Array.isArray(registry.navItemsSnapshot)) {
|
|
58
|
+
registry.navItemsSnapshot = buildNavItemsSnapshot(registry);
|
|
59
|
+
}
|
|
60
|
+
if (!registry.badgesByPlugin)
|
|
61
|
+
registry.badgesByPlugin = new Map;
|
|
62
|
+
if (!registry.badgesSnapshot)
|
|
63
|
+
registry.badgesSnapshot = buildBadgesSnapshot(registry);
|
|
64
|
+
if (!registry.badgeListeners)
|
|
65
|
+
registry.badgeListeners = new Set;
|
|
66
|
+
if (!registry.hitRenderersByPlugin)
|
|
67
|
+
registry.hitRenderersByPlugin = new Map;
|
|
68
|
+
if (!registry.hitRenderersSnapshot)
|
|
69
|
+
registry.hitRenderersSnapshot = buildHitRenderersSnapshot(registry);
|
|
70
|
+
if (!registry.hitRendererListeners)
|
|
71
|
+
registry.hitRendererListeners = new Set;
|
|
72
|
+
return registry;
|
|
73
|
+
}
|
|
74
|
+
function buildHitRenderersSnapshot(registry) {
|
|
75
|
+
const snapshot = new Map;
|
|
76
|
+
for (const byType of registry.hitRenderersByPlugin.values()) {
|
|
77
|
+
for (const [type, renderer] of byType) {
|
|
78
|
+
if (!snapshot.has(type))
|
|
79
|
+
snapshot.set(type, renderer);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return snapshot;
|
|
83
|
+
}
|
|
84
|
+
function bumpHitRenderers() {
|
|
85
|
+
const registry = getRegistry2();
|
|
86
|
+
registry.hitRenderersSnapshot = buildHitRenderersSnapshot(registry);
|
|
87
|
+
for (const l of registry.hitRendererListeners) {
|
|
88
|
+
try {
|
|
89
|
+
l();
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error("[bakin] hit-renderer listener threw:", err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function buildBadgesSnapshot(registry) {
|
|
96
|
+
const snapshot = new Map;
|
|
97
|
+
for (const byNavItemId of registry.badgesByPlugin.values()) {
|
|
98
|
+
for (const [navItemId, badge] of byNavItemId) {
|
|
99
|
+
if (!snapshot.has(navItemId))
|
|
100
|
+
snapshot.set(navItemId, badge);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return snapshot;
|
|
104
|
+
}
|
|
105
|
+
function bumpBadges() {
|
|
106
|
+
const registry = getRegistry2();
|
|
107
|
+
registry.badgesSnapshot = buildBadgesSnapshot(registry);
|
|
108
|
+
for (const l of registry.badgeListeners) {
|
|
109
|
+
try {
|
|
110
|
+
l();
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error("[bakin] badge listener threw:", err);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function bumpVersion() {
|
|
117
|
+
const registry = getRegistry2();
|
|
118
|
+
registry.navItemsSnapshot = buildNavItemsSnapshot(registry);
|
|
119
|
+
registry.version++;
|
|
120
|
+
for (const l of registry.listeners) {
|
|
121
|
+
try {
|
|
122
|
+
l();
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.error("[bakin] registry listener threw:", err);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function unregisterPlugin(id) {
|
|
129
|
+
const registry = getRegistry2();
|
|
130
|
+
const cleanups = registry.cleanupByPlugin.get(id);
|
|
131
|
+
if (cleanups) {
|
|
132
|
+
for (const fn of cleanups) {
|
|
133
|
+
try {
|
|
134
|
+
fn();
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error(`[bakin] cleanup fn for ${id} threw:`, err);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
registry.cleanupByPlugin.delete(id);
|
|
140
|
+
}
|
|
141
|
+
registry.navByPlugin.delete(id);
|
|
142
|
+
registry.routesByPlugin.delete(id);
|
|
143
|
+
clearSlotsOwnedBy(id);
|
|
144
|
+
const hadBadges = registry.badgesByPlugin.delete(id);
|
|
145
|
+
const hadRenderers = registry.hitRenderersByPlugin.delete(id);
|
|
146
|
+
bumpVersion();
|
|
147
|
+
if (hadBadges)
|
|
148
|
+
bumpBadges();
|
|
149
|
+
if (hadRenderers)
|
|
150
|
+
bumpHitRenderers();
|
|
151
|
+
}
|
|
152
|
+
function setManifestNav(pluginId, navItems) {
|
|
153
|
+
const registry = getRegistry2();
|
|
154
|
+
if (!navItems || navItems.length === 0) {
|
|
155
|
+
if (!registry.manifestNavByPlugin.delete(pluginId))
|
|
156
|
+
return;
|
|
157
|
+
bumpVersion();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const prev = registry.manifestNavByPlugin.get(pluginId);
|
|
161
|
+
if (prev && JSON.stringify(prev) === JSON.stringify(navItems))
|
|
162
|
+
return;
|
|
163
|
+
registry.manifestNavByPlugin.set(pluginId, navItems);
|
|
164
|
+
bumpVersion();
|
|
165
|
+
}
|
|
166
|
+
function getManifestNav(pluginId) {
|
|
167
|
+
return getRegistry2().manifestNavByPlugin.get(pluginId) ?? [];
|
|
168
|
+
}
|
|
169
|
+
function getRegistryVersion() {
|
|
170
|
+
return getRegistry2().version;
|
|
171
|
+
}
|
|
172
|
+
function subscribeRegistry(listener) {
|
|
173
|
+
const registry = getRegistry2();
|
|
174
|
+
registry.listeners.add(listener);
|
|
175
|
+
return () => {
|
|
176
|
+
registry.listeners.delete(listener);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function getAllNavItems() {
|
|
180
|
+
return [...getRegistry2().navItemsSnapshot];
|
|
181
|
+
}
|
|
182
|
+
function getNavItemsSnapshot() {
|
|
183
|
+
return getRegistry2().navItemsSnapshot;
|
|
184
|
+
}
|
|
185
|
+
function getPluginNavItems(pluginId) {
|
|
186
|
+
return getRegistry2().navByPlugin.get(pluginId) ?? [];
|
|
187
|
+
}
|
|
188
|
+
function getNavBadgesSnapshot() {
|
|
189
|
+
return getRegistry2().badgesSnapshot;
|
|
190
|
+
}
|
|
191
|
+
function subscribeNavBadges(listener) {
|
|
192
|
+
const registry = getRegistry2();
|
|
193
|
+
registry.badgeListeners.add(listener);
|
|
194
|
+
return () => {
|
|
195
|
+
registry.badgeListeners.delete(listener);
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function getSearchHitRenderer(type) {
|
|
199
|
+
return getRegistry2().hitRenderersSnapshot.get(type);
|
|
200
|
+
}
|
|
201
|
+
function getSearchHitRenderersSnapshot() {
|
|
202
|
+
return getRegistry2().hitRenderersSnapshot;
|
|
203
|
+
}
|
|
204
|
+
function subscribeSearchHitRenderers(listener) {
|
|
205
|
+
const registry = getRegistry2();
|
|
206
|
+
registry.hitRendererListeners.add(listener);
|
|
207
|
+
return () => {
|
|
208
|
+
registry.hitRendererListeners.delete(listener);
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function normalizePattern(pattern) {
|
|
212
|
+
return pattern.split("/").filter(Boolean);
|
|
213
|
+
}
|
|
214
|
+
function matchRoutePattern(pattern, pathname) {
|
|
215
|
+
const patternSegments = normalizePattern(pattern);
|
|
216
|
+
const pathSegments = normalizePattern(pathname);
|
|
217
|
+
if (patternSegments.length !== pathSegments.length)
|
|
218
|
+
return null;
|
|
219
|
+
const params = {};
|
|
220
|
+
let score = 0;
|
|
221
|
+
for (let i = 0;i < patternSegments.length; i++) {
|
|
222
|
+
const patternSegment = patternSegments[i];
|
|
223
|
+
const pathSegment = pathSegments[i];
|
|
224
|
+
const bracket = patternSegment.match(/^\[([^\]]+)\]$/);
|
|
225
|
+
const isColon = patternSegment.startsWith(":");
|
|
226
|
+
const isDollar = patternSegment.startsWith("$");
|
|
227
|
+
if (bracket || isColon || isDollar) {
|
|
228
|
+
const key = bracket?.[1] ?? patternSegment.slice(1);
|
|
229
|
+
params[key] = decodeURIComponent(pathSegment);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (patternSegment !== pathSegment)
|
|
233
|
+
return null;
|
|
234
|
+
score += 1;
|
|
235
|
+
}
|
|
236
|
+
return { params, score };
|
|
237
|
+
}
|
|
238
|
+
function getPluginRoute(pathname) {
|
|
239
|
+
const registry = getRegistry2();
|
|
240
|
+
let best = null;
|
|
241
|
+
for (const [pluginId, routes] of registry.routesByPlugin.entries()) {
|
|
242
|
+
for (const route2 of routes) {
|
|
243
|
+
const match = matchRoutePattern(route2.path, pathname);
|
|
244
|
+
if (!match)
|
|
245
|
+
continue;
|
|
246
|
+
if (!best || match.score > best.score) {
|
|
247
|
+
best = {
|
|
248
|
+
pluginId,
|
|
249
|
+
path: route2.path,
|
|
250
|
+
component: route2.component,
|
|
251
|
+
params: match.params,
|
|
252
|
+
score: match.score
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (!best)
|
|
258
|
+
return null;
|
|
259
|
+
const { score: _score, ...route } = best;
|
|
260
|
+
return route;
|
|
261
|
+
}
|
|
262
|
+
function getPluginRoutes(pluginId) {
|
|
263
|
+
const registry = getRegistry2();
|
|
264
|
+
if (pluginId)
|
|
265
|
+
return registry.routesByPlugin.get(pluginId) ?? [];
|
|
266
|
+
return [...registry.routesByPlugin.values()].flat();
|
|
267
|
+
}
|
|
268
|
+
// packages/sdk/src/lazy.ts
|
|
269
|
+
"use client";
|
|
270
|
+
function getStore() {
|
|
271
|
+
const g = globalThis;
|
|
272
|
+
if (!g.__bakinLazyPluginStore) {
|
|
273
|
+
g.__bakinLazyPluginStore = {
|
|
274
|
+
ownersBySlot: new Map,
|
|
275
|
+
routeOwners: [],
|
|
276
|
+
states: new Map,
|
|
277
|
+
errors: new Map,
|
|
278
|
+
loader: null,
|
|
279
|
+
version: 0,
|
|
280
|
+
listeners: new Set
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return g.__bakinLazyPluginStore;
|
|
284
|
+
}
|
|
285
|
+
function bump() {
|
|
286
|
+
const store = getStore();
|
|
287
|
+
store.version++;
|
|
288
|
+
for (const l of store.listeners) {
|
|
289
|
+
try {
|
|
290
|
+
l();
|
|
291
|
+
} catch (err) {
|
|
292
|
+
console.error("[bakin] lazy-plugin listener threw:", err);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function configureLazyPlugins(index) {
|
|
297
|
+
const store = getStore();
|
|
298
|
+
store.ownersBySlot = new Map(index.slotOwners);
|
|
299
|
+
store.routeOwners = [...index.routeOwners];
|
|
300
|
+
bump();
|
|
301
|
+
}
|
|
302
|
+
function setLazyPluginLoader(loader) {
|
|
303
|
+
getStore().loader = loader;
|
|
304
|
+
}
|
|
305
|
+
function setPluginLoadState(pluginId, state, error) {
|
|
306
|
+
const store = getStore();
|
|
307
|
+
if (store.states.get(pluginId) === state && store.errors.get(pluginId) === error)
|
|
308
|
+
return;
|
|
309
|
+
store.states.set(pluginId, state);
|
|
310
|
+
if (state === "error" && error)
|
|
311
|
+
store.errors.set(pluginId, error);
|
|
312
|
+
else
|
|
313
|
+
store.errors.delete(pluginId);
|
|
314
|
+
bump();
|
|
315
|
+
}
|
|
316
|
+
function getPluginLoadState(pluginId) {
|
|
317
|
+
return getStore().states.get(pluginId) ?? "idle";
|
|
318
|
+
}
|
|
319
|
+
function getPluginLoadError(pluginId) {
|
|
320
|
+
return getStore().errors.get(pluginId);
|
|
321
|
+
}
|
|
322
|
+
function getSlotOwners(name) {
|
|
323
|
+
return getStore().ownersBySlot.get(name) ?? [];
|
|
324
|
+
}
|
|
325
|
+
function getRouteOwners(pathname) {
|
|
326
|
+
const owners = [];
|
|
327
|
+
for (const { pattern, pluginId } of getStore().routeOwners) {
|
|
328
|
+
if (matchRoutePattern(pattern, pathname) && !owners.includes(pluginId)) {
|
|
329
|
+
owners.push(pluginId);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return owners;
|
|
333
|
+
}
|
|
334
|
+
function demand(pluginIds) {
|
|
335
|
+
const store = getStore();
|
|
336
|
+
if (!store.loader)
|
|
337
|
+
return;
|
|
338
|
+
for (const pluginId of pluginIds) {
|
|
339
|
+
if ((store.states.get(pluginId) ?? "idle") !== "idle")
|
|
340
|
+
continue;
|
|
341
|
+
store.loader(pluginId);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function requestSlotPlugins(name) {
|
|
345
|
+
demand(getSlotOwners(name));
|
|
346
|
+
}
|
|
347
|
+
function requestRoutePlugins(pathname) {
|
|
348
|
+
demand(getRouteOwners(pathname));
|
|
349
|
+
}
|
|
350
|
+
function requestAllPlugins() {
|
|
351
|
+
const store = getStore();
|
|
352
|
+
const ids = new Set;
|
|
353
|
+
for (const owners of store.ownersBySlot.values()) {
|
|
354
|
+
for (const id of owners)
|
|
355
|
+
ids.add(id);
|
|
356
|
+
}
|
|
357
|
+
for (const { pluginId } of store.routeOwners)
|
|
358
|
+
ids.add(pluginId);
|
|
359
|
+
demand([...ids]);
|
|
360
|
+
}
|
|
361
|
+
function retryPluginLoad(pluginId) {
|
|
362
|
+
const store = getStore();
|
|
363
|
+
if (store.states.get(pluginId) !== "error")
|
|
364
|
+
return;
|
|
365
|
+
store.states.set(pluginId, "idle");
|
|
366
|
+
store.errors.delete(pluginId);
|
|
367
|
+
bump();
|
|
368
|
+
demand([pluginId]);
|
|
369
|
+
}
|
|
370
|
+
function getLazyPluginsVersion() {
|
|
371
|
+
return getStore().version;
|
|
372
|
+
}
|
|
373
|
+
function subscribeLazyPlugins(listener) {
|
|
374
|
+
const store = getStore();
|
|
375
|
+
store.listeners.add(listener);
|
|
376
|
+
return () => {
|
|
377
|
+
store.listeners.delete(listener);
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
export {
|
|
381
|
+
unregisterPlugin,
|
|
382
|
+
subscribeSearchHitRenderers,
|
|
383
|
+
subscribeRegistry,
|
|
384
|
+
subscribeNavBadges,
|
|
385
|
+
subscribeLazyPlugins,
|
|
386
|
+
setPluginLoadState,
|
|
387
|
+
setManifestNav,
|
|
388
|
+
setLazyPluginLoader,
|
|
389
|
+
retryPluginLoad,
|
|
390
|
+
requestSlotPlugins,
|
|
391
|
+
requestRoutePlugins,
|
|
392
|
+
requestAllPlugins,
|
|
393
|
+
getSlotOwners,
|
|
394
|
+
getSearchHitRenderersSnapshot,
|
|
395
|
+
getSearchHitRenderer,
|
|
396
|
+
getRouteOwners,
|
|
397
|
+
getRegistryVersion,
|
|
398
|
+
getPluginRoutes,
|
|
399
|
+
getPluginRoute,
|
|
400
|
+
getPluginNavItems,
|
|
401
|
+
getPluginLoadState,
|
|
402
|
+
getPluginLoadError,
|
|
403
|
+
getNavItemsSnapshot,
|
|
404
|
+
getNavBadgesSnapshot,
|
|
405
|
+
getManifestNav,
|
|
406
|
+
getLazyPluginsVersion,
|
|
407
|
+
getAllNavItems,
|
|
408
|
+
configureLazyPlugins
|
|
409
|
+
};
|
package/metadata/index.d.ts
CHANGED
|
@@ -13,4 +13,17 @@
|
|
|
13
13
|
* from `@makinbakin/sdk/routing` (the typed declarative shape).
|
|
14
14
|
*/
|
|
15
15
|
export type { ContractMetadata, ContractStability, ContractVisibility, CliCommandContract, DocsAwareAPIRoute, DocsExample, ExecToolContract, HookContract, HookKind, PublicContract, RouteContract, SchemaLike, SlotContract, SourceLocation, } from '../_internal/core/docs';
|
|
16
|
-
|
|
16
|
+
/** Legacy: declare an API route with docs metadata. Prefer `defineRoute` from `/routing`. */
|
|
17
|
+
export { defineApiRoute } from '../_internal/core/docs';
|
|
18
|
+
/** Define a CLI command contract for documentation. */
|
|
19
|
+
export { defineCliCommandContract } from '../_internal/core/docs';
|
|
20
|
+
/** Define an MCP exec tool contract for documentation. */
|
|
21
|
+
export { defineExecToolContract } from '../_internal/core/docs';
|
|
22
|
+
/** Define a cross-plugin hook contract for documentation. */
|
|
23
|
+
export { defineHookContract } from '../_internal/core/docs';
|
|
24
|
+
/** Legacy: declare a plugin route with docs metadata. Prefer `defineRoute` from `/routing`. */
|
|
25
|
+
export { definePluginRoute } from '../_internal/core/docs';
|
|
26
|
+
/** Define a generic route contract (shared shape for API + plugin routes). */
|
|
27
|
+
export { defineRouteContract } from '../_internal/core/docs';
|
|
28
|
+
/** Define a UI slot contract for documentation. */
|
|
29
|
+
export { defineSlotContract } from '../_internal/core/docs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makinbakin/sdk",
|
|
3
|
-
"version": "0.0.1-rc.
|
|
3
|
+
"version": "0.0.1-rc.21",
|
|
4
4
|
"description": "SDK for building Bakin plugins — UI components, slot types, hooks, and the runtime registerPlugin helper.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -42,6 +42,14 @@
|
|
|
42
42
|
"./routing": {
|
|
43
43
|
"import": "./routing/index.js",
|
|
44
44
|
"types": "./routing/index.d.ts"
|
|
45
|
+
},
|
|
46
|
+
"./testing": {
|
|
47
|
+
"import": "./testing/index.js",
|
|
48
|
+
"types": "./testing/index.d.ts"
|
|
49
|
+
},
|
|
50
|
+
"./internal": {
|
|
51
|
+
"import": "./internal/index.js",
|
|
52
|
+
"types": "./internal/index.d.ts"
|
|
45
53
|
}
|
|
46
54
|
},
|
|
47
55
|
"files": [
|
|
@@ -58,7 +66,6 @@
|
|
|
58
66
|
"class-variance-authority": "^0.7.1",
|
|
59
67
|
"clsx": "^2.1.1",
|
|
60
68
|
"cmdk": "^1.1.1",
|
|
61
|
-
"lucide-react": "^0.577.0",
|
|
62
69
|
"react-hook-form": "^7.72.0",
|
|
63
70
|
"zod": "^4.3.0",
|
|
64
71
|
"zustand": "^5.0.12"
|
package/register.d.ts
CHANGED
|
@@ -17,15 +17,7 @@
|
|
|
17
17
|
* nav + slot trees re-render when plugins come or go at runtime.
|
|
18
18
|
*/
|
|
19
19
|
import type { ComponentType } from 'react';
|
|
20
|
-
|
|
21
|
-
id: string;
|
|
22
|
-
label: string;
|
|
23
|
-
icon?: string;
|
|
24
|
-
href?: string;
|
|
25
|
-
order?: number;
|
|
26
|
-
alwaysExpanded?: boolean;
|
|
27
|
-
children?: NavItem[];
|
|
28
|
-
}
|
|
20
|
+
import type { NavBadge, NavItem, SearchHitRenderer } from './types/index';
|
|
29
21
|
interface PluginRegistration {
|
|
30
22
|
id: string;
|
|
31
23
|
navItems?: NavItem[];
|
|
@@ -43,6 +35,14 @@ interface PluginRegistration {
|
|
|
43
35
|
* directly from the plugin's client.tsx alongside registerPlugin.
|
|
44
36
|
*/
|
|
45
37
|
slots?: Record<string, ComponentType<any>>;
|
|
38
|
+
/**
|
|
39
|
+
* Global-search contributions: content type (bare table name, e.g.
|
|
40
|
+
* 'assets') → hit renderer for the ⌘K overlay. Plain data-mapping
|
|
41
|
+
* functions, not components.
|
|
42
|
+
*/
|
|
43
|
+
search?: {
|
|
44
|
+
hitRenderers: Record<string, SearchHitRenderer>;
|
|
45
|
+
};
|
|
46
46
|
}
|
|
47
47
|
interface ClientRouteEntry {
|
|
48
48
|
path: string;
|
|
@@ -68,6 +68,23 @@ export declare function unregisterPlugin(id: string): void;
|
|
|
68
68
|
* use this to hook into the unified teardown path.
|
|
69
69
|
*/
|
|
70
70
|
export declare function registerPluginCleanup(id: string, fn: () => void): void;
|
|
71
|
+
/**
|
|
72
|
+
* Seed (or replace) a plugin's declarative nav from its manifest
|
|
73
|
+
* `contributes.nav`. Called by the host's PluginHost after every manifest
|
|
74
|
+
* fetch — including hot-swap refreshes — so the sidebar renders before any
|
|
75
|
+
* client bundle loads. Pass `null` (or an empty array) to clear.
|
|
76
|
+
*
|
|
77
|
+
* Manifest nav lives on its own map: `unregisterPlugin` never touches it,
|
|
78
|
+
* and a plugin that registers `navItems` at runtime overrides its manifest
|
|
79
|
+
* nav while registered (the conditional-nav escape hatch).
|
|
80
|
+
*/
|
|
81
|
+
export declare function setManifestNav(pluginId: string, navItems: NavItem[] | null): void;
|
|
82
|
+
/**
|
|
83
|
+
* The declarative manifest nav currently seeded for a plugin (empty if none).
|
|
84
|
+
* Used by the drift validation check to compare manifest nav against what a
|
|
85
|
+
* loaded client registered at runtime.
|
|
86
|
+
*/
|
|
87
|
+
export declare function getManifestNav(pluginId: string): ReadonlyArray<NavItem>;
|
|
71
88
|
/**
|
|
72
89
|
* Monotonic counter bumped on every register / unregister. Used with
|
|
73
90
|
* React's `useSyncExternalStore` (via `subscribeRegistry`) so the shell
|
|
@@ -92,6 +109,43 @@ export declare function getNavItemsSnapshot(): readonly NavItem[];
|
|
|
92
109
|
* Nav items for a specific plugin. Returned as a readonly copy.
|
|
93
110
|
*/
|
|
94
111
|
export declare function getPluginNavItems(pluginId: string): ReadonlyArray<NavItem>;
|
|
112
|
+
/**
|
|
113
|
+
* Set or clear the badge for a nav item owned by a plugin. Pass `null` to
|
|
114
|
+
* clear. Safe to call before the nav item itself is registered — the badge
|
|
115
|
+
* survives in the registry until the owning plugin is unregistered.
|
|
116
|
+
*/
|
|
117
|
+
export declare function setNavBadge(pluginId: string, navItemId: string, badge: NavBadge | null): void;
|
|
118
|
+
/** Read the current badge for a nav item, or undefined if none. */
|
|
119
|
+
export declare function getNavBadge(navItemId: string): NavBadge | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* Stable snapshot of every active badge keyed by navItemId. The identity
|
|
122
|
+
* of the returned Map only changes when a badge is set, cleared, or its
|
|
123
|
+
* owning plugin is unregistered — safe for React's `useSyncExternalStore`.
|
|
124
|
+
*/
|
|
125
|
+
export declare function getNavBadgesSnapshot(): ReadonlyMap<string, NavBadge>;
|
|
126
|
+
/**
|
|
127
|
+
* Subscribe to badge mutations on a channel separate from `subscribeRegistry`
|
|
128
|
+
* so high-frequency badge ticks don't force the whole nav to re-render.
|
|
129
|
+
*/
|
|
130
|
+
export declare function subscribeNavBadges(listener: () => void): () => void;
|
|
131
|
+
/** Renderer for a content type (bare table name), or undefined if none. */
|
|
132
|
+
export declare function getSearchHitRenderer(type: string): SearchHitRenderer | undefined;
|
|
133
|
+
/**
|
|
134
|
+
* Stable snapshot of every registered hit renderer keyed by content type.
|
|
135
|
+
* Identity changes only on register/unregister — safe for
|
|
136
|
+
* `useSyncExternalStore`. The ⌘K overlay derives its type chips from this.
|
|
137
|
+
*/
|
|
138
|
+
export declare function getSearchHitRenderersSnapshot(): ReadonlyMap<string, SearchHitRenderer>;
|
|
139
|
+
/** Subscribe to hit-renderer mutations (own channel, like nav badges). */
|
|
140
|
+
export declare function subscribeSearchHitRenderers(listener: () => void): () => void;
|
|
141
|
+
/**
|
|
142
|
+
* Match a route pattern (`/projects/[id]`, `/x/:id`, `/y/$id`) against a
|
|
143
|
+
* concrete pathname. Internal SDK helper shared with the lazy-loading index.
|
|
144
|
+
*/
|
|
145
|
+
export declare function matchRoutePattern(pattern: string, pathname: string): {
|
|
146
|
+
params: Record<string, string>;
|
|
147
|
+
score: number;
|
|
148
|
+
} | null;
|
|
95
149
|
export interface MatchedPluginRoute {
|
|
96
150
|
pluginId: string;
|
|
97
151
|
path: string;
|
package/routing/index.d.ts
CHANGED
|
@@ -7,5 +7,10 @@
|
|
|
7
7
|
* two parallel `APIRoute<...>` definitions in sync is a debt we are
|
|
8
8
|
* deliberately avoiding.
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
/** Define a plugin HTTP route with typed input/output and handler. */
|
|
11
|
+
export { defineRoute } from '../_internal/core/routing';
|
|
12
|
+
/** Define a core (non-plugin) HTTP route. */
|
|
13
|
+
export { defineCoreRoute } from '../_internal/core/routing';
|
|
14
|
+
/** Compose a plugin's routes into a single definition for the server. */
|
|
15
|
+
export { definePlugin } from '../_internal/core/routing';
|
|
11
16
|
export type { HttpMethod, HttpStatus, RouteContext, PluginContextLite, CoreContext, JsonBodySpec, MultipartBodySpec, RawBodySpec, NoBodySpec, BodySpec, JsonResponseSpec, NoContentResponseSpec, NonJsonResponseSpec, ResponseSpec, ParsedInput, APIRoute, PluginWithRoutes, DefinePluginInput, } from '../_internal/core/routing';
|