@rebasepro/agent-skills 0.7.0 → 0.9.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/LICENSE +21 -0
- package/package.json +9 -3
- package/skills/rebase-admin/SKILL.md +32 -32
- package/skills/rebase-api/SKILL.md +6 -6
- package/skills/rebase-auth/SKILL.md +52 -12
- package/skills/rebase-backend-postgres/SKILL.md +3 -3
- package/skills/rebase-basics/SKILL.md +11 -6
- package/skills/rebase-collections/SKILL.md +84 -46
- package/skills/rebase-cron-jobs/SKILL.md +5 -0
- package/skills/rebase-custom-functions/SKILL.md +21 -0
- package/skills/rebase-deployment/SKILL.md +12 -3
- package/skills/rebase-email/SKILL.md +1 -1
- package/skills/rebase-entity-history/SKILL.md +9 -9
- package/skills/rebase-realtime/SKILL.md +5 -5
- package/skills/rebase-sdk/SKILL.md +23 -0
- package/skills/rebase-security/SKILL.md +68 -28
- package/skills/rebase-storage/SKILL.md +180 -12
- package/skills/rebase-studio/SKILL.md +8 -8
- package/skills/rebase-ui-components/SKILL.md +91 -0
- package/skills/rebase-webhooks/SKILL.md +25 -30
|
@@ -848,6 +848,97 @@ Key props: `data`, `columns` (array of `VirtualTableColumn`), `cellRenderer`, `o
|
|
|
848
848
|
|
|
849
849
|
See `VirtualTableProps` and `VirtualTableColumn` types for full API.
|
|
850
850
|
|
|
851
|
+
#### Generic Editable Tables & Cell Selection
|
|
852
|
+
|
|
853
|
+
`@rebasepro/ui` includes a generic, high-performance selection engine and primitive cell inputs that allow you to construct keyboard-navigable editable grid tables without any Rebase database dependencies.
|
|
854
|
+
|
|
855
|
+
**Selection Hooks & Context:**
|
|
856
|
+
* `createVirtualTableSelectionStore()` — Instantiates a selection store tracking active selection by `rowId` and `columnKey`. It uses `useSyncExternalStore` so that selection changes only trigger re-renders on the active cell (preventing whole-table lag).
|
|
857
|
+
* `VirtualTableSelectionProvider` — React context provider to pass down the selection store.
|
|
858
|
+
* `useVirtualTableSelection()` — Hook to access the selection store and triggers inside custom cells.
|
|
859
|
+
* `useVirtualTableCellSelected(store, columnKey, rowId)` — Returns `boolean` indicating if this cell is currently focused.
|
|
860
|
+
|
|
861
|
+
**Generic Cell Inputs:**
|
|
862
|
+
* `VirtualTableInput` — Auto-growing, debounced multiline/single text input.
|
|
863
|
+
* `VirtualTableNumberInput` — Constrained numeric input.
|
|
864
|
+
* `VirtualTableSwitch` — Small boolean switch toggle.
|
|
865
|
+
* `VirtualTableDateField` — Decoupled Date/DateTime picker (accepts `locale`, `timezone`, `mode`).
|
|
866
|
+
* `VirtualTableSelect` — Option-based single/multi dropdown list using chips. Takes `options: { value, label, color? }[]`.
|
|
867
|
+
|
|
868
|
+
**Example Custom Editable Table:**
|
|
869
|
+
```tsx
|
|
870
|
+
import React, { useMemo, useState, useCallback } from "react";
|
|
871
|
+
import {
|
|
872
|
+
VirtualTable,
|
|
873
|
+
createVirtualTableSelectionStore,
|
|
874
|
+
VirtualTableSelectionProvider,
|
|
875
|
+
useVirtualTableCellSelected,
|
|
876
|
+
useVirtualTableSelection,
|
|
877
|
+
VirtualTableInput
|
|
878
|
+
} from "@rebasepro/ui";
|
|
879
|
+
|
|
880
|
+
function EditableCell({ rowId, columnKey, value, onUpdate }) {
|
|
881
|
+
const { selectionStore } = useVirtualTableSelection();
|
|
882
|
+
const selected = useVirtualTableCellSelected(selectionStore, columnKey, rowId);
|
|
883
|
+
|
|
884
|
+
return (
|
|
885
|
+
<div
|
|
886
|
+
className="w-full h-full flex items-center px-2 cursor-text"
|
|
887
|
+
onClick={(e) => selectionStore.select({
|
|
888
|
+
rowId,
|
|
889
|
+
columnKey,
|
|
890
|
+
cellRect: e.currentTarget.getBoundingClientRect(),
|
|
891
|
+
width: e.currentTarget.offsetWidth,
|
|
892
|
+
height: e.currentTarget.offsetHeight
|
|
893
|
+
})}
|
|
894
|
+
>
|
|
895
|
+
<VirtualTableInput
|
|
896
|
+
value={value}
|
|
897
|
+
updateValue={onUpdate}
|
|
898
|
+
focused={selected}
|
|
899
|
+
disabled={false}
|
|
900
|
+
multiline={false}
|
|
901
|
+
/>
|
|
902
|
+
</div>
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export function MyEditableGrid() {
|
|
907
|
+
const [data, setData] = useState([
|
|
908
|
+
{ id: "row-1", name: "Project Alpha" },
|
|
909
|
+
{ id: "row-2", name: "Project Beta" }
|
|
910
|
+
]);
|
|
911
|
+
|
|
912
|
+
const selectionStore = useMemo(() => createVirtualTableSelectionStore(), []);
|
|
913
|
+
|
|
914
|
+
const updateCell = useCallback((rowId: string, value: string) => {
|
|
915
|
+
setData(prev => prev.map(row => row.id === rowId ? { ...row, name: value } : row));
|
|
916
|
+
}, []);
|
|
917
|
+
|
|
918
|
+
const columns = [
|
|
919
|
+
{ key: "name", title: "Name", width: 300 }
|
|
920
|
+
];
|
|
921
|
+
|
|
922
|
+
return (
|
|
923
|
+
<VirtualTableSelectionProvider store={selectionStore}>
|
|
924
|
+
<VirtualTable
|
|
925
|
+
data={data}
|
|
926
|
+
columns={columns}
|
|
927
|
+
rowHeight={52}
|
|
928
|
+
cellRenderer={({ rowData, column }) => (
|
|
929
|
+
<EditableCell
|
|
930
|
+
rowId={rowData.id}
|
|
931
|
+
columnKey={column.key}
|
|
932
|
+
value={rowData.name}
|
|
933
|
+
onUpdate={(val) => updateCell(rowData.id, val || "")}
|
|
934
|
+
/>
|
|
935
|
+
)}
|
|
936
|
+
/>
|
|
937
|
+
</VirtualTableSelectionProvider>
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
851
942
|
### ToggleButtonGroup
|
|
852
943
|
|
|
853
944
|
Single-select button group for toggling between options.
|
|
@@ -5,7 +5,7 @@ description: Guide for sending outbound HTTP webhooks on entity changes in a Reb
|
|
|
5
5
|
|
|
6
6
|
# Rebase Webhooks
|
|
7
7
|
|
|
8
|
-
> **IMPORTANT FOR AGENTS**: The `WebhookDispatcher` is a **standalone service class** exported from `@rebasepro/server-core`. It is **not** auto-wired into the backend init pipeline — you must instantiate it yourself and call `onEntityChange()` from your application code (e.g.
|
|
8
|
+
> **IMPORTANT FOR AGENTS**: The `WebhookDispatcher` is a **standalone service class** exported from `@rebasepro/server-core`. It is **not** auto-wired into the backend init pipeline — you must instantiate it yourself and call `onEntityChange()` from your application code (e.g. collection callbacks, custom functions, or cron jobs). Do NOT look for a `webhooks` key in `RebaseBackendConfig`.
|
|
9
9
|
|
|
10
10
|
> **IMPORTANT FOR AGENTS**: Webhooks are **outbound** HTTP POST requests sent by your Rebase backend to external URLs. They are NOT inbound endpoints. To receive webhooks FROM external services, use Rebase custom functions instead (see `rebase-custom-functions` skill).
|
|
11
11
|
|
|
@@ -71,7 +71,7 @@ dispatcher.setWebhooks([
|
|
|
71
71
|
|
|
72
72
|
### Dispatch on Entity Changes
|
|
73
73
|
|
|
74
|
-
Call `onEntityChange()` whenever
|
|
74
|
+
Call `onEntityChange()` whenever a entity is created, updated, or deleted. The dispatcher checks all registered webhooks and fires matching ones.
|
|
75
75
|
|
|
76
76
|
```typescript
|
|
77
77
|
const results = await dispatcher.onEntityChange(
|
|
@@ -355,13 +355,13 @@ If the receiver does not respond within 10 seconds, the request is aborted and t
|
|
|
355
355
|
|
|
356
356
|
## Integration Patterns
|
|
357
357
|
|
|
358
|
-
### With
|
|
358
|
+
### With Collection Callbacks
|
|
359
359
|
|
|
360
|
-
The most common pattern is to wire the dispatcher into Rebase
|
|
360
|
+
The most common pattern is to wire the dispatcher into Rebase collection callbacks so webhooks fire automatically on CRUD operations:
|
|
361
361
|
|
|
362
362
|
```typescript
|
|
363
363
|
// backend/collections/orders.ts
|
|
364
|
-
import type {
|
|
364
|
+
import type { CollectionConfig, CollectionCallbacks } from "@rebasepro/types";
|
|
365
365
|
import { WebhookDispatcher } from "@rebasepro/server-core/services/webhook-service";
|
|
366
366
|
|
|
367
367
|
const dispatcher = new WebhookDispatcher();
|
|
@@ -376,35 +376,28 @@ dispatcher.setWebhooks([
|
|
|
376
376
|
},
|
|
377
377
|
]);
|
|
378
378
|
|
|
379
|
-
const callbacks:
|
|
380
|
-
|
|
379
|
+
const callbacks: CollectionCallbacks = {
|
|
380
|
+
afterSave: async ({ id, values, previousValues, status, collection }) => {
|
|
381
|
+
const event = status === "new" ? "INSERT" : "UPDATE";
|
|
381
382
|
await dispatcher.onEntityChange(
|
|
382
383
|
collection.path,
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
},
|
|
388
|
-
afterUpdate: async ({ entity, previousEntity, collection }) => {
|
|
389
|
-
await dispatcher.onEntityChange(
|
|
390
|
-
collection.path,
|
|
391
|
-
"UPDATE",
|
|
392
|
-
entity.id,
|
|
393
|
-
entity,
|
|
394
|
-
previousEntity
|
|
384
|
+
event,
|
|
385
|
+
String(id),
|
|
386
|
+
values,
|
|
387
|
+
status === "new" ? undefined : previousValues
|
|
395
388
|
);
|
|
396
389
|
},
|
|
397
|
-
afterDelete: async ({
|
|
390
|
+
afterDelete: async ({ id, collection }) => {
|
|
398
391
|
await dispatcher.onEntityChange(
|
|
399
392
|
collection.path,
|
|
400
393
|
"DELETE",
|
|
401
|
-
|
|
394
|
+
String(id),
|
|
402
395
|
null
|
|
403
396
|
);
|
|
404
397
|
},
|
|
405
398
|
};
|
|
406
399
|
|
|
407
|
-
const ordersCollection:
|
|
400
|
+
const ordersCollection: CollectionConfig = {
|
|
408
401
|
name: "Orders",
|
|
409
402
|
path: "orders",
|
|
410
403
|
callbacks,
|
|
@@ -519,9 +512,11 @@ Then import it from any callback or function:
|
|
|
519
512
|
```typescript
|
|
520
513
|
import { dispatcher } from "../lib/webhooks";
|
|
521
514
|
|
|
522
|
-
// In
|
|
523
|
-
|
|
524
|
-
|
|
515
|
+
// In a collection callback:
|
|
516
|
+
afterSave: async ({ id, values, status, collection }) => {
|
|
517
|
+
if (status === "new") {
|
|
518
|
+
await dispatcher.onEntityChange(collection.path, "INSERT", String(id), values);
|
|
519
|
+
}
|
|
525
520
|
},
|
|
526
521
|
```
|
|
527
522
|
|
|
@@ -535,7 +530,7 @@ Registers the list of webhooks to watch. Filters out any with `enabled: false`.
|
|
|
535
530
|
|-----------|------|-------------|
|
|
536
531
|
| `webhooks` | `WebhookConfig[]` | Array of webhook configurations. |
|
|
537
532
|
|
|
538
|
-
### `onEntityChange(table, event,
|
|
533
|
+
### `onEntityChange(table, event, id, entity, previousEntity?): Promise<WebhookDeliveryResult[]>`
|
|
539
534
|
|
|
540
535
|
Checks all registered webhooks for matching `table` + `event`, and dispatches to each match.
|
|
541
536
|
|
|
@@ -543,7 +538,7 @@ Checks all registered webhooks for matching `table` + `event`, and dispatches to
|
|
|
543
538
|
|-----------|------|-------------|
|
|
544
539
|
| `table` | `string` | The database table name (e.g. `"orders"`). |
|
|
545
540
|
| `event` | `"INSERT" \| "UPDATE" \| "DELETE"` | The type of entity change. |
|
|
546
|
-
| `
|
|
541
|
+
| `id` | `string` | The unique ID of the changed entity. |
|
|
547
542
|
| `entity` | `Record<string, unknown> \| null` | The current entity data. May be `null` for deletes. |
|
|
548
543
|
| `previousEntity` | `Record<string, unknown> \| null` | *(Optional)* The previous entity state. Only relevant for `UPDATE` events — included as `old_record` in the payload. |
|
|
549
544
|
|
|
@@ -557,7 +552,7 @@ The dispatcher supports three event types, passed as the `event` parameter to `o
|
|
|
557
552
|
|-------|-------------|--------------------|-----------------------|
|
|
558
553
|
| `INSERT` | A new entity was created | The new entity | `undefined` |
|
|
559
554
|
| `UPDATE` | An existing entity was modified | The updated entity | The entity before update |
|
|
560
|
-
| `DELETE` |
|
|
555
|
+
| `DELETE` | a entity was removed | The deleted entity (or `null`) | `undefined` |
|
|
561
556
|
|
|
562
557
|
## Error Handling
|
|
563
558
|
|
|
@@ -592,9 +587,9 @@ for (const result of results) {
|
|
|
592
587
|
If you don't want webhook delivery to block your API response, use fire-and-forget:
|
|
593
588
|
|
|
594
589
|
```typescript
|
|
595
|
-
|
|
590
|
+
afterSave: async ({ id, values, collection }) => {
|
|
596
591
|
// Fire-and-forget — don't await
|
|
597
|
-
dispatcher.onEntityChange(collection.path, "INSERT",
|
|
592
|
+
dispatcher.onEntityChange(collection.path, "INSERT", String(id), values)
|
|
598
593
|
.catch(err => console.error("Webhook dispatch error:", err));
|
|
599
594
|
},
|
|
600
595
|
```
|