@hachej/boring-data-catalog 0.1.41 → 0.1.43

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.
Files changed (2) hide show
  1. package/README.md +71 -244
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -1,297 +1,124 @@
1
1
  # @hachej/boring-data-catalog
2
2
 
3
- <div align="center">
3
+ A configurable data-catalog plugin **builder** for the workbench. One call to
4
+ `createDataCatalogPlugin(options)` binds your `ExplorerDataSource` adapter and
5
+ returns a configured front plugin that contributes a left sidebar tab, a
6
+ visualization panel, a catalog entry, and a surface resolver. Built on
7
+ [`@hachej/boring-data-explorer`](../data-explorer/README.md).
4
8
 
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ ## What it does
6
10
 
7
- </div>
11
+ - Adds a left sidebar tab listing rows from your adapter, with search + facets.
12
+ - Opens a center "visualization" panel when a row is activated (default panel
13
+ shows another explorer scoped to the row; swap in your own component).
14
+ - Lets the agent search the catalog and open a specific row in a panel.
8
15
 
9
- A configurable data catalog plugin for the workbench — left sidebar tab with a searchable, faceted list and a visualization panel to explore rows. Built on [`@hachej/boring-data-explorer`](../data-explorer/README.md).
16
+ ## What it contributes to the workspace
10
17
 
11
- ```bash
12
- git clone https://github.com/hachej/boring-ui.git && cd boring-ui && pnpm install
13
- ```
14
-
15
- > **Note:** This plugin is workspace-private (`"private": true`) — install from source within the monorepo.
16
-
17
- ---
18
-
19
- ## TL;DR
20
-
21
- **The Problem**: You have a data source (customers, invoices, time series, whatever) and you want users to browse it from a sidebar tab, click rows to open detail visualizations, and let the agent open specific rows programmatically. But wiring up a catalog tab + explorer panel + surface resolver + agent tool is repetitive.
22
-
23
- **The Solution**: `@hachej/boring-data-catalog` gives you a one-call plugin factory: pass in an `ExplorerDataSource` adapter and configure labels, facets, and behavior. It contributes a left tab, a visualization panel, a catalog, and a surface resolver — all wired up.
18
+ `createDataCatalogPlugin()` returns one front plugin with up to four
19
+ contributions, each opt-out via an `include*` flag:
24
20
 
25
- ### Why Use @hachej/boring-data-catalog?
21
+ | Contribution | What | Flag (default) |
22
+ |--------------|------|----------------|
23
+ | Left tab | searchable, faceted row list | `includeLeftTab` (true) |
24
+ | Visualization panel | center panel for the selected row | `includeVisualizationPanel` (true) |
25
+ | Catalog | command-palette-searchable catalog entry | `includeCatalog` (true) |
26
+ | Surface resolver | maps `openSurface` → opens the panel | `includeSurfaceResolver` (true if panel on and no custom `onSelect`) |
26
27
 
27
- | Feature | What It Does |
28
- |---------|--------------|
29
- | **Left sidebar catalog tab** | Persistent sidebar listing rows from your adapter, with search and facet filters |
30
- | **Visualization panel** | Click a row → opens a detail panel that also shows an explorer table |
31
- | **Surface resolver** | Agent can open a specific row via `openSurface` with `DATA_CATALOG_ROW_SURFACE_KIND` |
32
- | **Agent tool** | Server plugin ships a `search_catalog` tool the agent uses to find rows before opening them |
33
- | **Pre-wired with data-explorer** | Search + facets + drag-out behavior comes for free |
34
- | **Customizable** | Swap the visualization component, customize facets/groupBy/onSelect, or pick which contributions to include |
28
+ The server plugin (`createDataCatalogServerPlugin`) contributes an agent tool
29
+ (default name **`query_data_catalog`**) plus a system-prompt snippet.
35
30
 
36
- ---
31
+ ## How it's wired
37
32
 
38
- ## Quick Example
33
+ This package has **no default export** and requires `options`, so it is **not**
34
+ a direct `defaultPluginPackages` entry. Wrap it in an app-local module that
35
+ default-exports the built factory, then list that wrapper.
39
36
 
