@rebasepro/plugin-insights 0.4.0 → 0.5.0

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,115 @@
1
+ # @rebasepro/plugin-insights
2
+
3
+ Scorecard and KPI widget plugin for the Rebase admin panel.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @rebasepro/plugin-insights
9
+ ```
10
+
11
+ **Peer dependencies:** `react >= 19.0.0`, `react-dom >= 19.0.0`
12
+
13
+ ## What This Package Does
14
+
15
+ This plugin injects data-driven scorecard widgets into the Rebase admin UI. You define insight definitions with custom `data()` callbacks — use the Rebase SDK, call a custom function, or hit any external API. The plugin handles caching, rendering, and slot injection.
16
+
17
+ Widgets appear in three locations automatically:
18
+
19
+ - **Home page header** — KPI overview cards via the `home.children.start` slot
20
+ - **Collection list view** — Inline scorecards below the title, above the data list via `collection.insights`
21
+ - **Home page cards** — Compact metrics auto-extracted from collection insights via `home.card.insight`
22
+
23
+ Collection-level insights are the single source of truth: define once under `collections.<slug>`, and they render both in the collection view and on the home card.
24
+
25
+ ## Key Exports
26
+
27
+ | Export | Type | Description |
28
+ |---|---|---|
29
+ | `useInsightsPlugin` | Hook | Creates the plugin from an `InsightsPluginConfig`. Returns a `RebasePlugin` |
30
+ | `InsightsPluginConfig` | Type | Top-level config: `insights` (home + collections) and optional `cacheTTL` |
31
+ | `InsightDefinition` | Type | Single insight: `id`, `title`, `data()` callback, `scorecard` config |
32
+ | `InsightDataResult` | Type | Return type of `data()`: `{ rows: DataRow[] }` |
33
+ | `DataRow` | Type | `Record<string, string \| number \| boolean \| null>` |
34
+ | `ScorecardConfig` | Type | Field mapping for value, comparison, icon, dateRange |
35
+ | `ScorecardFormat` | Type | Number formatting: style (decimal/currency/percent), notation, currency, decimals, showSign |
36
+ | `InsightsProvider` | Component | React context provider (injected automatically by the plugin) |
37
+ | `useInsightsEngine` | Hook | Access the insights engine from context (advanced) |
38
+ | `InsightsCache` | Class | TTL-based cache for insight data (advanced) |
39
+ | `useInsightsData` | Hook | Fetch and cache data for a specific insight (advanced) |
40
+ | `InsightsScorecardView` | Component | Renders a scorecard from data + config (custom layouts) |
41
+ | `InsightWidget` | Component | Single insight widget container (custom layouts) |
42
+ | `InsightWidgetSkeleton` | Component | Loading placeholder for insight widgets |
43
+
44
+ ### `InsightsPluginConfig`
45
+
46
+ | Prop | Type | Default | Description |
47
+ |---|---|---|---|
48
+ | `insights.home` | `InsightDefinition[]` | — | Insights shown at the top of the home page |
49
+ | `insights.collections` | `Record<string, InsightDefinition[]>` | — | Insights per collection slug |
50
+ | `cacheTTL` | `number` | `60_000` | Cache TTL in milliseconds |
51
+
52
+ ### `ScorecardConfig`
53
+
54
+ | Prop | Type | Description |
55
+ |---|---|---|
56
+ | `value.field` | `string` | Column name from data rows for the main value |
57
+ | `value.format` | `ScorecardFormat` | Number formatting options |
58
+ | `comparison.field` | `string` | Column name for comparison/delta value |
59
+ | `comparison.format` | `ScorecardFormat` | Formatting for comparison |
60
+ | `comparison.intent` | `"increase_is_good" \| "decrease_is_good"` | Controls green/red coloring |
61
+ | `icon` | `string` | Icon key (e.g., `"shopping_cart"`) |
62
+ | `dateRange` | `string` | Label text (e.g., `"Last 30 days"`) |
63
+
64
+ ## Quick Start
65
+
66
+ ```tsx
67
+ import { useInsightsPlugin } from "@rebasepro/plugin-insights";
68
+
69
+ const insightsPlugin = useInsightsPlugin({
70
+ cacheTTL: 120_000,
71
+ insights: {
72
+ home: [
73
+ {
74
+ id: "revenue",
75
+ title: "Revenue",
76
+ data: async () => {
77
+ const res = await fetch("/api/analytics/revenue");
78
+ return { rows: [await res.json()] };
79
+ },
80
+ scorecard: {
81
+ value: { field: "total", format: { style: "currency", currency: "USD", notation: "compact" } },
82
+ comparison: { field: "delta_pct", format: { style: "percent", decimals: 1 }, intent: "increase_is_good" },
83
+ icon: "attach_money",
84
+ dateRange: "Last 30 days"
85
+ }
86
+ }
87
+ ],
88
+ collections: {
89
+ orders: [
90
+ {
91
+ id: "total_orders",
92
+ title: "Total Orders",
93
+ data: async () => {
94
+ const count = await rebaseClient.data.orders.count();
95
+ return { rows: [{ total: count }] };
96
+ },
97
+ scorecard: {
98
+ value: { field: "total", format: { style: "decimal", notation: "compact" } },
99
+ icon: "shopping_cart"
100
+ }
101
+ }
102
+ ]
103
+ }
104
+ }
105
+ });
106
+
107
+ // Pass to your Rebase app:
108
+ <RebaseFirebaseApp plugins={[insightsPlugin]} />
109
+ ```
110
+
111
+ ## Related Packages
112
+
113
+ - `@rebasepro/core` — Core framework providing the plugin system
114
+ - `@rebasepro/types` — Shared types (`RebasePlugin`, `SlotContribution`)
115
+ - `@rebasepro/ui` — UI components used by insight widgets
@@ -1,6 +1,14 @@
1
- import { AuthController, Entity, EntityCollection, User } from "@rebasepro/types";
2
- export declare function checkOperation<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>, entity: Entity<M> | null, targetOperation: "select" | "insert" | "update" | "delete"): boolean;
3
- export declare function canReadCollection<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>): boolean;
4
- export declare function canEditEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>, path: string, entity: Entity<M> | null): boolean;
5
- export declare function canCreateEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>, path: string, entity: Entity<M> | null): boolean;
6
- export declare function canDeleteEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>, path: string, entity: Entity<M> | null): boolean;
1
+ import { Entity, EntityCollection, User } from "@rebasepro/types";
2
+ /**
3
+ * Minimal auth context for permission checking.
4
+ * Only requires the user object avoids forcing callers to construct
5
+ * a full AuthController just to check permissions.
6
+ */
7
+ export interface AuthContext<USER extends User = User> {
8
+ user: USER | null;
9
+ }
10
+ export declare function checkOperation<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, entity: Entity<M> | null, targetOperation: "select" | "insert" | "update" | "delete"): boolean;
11
+ export declare function canReadCollection<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>): boolean;
12
+ export declare function canEditEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
13
+ export declare function canCreateEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
14
+ export declare function canDeleteEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
@@ -1,5 +1,6 @@
1
1
  import type { Entity } from "./entities";
2
2
  import type { EntityCollection, FilterValues, WhereFilterOp } from "./collections";
3
+ import type { AuthAdapter } from "./auth_adapter";
3
4
  /**
4
5
  * Abstract database connection interface.
5
6
  * Represents a connection to any database system.
@@ -182,6 +183,24 @@ export interface RealtimeProvider {
182
183
  * Notify all relevant subscribers of an entity update
183
184
  */
184
185
  notifyEntityUpdate(path: string, entityId: string, entity: Entity | null, databaseId?: string): Promise<void>;
186
+ /**
187
+ * Called when the HTTP server is ready and listening.
188
+ * Useful for providers that need the server address for callbacks.
189
+ */
190
+ onServerReady?(serverInfo: {
191
+ port: number;
192
+ hostname?: string;
193
+ }): void;
194
+ /**
195
+ * Gracefully shut down the realtime provider.
196
+ * Called during server shutdown to clean up resources.
197
+ */
198
+ destroy?(): Promise<void>;
199
+ /**
200
+ * Stop the internal LISTEN client (e.g., PostgreSQL LISTEN/NOTIFY).
201
+ * Called during graceful shutdown before closing database connections.
202
+ */
203
+ stopListening?(): Promise<void>;
185
204
  }
186
205
  /**
187
206
  * Abstract collection registry interface.
@@ -464,6 +483,22 @@ export interface BackendBootstrapper {
464
483
  * (e.g., `"postgres"`, `"mongodb"`, `"mysql"`).
465
484
  */
466
485
  type: string;
486
+ /**
487
+ * Unique identifier for this bootstrapper instance.
488
+ * Used to register the driver in the driver registry.
489
+ * Defaults to `type` if not set.
490
+ */
491
+ id?: string;
492
+ /**
493
+ * Whether this bootstrapper provides the default driver.
494
+ * When true, the coordinator uses this driver as the primary one.
495
+ */
496
+ isDefault?: boolean;
497
+ /**
498
+ * Run database migrations for this driver.
499
+ * Called by the coordinator after all drivers are initialized.
500
+ */
501
+ runMigrations?(config: unknown, driverResult: InitializedDriver): Promise<void>;
467
502
  /**
468
503
  * Create a DataDriver from the given config.
469
504
  * This is the only **required** method.
@@ -498,7 +533,7 @@ export interface BackendBootstrapper {
498
533
  /**
499
534
  * Initialize WebSocket server for realtime operations.
500
535
  */
501
- initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: import("../controllers/data_driver").DataDriver, config?: unknown): Promise<void> | void;
536
+ initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: import("../controllers/data_driver").DataDriver, config?: unknown, authAdapter?: AuthAdapter): Promise<void> | void;
502
537
  }
503
538
  /**
504
539
  * Result of `BackendBootstrapper.initializeDriver()`.
@@ -343,6 +343,23 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
343
343
  * Builder for the collection actions rendered in the toolbar
344
344
  */
345
345
  Actions?: ComponentRef<CollectionActionsProps>[];
346
+ /**
347
+ * The database table name for this collection.
348
+ * Automatically set for PostgreSQL collections.
349
+ * For non-SQL backends, this may be undefined.
350
+ */
351
+ table?: string;
352
+ /**
353
+ * Relations defined for this collection.
354
+ * Populated at normalization time from inline relation properties
355
+ * or explicit relation definitions.
356
+ */
357
+ relations?: Relation[];
358
+ /**
359
+ * Security rules for this collection (Row Level Security).
360
+ * When defined, the backend enforces access control policies.
361
+ */
362
+ securityRules?: SecurityRule[];
346
363
  }
347
364
  /**
348
365
  * A collection backed by PostgreSQL (or any SQL database).
@@ -436,7 +453,10 @@ export interface MongoDBCollection<M extends Record<string, unknown> = Record<st
436
453
  * @group Models
437
454
  */
438
455
  export type EntityCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> = PostgresCollection<M, USER> | FirebaseCollection<M, USER> | MongoDBCollection<M, USER>;
439
- /** An EntityCollection that supports SQL-style relations (e.g. Postgres). */
456
+ /**
457
+ * An EntityCollection that supports SQL-style relations (e.g. Postgres).
458
+ * @deprecated Use `EntityCollection` directly — `table`, `relations`, and `securityRules` are now on `BaseEntityCollection`.
459
+ */
440
460
  export type CollectionWithRelations<M extends Record<string, unknown> = Record<string, unknown>> = EntityCollection<M> & {
441
461
  table?: string;
442
462
  relations?: Relation[];
@@ -641,14 +641,6 @@ export interface MapProperty extends BaseProperty {
641
641
  * Properties that are displayed when rendered as a preview
642
642
  */
643
643
  previewProperties?: string[];
644
- /**
645
- * Allow the user to add only some keys in this map.
646
- * By default, all properties of the map have the corresponding field in
647
- * the form view. Setting this flag to true allows to pick only some.
648
- * Useful for map that can have a lot of sub-properties that may not be
649
- * needed
650
- */
651
- pickOnlySomeKeys?: boolean;
652
644
  /**
653
645
  * Render this map as a key-value table that allows to use
654
646
  * arbitrary keys. You don't need to define the properties in this case.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/plugin-insights",
3
3
  "type": "module",
4
- "version": "0.4.0",
4
+ "version": "0.5.0",
5
5
  "main": "./dist/index.umd.js",
6
6
  "module": "./dist/index.es.js",
7
7
  "types": "./dist/index.d.ts",
@@ -16,9 +16,9 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "dependencies": {
19
- "@rebasepro/types": "0.4.0",
20
- "@rebasepro/core": "0.4.0",
21
- "@rebasepro/ui": "0.4.0"
19
+ "@rebasepro/core": "0.5.0",
20
+ "@rebasepro/types": "0.5.0",
21
+ "@rebasepro/ui": "0.5.0"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "react": ">=19.0.0",