@nbt-dev/devtools 0.0.1

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,73 @@
1
+ # @nbt-dev/devtools
2
+
3
+ A drop-in React devtools panel for any frontend backed by a [nimbit
4
+ console](https://github.com/nbt-dev). It gives you, against the live daemon:
5
+
6
+ - **Console** — streaming server logs (`/_console/logs/ws`)
7
+ - **Data** — a live, virtualized browser over every installed cartridge's
8
+ entities, streamed off the bulk WebSocket (`/_ws/bulk`) with search + row
9
+ detail
10
+ - **Diagram** — an entity-relationship graph with live row counts
11
+
12
+ It assumes your app uses nimbit auth: the data socket reads the current session
13
+ bearer from `POST /api/auth/session/current` (sent with `credentials:
14
+ "include"`), same as the console inspector.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install @nbt-dev/devtools
20
+ ```
21
+
22
+ `react` and `react-dom` (>=18) are peer dependencies.
23
+
24
+ ## Usage
25
+
26
+ Mount the panel once near your app root and import the stylesheet once anywhere:
27
+
28
+ ```tsx
29
+ import { NimbitDevTools } from "@nbt-dev/devtools";
30
+ import "@nbt-dev/devtools/styles.css";
31
+
32
+ export default function App() {
33
+ return (
34
+ <>
35
+ <YourApp />
36
+ {/* Empty string => same-origin. Point at the console otherwise. */}
37
+ <NimbitDevTools apiBaseUrl="https://console.example.com" />
38
+ </>
39
+ );
40
+ }
41
+ ```
42
+
43
+ The panel renders nothing until toggled. Open/close it from anywhere:
44
+
45
+ ```ts
46
+ import { toggleDevTools } from "@nbt-dev/devtools";
47
+
48
+ // e.g. bind to Ctrl+F12
49
+ toggleDevTools();
50
+ // or, with no import: window.dispatchEvent(new CustomEvent("devtools-toggle"))
51
+ ```
52
+
53
+ ### Next.js
54
+
55
+ `NimbitDevTools` is a client component (the bundle carries a `"use client"`
56
+ banner). Render it inside a client boundary / your root layout's client tree.
57
+
58
+ ## Styling
59
+
60
+ The shipped `styles.css` is self-contained and scoped under `.nimbit-devtools`
61
+ (the panel + its portalled surfaces). It ships only Tailwind's theme + utilities
62
+ layers — no preflight reset — so it never disturbs your app's own styles. Works
63
+ with or without Tailwind in the host.
64
+
65
+ ## API
66
+
67
+ | Export | Description |
68
+ | --- | --- |
69
+ | `NimbitDevTools` | The panel. Props: `apiBaseUrl?`, `defaultActiveTab?`. |
70
+ | `toggleDevTools()` | Dispatches the `devtools-toggle` window event. |
71
+ | `DevToolsProvider`, `useDevTools` | Lower-level state if you compose `DevTools` yourself. |
72
+ | `DevToolsConfigProvider`, `useDevToolsConfig` | The `apiBaseUrl` config context. |
73
+ | `DevTools` | The bare panel (expects the providers above). |
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ declare const ConsoleTab: React.FC;
3
+ export default ConsoleTab;
@@ -0,0 +1,16 @@
1
+ import { type ColumnDef, type SchemaFrame, type DeltaFrame } from "../../../generated/bulk-protocol";
2
+ export declare function getFrameType(buf: ArrayBuffer): number;
3
+ export declare function getFrameSid(buf: ArrayBuffer): number;
4
+ export declare function parseSchema(buf: ArrayBuffer): SchemaFrame;
5
+ export declare function parseDataChunk(buf: ArrayBuffer, columns: ColumnDef[]): string[][];
6
+ export declare function parseDelta(buf: ArrayBuffer, columns: ColumnDef[]): DeltaFrame;
7
+ export declare function parseError(buf: ArrayBuffer): string;
8
+ export declare function encodeSchemaCmd(sid: number, cart: string, entity: string): string;
9
+ export declare function encodeStreamCmd(sid: number, cart: string, entity: string, opts?: {
10
+ sort?: string;
11
+ sort_desc?: boolean;
12
+ filters?: Record<string, string>;
13
+ }): string;
14
+ export declare function encodeSearchCmd(sid: number, query: string): string;
15
+ export declare function encodeUnsubCmd(sid: number): string;
16
+ export declare function encodeClearSearchCmd(sid: number): string;
@@ -0,0 +1,18 @@
1
+ import { type ColumnDef, type SchemaFrame, type DeltaFrame } from "../../../generated/bulk-protocol";
2
+ export declare class BulkDataStore {
3
+ columns: ColumnDef[];
4
+ rows: string[][];
5
+ totalRows: number;
6
+ searchActive: boolean;
7
+ private _fullRows;
8
+ private _fullTotalRows;
9
+ private _idColIndex;
10
+ applySchema(schema: SchemaFrame): void;
11
+ appendChunk(chunk: string[][]): void;
12
+ applyDelta(delta: DeltaFrame): void;
13
+ enterSearch(schema: SchemaFrame): void;
14
+ exitSearch(): void;
15
+ getRowCount(): number;
16
+ private _findRowById;
17
+ private _findRowByIdStr;
18
+ }
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ type DataTableProps = {
3
+ cart: string;
4
+ entity: string;
5
+ searchFields: readonly string[];
6
+ onSelectRow: (rowJson: Record<string, string>) => void;
7
+ };
8
+ declare const DataTable: React.FC<DataTableProps>;
9
+ export default DataTable;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ declare const DataTab: React.FC;
3
+ export default DataTab;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ declare const DevTools: React.FC;
3
+ export default DevTools;
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+ export type DevToolsDock = "bottom" | "right";
3
+ export type DevToolsTab = {
4
+ id: string;
5
+ title: string;
6
+ icon?: React.ReactNode;
7
+ render: () => React.ReactNode;
8
+ };
9
+ export type DevToolsSize = {
10
+ h: number;
11
+ w: number;
12
+ };
13
+ type DevToolsCtx = {
14
+ open: boolean;
15
+ setOpen: (v: boolean) => void;
16
+ toggle: () => void;
17
+ dock: DevToolsDock;
18
+ setDock: (d: DevToolsDock) => void;
19
+ activeTab: string | null;
20
+ setActiveTab: (id: string) => void;
21
+ size: DevToolsSize;
22
+ setSize: (s: DevToolsSize | ((prev: DevToolsSize) => DevToolsSize)) => void;
23
+ maximized: boolean;
24
+ setMaximized: (v: boolean) => void;
25
+ dataCart: string | null;
26
+ setDataCart: (v: string | null) => void;
27
+ dataEntity: string | null;
28
+ setDataEntity: (v: string | null) => void;
29
+ };
30
+ export declare const DevToolsProvider: React.FC<{
31
+ children: React.ReactNode;
32
+ defaultActiveTab?: string;
33
+ }>;
34
+ export declare function useDevTools(): DevToolsCtx;
35
+ export {};
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const DiagramView: React.FC;
@@ -0,0 +1,92 @@
1
+ export type GraphInputField = {
2
+ name: string;
3
+ type: string;
4
+ optional?: boolean;
5
+ array?: boolean;
6
+ kind?: string;
7
+ target?: string;
8
+ targetCart?: string;
9
+ relationKind?: string;
10
+ fkField?: string;
11
+ };
12
+ export type GraphInputEntity = {
13
+ name: string;
14
+ fields: GraphInputField[];
15
+ };
16
+ export type GraphInputCart = {
17
+ name: string;
18
+ entities: GraphInputEntity[];
19
+ };
20
+ export type EntityGraphField = {
21
+ name: string;
22
+ displayName: string;
23
+ type: string;
24
+ kind: string;
25
+ optional: boolean;
26
+ array: boolean;
27
+ target?: string;
28
+ targetCart?: string;
29
+ relationKind?: string;
30
+ fkField?: string;
31
+ implicit?: boolean;
32
+ };
33
+ export type EntityGraphNodeData = {
34
+ id: string;
35
+ cartridge: string;
36
+ entity: string;
37
+ fields: EntityGraphField[];
38
+ fieldCount: number;
39
+ scalarCount: number;
40
+ relationCount: number;
41
+ documentCount: number;
42
+ rowCount: number;
43
+ };
44
+ export type EntityGraphNode = EntityGraphNodeData & {
45
+ position: {
46
+ x: number;
47
+ y: number;
48
+ };
49
+ };
50
+ export type EntityGraphEdge = {
51
+ id: string;
52
+ source: string;
53
+ target: string;
54
+ sourceField: string;
55
+ targetField: string;
56
+ label: string;
57
+ relationKind?: string;
58
+ fkField?: string;
59
+ };
60
+ export type EntityGraphTotals = {
61
+ entities: number;
62
+ relationships: number;
63
+ rows: number;
64
+ };
65
+ export type EntityGraphModel = {
66
+ nodes: EntityGraphNode[];
67
+ edges: EntityGraphEdge[];
68
+ totals: EntityGraphTotals;
69
+ };
70
+ type ContractField = {
71
+ name: string;
72
+ type: string;
73
+ optional?: boolean;
74
+ array?: boolean;
75
+ kind?: string;
76
+ target?: string;
77
+ target_cart?: string;
78
+ relation_kind?: string;
79
+ fk_field?: string;
80
+ };
81
+ type ContractEntity = {
82
+ fields?: ContractField[];
83
+ };
84
+ export type Contract = {
85
+ cartridge?: string;
86
+ owns?: Record<string, ContractEntity>;
87
+ };
88
+ export declare function cartsFromContracts(contracts: Contract[]): GraphInputCart[];
89
+ export declare function entityGraphId(cartridge: string, entity: string): string;
90
+ export declare function buildEntityGraphModel(cartridges: GraphInputCart[]): EntityGraphModel;
91
+ export declare function filterEntityGraphModel(model: EntityGraphModel, visibleIds: Set<string>): EntityGraphModel;
92
+ export {};
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import { type NodeProps } from "@xyflow/react";
3
+ export type EntityNodeHighlight = {
4
+ focused: boolean;
5
+ connected: boolean;
6
+ dimmed: boolean;
7
+ fields: string[];
8
+ };
9
+ export declare function EntityNode({ data, selected }: NodeProps): React.JSX.Element;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ declare const NetworkTab: React.FC;
3
+ export default NetworkTab;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ declare const SettingsTab: React.FC;
3
+ export default SettingsTab;
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ declare const buttonVariants: (props?: ({
4
+ variant?: "link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | null | undefined;
5
+ size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
8
+ asChild?: boolean;
9
+ }): React.JSX.Element;
10
+ export { Button, buttonVariants };
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ import { Dialog as SheetPrimitive } from "radix-ui";
3
+ declare function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>): React.JSX.Element;
4
+ declare function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>): React.JSX.Element;
5
+ declare function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>): React.JSX.Element;
6
+ declare function SheetContent({ className, children, side, showCloseButton, ...props }: React.ComponentProps<typeof SheetPrimitive.Content> & {
7
+ side?: "top" | "right" | "bottom" | "left";
8
+ showCloseButton?: boolean;
9
+ }): React.JSX.Element;
10
+ declare function SheetHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
11
+ declare function SheetFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
12
+ declare function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>): React.JSX.Element;
13
+ declare function SheetDescription({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Description>): React.JSX.Element;
14
+ export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ export type DevToolsConfig = {
3
+ apiBaseUrl: string;
4
+ };
5
+ export declare const DevToolsConfigProvider: React.FC<{
6
+ apiBaseUrl?: string;
7
+ children: React.ReactNode;
8
+ }>;
9
+ export declare function useDevToolsConfig(): DevToolsConfig;
10
+ export declare function wsBaseFrom(apiBaseUrl: string): string;
@@ -0,0 +1,38 @@
1
+ export declare const FRAME_SCHEMA = 1;
2
+ export declare const FRAME_DATA = 2;
3
+ export declare const FRAME_DATA_END = 3;
4
+ export declare const FRAME_DELTA_INS = 4;
5
+ export declare const FRAME_DELTA_UPD = 5;
6
+ export declare const FRAME_DELTA_DEL = 6;
7
+ export declare const FRAME_SEARCH_RESULT = 7;
8
+ export declare const FRAME_SEARCH_END = 8;
9
+ export declare const FRAME_ERROR = 255;
10
+ export declare const TYPE_U8 = 1;
11
+ export declare const TYPE_U16 = 2;
12
+ export declare const TYPE_U32 = 3;
13
+ export declare const TYPE_U64 = 4;
14
+ export declare const TYPE_S8 = 5;
15
+ export declare const TYPE_S16 = 6;
16
+ export declare const TYPE_S32 = 7;
17
+ export declare const TYPE_S64 = 8;
18
+ export declare const TYPE_BOOL = 9;
19
+ export declare const TYPE_FLOAT32 = 10;
20
+ export declare const TYPE_FLOAT64 = 11;
21
+ export declare const TYPE_STRING = 12;
22
+ export declare const TYPE_DATETIME = 13;
23
+ export declare const TYPE_DOCUMENT = 14;
24
+ export type ColumnDef = {
25
+ name: string;
26
+ type: number;
27
+ fixedSize: number;
28
+ };
29
+ export type SchemaFrame = {
30
+ totalRows: number;
31
+ columns: ColumnDef[];
32
+ };
33
+ export type DeltaFrame = {
34
+ op: number;
35
+ rowData?: string[];
36
+ id?: string;
37
+ };
38
+ export declare function fixedSizeForType(type: number): number;
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import { BulkDataStore } from "../components/devtools/data-browser/data-store";
3
+ import { type ColumnDef } from "../generated/bulk-protocol";
4
+ import type { BulkRegistry } from "./use-cartridge-info";
5
+ export type BulkSubscription = {
6
+ store: BulkDataStore;
7
+ connected: boolean;
8
+ streaming: boolean;
9
+ error: string | null;
10
+ columns: ColumnDef[];
11
+ totalRows: number;
12
+ loadedRows: number;
13
+ search: (q: string) => void;
14
+ clearSearch: () => void;
15
+ setOnRender: (fn: (() => void) | null) => void;
16
+ };
17
+ type Props = {
18
+ registry: BulkRegistry;
19
+ children: React.ReactNode;
20
+ };
21
+ export declare function BulkStreamProvider({ registry, children }: Props): React.ReactElement;
22
+ export declare function useBulkRowCounts(registry: BulkRegistry): Record<string, number>;
23
+ export declare function useBulkSubscription(cart: string, entity: string): BulkSubscription;
24
+ export {};
@@ -0,0 +1,13 @@
1
+ export type BulkEntity = {
2
+ name: string;
3
+ route: string;
4
+ searchFields: readonly string[];
5
+ };
6
+ export type BulkRegistry = Record<string, BulkEntity[]>;
7
+ export type LiveRegistryState = {
8
+ registry: BulkRegistry;
9
+ carts: string[];
10
+ loading: boolean;
11
+ error: string | null;
12
+ };
13
+ export declare function useLiveBulkRegistry(): LiveRegistryState;
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ export type NimbitDevToolsProps = {
3
+ apiBaseUrl?: string;
4
+ defaultActiveTab?: string;
5
+ };
6
+ export declare const NimbitDevTools: React.FC<NimbitDevToolsProps>;
7
+ export default NimbitDevTools;
8
+ export declare function toggleDevTools(): void;
9
+ export { DevToolsProvider, useDevTools, } from "./components/devtools/devtools-context";
10
+ export { DevToolsConfigProvider, useDevToolsConfig } from "./config";
11
+ export type { DevToolsConfig } from "./config";
12
+ export { default as DevTools } from "./components/devtools/dev-tools";
13
+ export type { DevToolsDock, DevToolsTab, DevToolsSize, } from "./components/devtools/devtools-context";