40
- **Frontend (workbench):**
37
+ **Front:**
41
38
 
42
39
  ```ts
43
40
  import { createDataCatalogPlugin } from "@hachej/boring-data-catalog/front"
44
41
  import type { ExplorerDataSource } from "@hachej/boring-data-explorer/shared"
45
42
 
46
- // Your data adapter
47
- const adapter: ExplorerDataSource = {
48
- async search({ query, filters, limit, offset }) {
49
- // fetch from your backend
50
- return { items: [...], total, hasMore: ... }
51
- },
52
- }
43
+ const adapter: ExplorerDataSource = { async search(args) { /* ... */ } }
53
44
 
54
- // One plugin = left tab + visualization panel + catalog + surface resolver
55
45
  const catalogPlugin = createDataCatalogPlugin({
56
46
  id: "customers",
57
47
  label: "Customers",
58
48
  adapter,
59
- facets: [
60
- { key: "industry", label: "Industry", formatValue: (v) => v },
61
- { key: "region", label: "Region", order: ["US", "EU", "APAC"], formatValue: (v) => v },
62
- ],
63
- groupBy: "industry",
49
+ facets: [{ key: "region", label: "Region", order: ["US", "EU", "APAC"] }],
50
+ groupBy: "region",
64
51
  })
52
+ // <WorkspaceProvider plugins={[catalogPlugin, ...]}>
65
53
  ```
66
54
 
67
- Add `catalogPlugin` to your `WorkspaceProvider` plugins array.
68
-
69
- **Server (agent runtime):**
55
+ **Server:**
70
56
 
71
57
  ```ts
72
58
  import { createDataCatalogServerPlugin } from "@hachej/boring-data-catalog/server"
73
- import { ExplorerDataSource } from "@hachej/boring-data-explorer/shared"
74
59
 
75
- const catalogServerPlugin = createDataCatalogServerPlugin({
60
+ const catalogServer = createDataCatalogServerPlugin({
76
61
  label: "Customers",
77
- adapter, // same ExplorerDataSource as front
78
- defaultLimit: 20,
79
- maxLimit: 50,
62
+ adapter, // same ExplorerDataSource as the front
63
+ defaultLimit: 20, // optional (default 20)
64
+ maxLimit: 50, // optional (default 50)
80
65
  })
81
66
  ```
82
67
 
83
- Add `catalogServerPlugin` to `createAgentApp` `plugins` array.
84
-
85
- Now agents can search the catalog via the tool and open specific rows via the UI bridge `openSurface` command:
86
- ```
87
- openSurface({ kind: DATA_CATALOG_ROW_SURFACE_KIND, target: row.id, meta: { catalogId, row } })
88
- ```
89
-
90
- ---
91
-
92
- ## What It Contributes
93
-
94
- One call to `createDataCatalogPlugin()` contributes four front registrations:
95
-
96
- | Contribution | What | Toggle |
97
- |--------|------|--------|
98
- | **Left tab** | Sidebar tab with searchable, faceted table | `includeLeftTab` (default: true) |
99
- | **Visualization panel** | Center panel for exploring a selected row's context | `includeVisualizationPanel` (default: true) |
100
- | **Catalog** | Command-palette-searchable catalog entry | `includeCatalog` (default: true) |
101
- | **Surface resolver** | Maps `openSurface` calls to panel open | `includeSurfaceResolver` (default: true if visualization panel is on) |
102
-
103
- You can disable any of these with the `include*` flags. For example, a plugin that only contributes a left tab:
68
+ The agent searches via the tool, then opens a row through the UI bridge:
104
69
 
105
70
  ```ts
106
- createDataCatalogPlugin({
107
- id: "metrics",
108
- label: "Metrics",
109
- adapter,
110
- includeLeftTab: true,
111
- includeVisualizationPanel: false,
112
- includeCatalog: false,
113
- includeSurfaceResolver: false,
71
+ openSurface({
72
+ kind: "data-catalog.open-row", // DATA_CATALOG_ROW_SURFACE_KIND
73
+ target: row.id,
74
+ meta: { catalogId: "customers", row },
114
75
  })
115
76
  ```
116
77
 
117
- ---
118
-
119
78
  ## Configuration
120
79
 
