@makinbakin/sdk 0.0.1-rc.20 → 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/_internal/app/components/agent-select.d.ts +10 -1
- 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 +23 -2
- 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 +5 -2
- 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/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-file-drop.d.ts +25 -0
- package/_internal/app/hooks/use-history-back.d.ts +1 -0
- package/_internal/app/hooks/use-json-fetch.d.ts +5 -0
- package/_internal/app/hooks/use-query-state.d.ts +2 -0
- package/_internal/app/hooks/use-schedule.d.ts +45 -0
- package/_internal/app/hooks/use-search.d.ts +27 -8
- package/_internal/app/hooks/use-toast.d.ts +3 -3
- package/_internal/app/lib/browser-notify.d.ts +12 -1
- package/_internal/core/adapters/runtime/concepts.d.ts +547 -49
- package/_internal/core/adapters/runtime/errors.d.ts +13 -1
- package/_internal/core/adapters/runtime/helpers.d.ts +19 -0
- package/_internal/core/adapters/runtime/index.d.ts +5 -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 +8 -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 +5 -1
- 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 +102 -39
- 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 +47 -0
- package/_internal/plugins/workflows/hooks/use-notification-channels.d.ts +1 -1
- package/components/index.d.ts +87 -19
- package/components/index.js +32904 -15368
- package/hooks/index.d.ts +24 -4
- package/hooks/index.js +837 -561
- package/hooks/router.d.ts +20 -4
- package/index.d.ts +5 -50
- package/index.js +223 -406
- package/internal/index.d.ts +70 -0
- package/internal/index.js +409 -0
- package/package.json +9 -2
- package/register.d.ts +19 -1
- package/routing/index.js +175 -147
- package/slots/index.js +20 -1
- package/testing/index.d.ts +122 -0
- package/testing/index.js +21874 -0
- package/types/index.d.ts +4 -0
- package/types/index.js +16 -0
- package/utils/index.d.ts +28 -25
- package/utils/index.js +279 -263
- 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/{plugins/models → app}/hooks/use-available-models.d.ts +0 -0
|
@@ -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/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,7 +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
|
-
import type { NavBadge, NavItem } from './types/index';
|
|
20
|
+
import type { NavBadge, NavItem, SearchHitRenderer } from './types/index';
|
|
21
21
|
interface PluginRegistration {
|
|
22
22
|
id: string;
|
|
23
23
|
navItems?: NavItem[];
|
|
@@ -35,6 +35,14 @@ interface PluginRegistration {
|
|
|
35
35
|
* directly from the plugin's client.tsx alongside registerPlugin.
|
|
36
36
|
*/
|
|
37
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
|
+
};
|
|
38
46
|
}
|
|
39
47
|
interface ClientRouteEntry {
|
|
40
48
|
path: string;
|
|
@@ -120,6 +128,16 @@ export declare function getNavBadgesSnapshot(): ReadonlyMap<string, NavBadge>;
|
|
|
120
128
|
* so high-frequency badge ticks don't force the whole nav to re-render.
|
|
121
129
|
*/
|
|
122
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;
|
|
123
141
|
/**
|
|
124
142
|
* Match a route pattern (`/projects/[id]`, `/x/:id`, `/y/$id`) against a
|
|
125
143
|
* concrete pathname. Internal SDK helper shared with the lazy-loading index.
|