121
- ```ts
122
- interface CreateDataCatalogPluginOptions {
123
- pluginId?: string
124
- id: string
125
- label: string
126
- adapter: ExplorerDataSource // required — your data source
127
- facets?: FacetConfig[] // facet filter definitions
128
- groupBy?: string // group key for the left tab rows
129
- getDragPayload?: (row) => DragPayload | null
130
- onSelect?: (row, context) => void // custom row click handler
131
- emptyState?: ReactNode
132
- searchPlaceholder?: string
133
- pageSize?: number
134
- debounceMs?: number
135
- leftTabId?: string
136
- leftTabTitle?: string
137
- leftTabIcon?: IconType
138
- catalogId?: string
139
- catalogLabel?: string
140
- visualizationPanelId?: string
141
- visualizationTitle?: string
142
- visualizationIcon?: IconType
143
- visualizationComponent?: ComponentType<PaneProps<DataCatalogVisualizationParams>>
144
- includeLeftTab?: boolean // default true
145
- includeCatalog?: boolean // default true
146
- includeVisualizationPanel?: boolean // default true
147
- includeSurfaceResolver?: boolean // default true (if visualization panel on)
148
- source?: "builtin" | "app" // panel attribution
149
- }
150
- ```
151
-
152
- ---
153
-
154
- ## Architecture
155
-
156
- ```
157
- ┌───────────────────────────────────────┐
158
- │ Workspace Left Sidebar │
159
- │ ┌───────────────────────────────┐ │
160
- │ │ 📊 Customers (left tab) │ │
161
- │ │ ┌───────────────────────────┐ │ │
162
- │ │ │ [Search: "acme..."] │ │ │
163
- │ │ │ [Industry: Tech ▼] │ │ │
164
- │ │ │ ───────────────────────── │ │ │
165
- │ │ │ US: Acme Corp [T] ✓ │ │ │
166
- │ │ │ EU: Acme GmbH [T] │ │ │
167
- │ │ │ APAC: Acme KK [L] │ │ │
168
- │ │ └─────────────────────────── │ │ │
169
- │ └───────────────────────────────┘ │
170
- └───────────────┬───────────────────────┘
171
- │ click row → open panel
172
-
173
- ┌───────────────────────────────────────┐
174
- │ Center Panel (visualization) │
175
- │ ┌───────────────────────────────┐ │
176
- │ │ Acme Corp │ │
177
- │ │ US: Acme Corp [T] │ │
178
- │ │ ┌───────────────────────────┐│ │
179
- │ │ │ [Search...] ││ │
180
- │ │ │ [filtered by row context] ││ │
181
- │ │ │ ...explorer table... ││ │
182
- │ │ └───────────────────────────┘│ │
183
- │ └───────────────────────────────┘ │
184
- └───────────────────────────────────────┘
185
- ```
186
-
187
- The agent can open any row programmatically:
188
- ```
189
- POST /api/v1/ui/commands
190
- { kind: "openSurface", params: {
191
- surfaceKind: "data-catalog-row",
192
- target: <row.id>,
193
- meta: { catalogId: "customers", row: { title: "Acme Corp", ... } }
194
- }}
195
- ```
196
-
197
- ---
198
-
199
- ## Installation
200
-
201
- ```bash
202
- # From source (workspace-only — not published to npm)
203
- cd boring-ui/plugins/data-catalog && pnpm install && pnpm build
204
- ```
80
+ `createDataCatalogPlugin(options)` — only `adapter` is required. Key options:
205
81
 
206
- ---
82
+ - `id`, `label`, `pluginId`, `source` — identity / panel attribution.
83
+ - `facets`, `groupBy`, `getDragPayload`, `onSelect(row, ctx)`, `emptyState`,
84
+ `searchPlaceholder`, `pageSize`, `debounceMs` — explorer behavior.
85
+ - `leftTabId` / `leftTabTitle` / `leftTabIcon`,
86
+ `catalogId` / `catalogLabel`,
87
+ `visualizationPanelId` / `visualizationTitle` / `visualizationIcon` /
88
+ `visualizationComponent` — per-contribution overrides.
89
+ - `surfaceKind`, `surfaceResolverId` — surface wiring overrides.
90
+ - `include*` flags — toggle each contribution.
207
91
 
208
- ## How @hachej/boring-data-catalog Compares
92
+ Server options: `name`, `label`, `adapter`, `defaultLimit`, `maxLimit`,
93
+ `surfaceKind`, `guidance`, `id`.
209
94
 
210
- | Feature | @hachej/boring-data-catalog | Custom sidebar + table | Embedded BI tool |
211
- |---------|------------------------------|-----------------------|------------------|
212
- | Catalog tab + panel | ✅ One plugin call | ❌ DIY each piece | ⚠️ Configuration-heavy |
213
- | Agent tool + bridge | ✅ search + openSurface | ❌ DIY | ❌ |
214
- | Wiring effort | ✅ ~10 lines | ❌ Hours | ❌ Days |
215
- | Data source flexibility | ✅ Any backend via ExplorerDataSource | ⚠️ Custom per source | ⚠️ Vendor-defined |
216
- | Workbench integration | ✅ Drag-to-panel, exec_ui | ⚠️ Manual | ❌ None |
217
- | Customizable outputs | ✅ Toggle left-tab / panel / catalog / resolver | ❌ All or nothing | ❌ |
95
+ ## Package surfaces
218
96
 
219
- **When to use @hachej/boring-data-catalog:**
220
- - You want a sidebar catalog tab that lets users browse your data
221
- - You want the agent to search and open specific rows in panels
222
- - You want a left-tab + visualization panel combo with minimal code
223
-
224
- **When it might not fit:**
225
- - You only need a standalone table (use `@hachej/boring-data-explorer` directly)
226
- - You want a full BI dashboard with charting (embed a dedicated BI tool)
227
- - You need real-time data streaming (not supported in v1)
228
-
229
- ---
230
-
231
- ## Package Surfaces
232
-
233
- | Import | Environment | What You Get |
234
- |--------|-------------|--------------|
235
- | `@hachej/boring-data-catalog/front` | Browser | `createDataCatalogPlugin()`, types, hooks, surface resolver |
236
- | `@hachej/boring-data-catalog/server` | Node | `createDataCatalogServerPlugin()` — agent tool + skill prompt |
237
- | `@hachej/boring-data-catalog/shared` | Any | `DATA_CATALOG_PLUGIN_ID`, `DATA_CATALOG_ROW_SURFACE_KIND`, constants |
238
-
239
- ---
97
+ | Import | Env | Exports |
98
+ |--------|-----|---------|
99
+ | `@hachej/boring-data-catalog/front` | Browser | `createDataCatalogPlugin`, surface-resolver + open/query helpers, types |
100
+ | `@hachej/boring-data-catalog/server` | Node | `createDataCatalogServerPlugin`, `createDataCatalogAgentTool`, `createDataCatalogSkillPrompt` |
101
+ | `@hachej/boring-data-catalog/shared` | Any | `DATA_CATALOG_PLUGIN_ID`, `DATA_CATALOG_DEFAULT_TOOL_NAME`, `DATA_CATALOG_ROW_SURFACE_KIND` |
240
102
 
241
103
  ## Dependencies
242
104
 
243
- | Package | Required | Why |
244
- |---------|----------|-----|
245
- | `@hachej/boring-data-explorer` | ✅ Yes | Core table component and `ExplorerDataSource` adapter |
246
- | `@hachej/boring-workspace` | ✅ peerDependency | Plugin system and panel registry |
247
- | `lucide-react` | ✅ Yes | Catalog icons (Database, BarChart3) |
248
-
249
- ---
105
+ Requires `@hachej/boring-data-explorer` (table + adapter contract), peer
106
+ `@hachej/boring-workspace` (plugin/panel registry), and `lucide-react` (icons).
250
107
 
251
- ## Troubleshooting
108
+ ## Notes
252
109
 
253
- | Error | Cause | Fix |
254
- |-------|-------|-----|
255
- | Catalog tab doesn't appear | Front plugin not in workspace | Add `createDataCatalogPlugin()` to `WorkspaceProvider` plugins |
256
- | Clicking a row does nothing | `adapter.search()` is broken or returns nothing | Test your adapter independently |
257
- | Agent tool not found | Server plugin not registered | Add `createDataCatalogServerPlugin()` to agent app `plugins` |
258
- | Agent can't open rows | Surface resolver disabled | Set `includeSurfaceResolver: true` (on by default) |
259
- | Icons not rendering | Invalid lucide icon name | Check `leftTabIcon` / `visualizationIcon` against [lucide.dev](https://lucide.dev/icons/) |
110
+ - One adapter per plugin instance — call the builder once per data source.
111
+ - The default visualization panel shows another explorer scoped to the row, not
112
+ charts; pass `visualizationComponent` for a custom view.
113
+ - No server-side caching; each search hits the adapter.
260
114
 
261
- ---
115
+ ## Validation
262
116
 
263
- ## Limitations
264
-
265
- - **Workspace-private** — `"private": true` in package.json. Not published to npm. Install from source within the monorepo.
266
- - **Single data source per plugin** — Each call to `createDataCatalogPlugin()` wires to one `ExplorerDataSource`. For multiple catalogs, instantiate the plugin multiple times with different adapters and labels.
267
- - **No charting or visualization** — The visualization panel shows another explorer table, not charts. For visualizations, provide a custom `visualizationComponent`.
268
- - **No server-side caching** — Each search triggers a fresh `search()` call. Cache at your adapter level if needed.
269
- - **Real-time data** — Not supported in v1. Search uses debounce and pagination but no live streaming.
270
-
271
- ---
272
-
273
- ## FAQ
274
-
275
- **Q: How do I use multiple data sources?**
276
- A: Call `createDataCatalogPlugin()` once per data source, each with a different `id`, `label`, and `adapter`. For a static source list, implement a small `ExplorerDataSource` whose `search()` filters and slices an in-memory `ExplorerItem[]`.
277
-
278
- **Q: How does the agent open a specific row?**
279
- A: The catalog registers a surface resolver. The agent uses the UI bridge `openSurface` command: `{ kind: DATA_CATALOG_ROW_SURFACE_KIND, target: row.id, meta: { catalogId, row } }`.
280
-
281
- **Q: Can I customize the visualization panel?**
282
- A: Yes. Pass a custom `visualizationComponent` to `createDataCatalogPlugin()`. It receives `PaneProps<DataCatalogVisualizationParams>` with the selected row and context.
283
-
284
- **Q: What if my data source is a REST API, not a database?**
285
- A: The `ExplorerDataSource` adapter is backend-agnostic. Implement `search()` (and optionally `fetchFacets()`) to hit your REST endpoint.
286
-
287
- **Q: Can I disable outputs I don't need?**
288
- A: Yes. Set `includeLeftTab`, `includeVisualizationPanel`, `includeCatalog`, or `includeSurfaceResolver` to `false`. A left-tab-only plugin is a perfectly valid output.
289
-
290
- ---
291
-
292
- *About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
293
-
294
- ---
117
+ ```bash
118
+ pnpm --filter @hachej/boring-data-catalog typecheck
119
+ pnpm --filter @hachej/boring-data-catalog test
120
+ pnpm --filter @hachej/boring-data-catalog build
121
+ ```
295
122
 
296
123
  ## License
297
124
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-data-catalog",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -36,11 +36,11 @@
36
36
  "peerDependencies": {
37
37
  "react": "^18.0.0 || ^19.0.0",
38
38
  "react-dom": "^18.0.0 || ^19.0.0",
39
- "@hachej/boring-workspace": "0.1.41"
39
+ "@hachej/boring-workspace": "0.1.43"
40
40
  },
41
41
  "dependencies": {
42
42
  "lucide-react": "^1.8.0",
43
- "@hachej/boring-data-explorer": "0.1.41"
43
+ "@hachej/boring-data-explorer": "0.1.43"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@testing-library/jest-dom": "^6.9.1",
@@ -55,7 +55,7 @@
55
55
  "tsup": "^8.0.0",
56
56
  "typescript": "^5.4.0",
57
57
  "vitest": "^3.2.6",
58
- "@hachej/boring-workspace": "0.1.41"
58
+ "@hachej/boring-workspace": "0.1.43"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "tsup",