@rebasepro/types 0.6.1 → 0.8.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.
Files changed (49) hide show
  1. package/dist/controllers/client.d.ts +69 -1
  2. package/dist/controllers/data.d.ts +19 -47
  3. package/dist/controllers/navigation.d.ts +14 -14
  4. package/dist/controllers/registry.d.ts +8 -4
  5. package/dist/index.es.js +164 -10
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +171 -9
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/types/api_keys.d.ts +57 -0
  10. package/dist/types/auth_adapter.d.ts +66 -4
  11. package/dist/types/backend.d.ts +5 -0
  12. package/dist/types/collections.d.ts +200 -90
  13. package/dist/types/data_source.d.ts +79 -3
  14. package/dist/types/entity_actions.d.ts +2 -2
  15. package/dist/types/entity_callbacks.d.ts +18 -6
  16. package/dist/types/entity_views.d.ts +1 -1
  17. package/dist/types/filter-operators.d.ts +108 -0
  18. package/dist/types/index.d.ts +4 -2
  19. package/dist/types/plugins.d.ts +1 -1
  20. package/dist/types/policy.d.ts +137 -0
  21. package/dist/types/properties.d.ts +122 -37
  22. package/dist/types/property_config.d.ts +1 -1
  23. package/dist/types/storage_source.d.ts +83 -0
  24. package/dist/types/translations.d.ts +8 -0
  25. package/package.json +1 -1
  26. package/src/controllers/client.ts +69 -1
  27. package/src/controllers/data.ts +20 -59
  28. package/src/controllers/navigation.ts +17 -16
  29. package/src/controllers/registry.ts +10 -4
  30. package/src/types/api_keys.ts +52 -0
  31. package/src/types/auth_adapter.ts +80 -4
  32. package/src/types/backend.ts +6 -1
  33. package/src/types/collections.ts +235 -113
  34. package/src/types/data_source.ts +89 -5
  35. package/src/types/entity_actions.tsx +2 -2
  36. package/src/types/entity_callbacks.ts +18 -7
  37. package/src/types/entity_views.tsx +1 -1
  38. package/src/types/filter-operators.ts +167 -0
  39. package/src/types/index.ts +5 -2
  40. package/src/types/plugins.tsx +1 -1
  41. package/src/types/policy.ts +173 -0
  42. package/src/types/properties.ts +132 -39
  43. package/src/types/property_config.tsx +0 -1
  44. package/src/types/storage_source.ts +90 -0
  45. package/src/types/translations.ts +8 -0
  46. package/dist/types/backend_hooks.d.ts +0 -109
  47. package/dist/types/entity_overrides.d.ts +0 -10
  48. package/src/types/backend_hooks.ts +0 -114
  49. package/src/types/entity_overrides.tsx +0 -11
@@ -4,15 +4,25 @@ import type { User } from "../users";
4
4
  import type { RebaseCallContext } from "../rebase_context";
5
5
 
6
6
  /**
7
- * This interface defines all the callbacks that can be used when an entity
8
- * is being created, updated or deleted.
9
- * Useful for adding your own logic or blocking the execution of the operation.
7
+ * Lifecycle callbacks for entity CRUD operations.
8
+ *
9
+ * Register per-collection on the collection's `callbacks` field, or globally
10
+ * via `initBackend({ callbacks })`. Fires on **every** data path — REST API,
11
+ * WebSocket / realtime subscriptions, and server-side `rebase.data`.
12
+ *
13
+ * When both global and per-collection callbacks are registered, execution
14
+ * order is: **global → collection → property callbacks**.
15
+ *
10
16
  * @group Models
11
17
  */
12
18
  export type EntityCallbacks<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> = {
13
19
 
14
20
  /**
15
- * Callback used after fetching data
21
+ * Callback used after fetching data.
22
+ *
23
+ * Fires on every read path. Use this for security-critical redaction
24
+ * (PII masking, row filtering) — no read path bypasses it.
25
+ *
16
26
  * @param props
17
27
  */
18
28
  afterRead?(props: EntityAfterReadProps<M, USER>)
@@ -24,13 +34,15 @@ export type EntityCallbacks<M extends Record<string, unknown> = Record<string, u
24
34
  * saved. If you throw an error in this method the process stops, and an
25
35
  * error snackbar gets displayed.
26
36
  * This runs after schema validation.
37
+ *
27
38
  * @param props
28
39
  */
29
40
  beforeSave?(props: EntityBeforeSaveProps<M, USER>)
30
41
  : Promise<Partial<EntityValues<M>>> | Partial<EntityValues<M>>;
31
42
 
32
43
  /**
33
- * Callback used when save is successful
44
+ * Callback used when save is successful.
45
+ *
34
46
  * @param props
35
47
  */
36
48
  afterSave?(props: EntityAfterSaveProps<M, USER>)
@@ -57,7 +69,7 @@ export type EntityCallbacks<M extends Record<string, unknown> = Record<string, u
57
69
  *
58
70
  * @param props
59
71
  */
60
- afterDelete?(props: EntityAfterDeleteProps<M, USER>): void;
72
+ afterDelete?(props: EntityAfterDeleteProps<M, USER>): Promise<void> | void;
61
73
 
62
74
  }
63
75
 
@@ -214,4 +226,3 @@ export interface EntityAfterDeleteProps<M extends Record<string, unknown> = Reco
214
226
  */
215
227
  context: RebaseCallContext<USER>;
216
228
  }
217
-
@@ -55,7 +55,7 @@ export interface FormContext<M extends Record<string, unknown> = Record<string,
55
55
 
56
56
  savingError?: Error;
57
57
 
58
- openEntityMode: "side_panel" | "full_screen" | "split" | "dialog";
58
+ openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
59
59
 
60
60
  /**
61
61
  * The underlying formex controller that powers the form.
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Canonical filter operators and REST wire-format mappings.
3
+ *
4
+ * `WhereFilterOp` is THE operator type used at every layer — from React
5
+ * components through the SDK, server, and down to the database driver.
6
+ *
7
+ * PostgREST short-codes (`eq`, `gt`, `cs`, …) exist **only** at the
8
+ * HTTP wire boundary, handled by `serializeFilter` / `deserializeFilter`
9
+ * in `@rebasepro/common`.
10
+ *
11
+ * ┌──────────────────┬───────────────┬──────────────────────────┐
12
+ * │ Canonical │ REST short │ Meaning │
13
+ * ├──────────────────┼───────────────┼──────────────────────────┤
14
+ * │ "==" │ "eq" │ Equal │
15
+ * │ "!=" │ "neq" │ Not equal │
16
+ * │ ">" │ "gt" │ Greater than │
17
+ * │ ">=" │ "gte" │ Greater than or equal │
18
+ * │ "<" │ "lt" │ Less than │
19
+ * │ "<=" │ "lte" │ Less than or equal │
20
+ * │ "in" │ "in" │ Value in list │
21
+ * │ "not-in" │ "nin" │ Value not in list │
22
+ * │ "array-contains" │ "cs" │ Array contains element │
23
+ * │ "array-contains-any" │ "csa" │ Array contains any of │
24
+ * └──────────────────┴───────────────┴──────────────────────────┘
25
+ *
26
+ * @module
27
+ */
28
+
29
+ /**
30
+ * Canonical filter operators supported across all database backends.
31
+ * Each DB driver translates these to its native query format.
32
+ *
33
+ * @group Models
34
+ */
35
+ export type WhereFilterOp =
36
+ | "<"
37
+ | "<="
38
+ | "=="
39
+ | "!="
40
+ | ">="
41
+ | ">"
42
+ | "array-contains"
43
+ | "in"
44
+ | "not-in"
45
+ | "array-contains-any";
46
+
47
+ /**
48
+ * Used to define filters applied in collections.
49
+ *
50
+ * A single condition is a tuple `[operator, value]`.
51
+ * Multiple conditions on the same field use an array of tuples.
52
+ *
53
+ * @example
54
+ * // Single condition per field
55
+ * { status: ["==", "active"], price: [">=", 9.99] }
56
+ *
57
+ * // Multiple conditions on one field
58
+ * { age: [[">=", 18], ["<", 65]] }
59
+ *
60
+ * // Array operators
61
+ * { role: ["in", ["admin", "editor"]] }
62
+ * { tags: ["array-contains", "featured"] }
63
+ *
64
+ * @group Models
65
+ */
66
+ export type FilterValues<Key extends string> =
67
+ Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][]>>;
68
+
69
+ /**
70
+ * Relaxed filter type that also accepts pre-serialized PostgREST strings.
71
+ * **Internal only** — used at the wire-format boundary
72
+ * (`serializeFilter` / `deserializeFilter` in `@rebasepro/common`).
73
+ *
74
+ * Application code, UI components, and SDK consumers should use
75
+ * {@link FilterValues} instead.
76
+ *
77
+ * @internal
78
+ */
79
+ export type WireFilterValues<Key extends string> =
80
+ Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][] | string>>;
81
+
82
+ /**
83
+ * A pre-defined filter preset for quick access in the collection toolbar.
84
+ * Users can select a preset to instantly apply a set of filters and
85
+ * optionally a sort order.
86
+ *
87
+ * @group Models
88
+ */
89
+ export interface FilterPreset<Key extends string = string> {
90
+ /**
91
+ * Display label shown in the preset menu.
92
+ * If omitted, a summary is auto-generated from the filter keys.
93
+ */
94
+ label?: string;
95
+
96
+ /**
97
+ * The filter values to apply when this preset is selected.
98
+ */
99
+ filterValues: FilterValues<Key>;
100
+
101
+ /**
102
+ * Optional sort override to apply alongside the filter values.
103
+ */
104
+ sort?: [Key, "asc" | "desc"];
105
+ }
106
+
107
+ /**
108
+ * PostgREST short-code operators. Wire format only — these never appear
109
+ * in application code. Used by `serializeFilter`/`deserializeFilter`
110
+ * in `@rebasepro/common`.
111
+ */
112
+ export type RestFilterOp =
113
+ | "eq" | "neq"
114
+ | "gt" | "gte"
115
+ | "lt" | "lte"
116
+ | "in" | "nin"
117
+ | "cs" | "csa";
118
+
119
+ /** Maps canonical operators to their REST short-code equivalents. */
120
+ export const CANONICAL_TO_REST: Readonly<Record<WhereFilterOp, RestFilterOp>> = {
121
+ "==": "eq",
122
+ "!=": "neq",
123
+ ">": "gt",
124
+ ">=": "gte",
125
+ "<": "lt",
126
+ "<=": "lte",
127
+ "in": "in",
128
+ "not-in": "nin",
129
+ "array-contains": "cs",
130
+ "array-contains-any": "csa"
131
+ };
132
+
133
+ /** Maps REST short-code operators to their canonical equivalents. */
134
+ export const REST_TO_CANONICAL: Readonly<Record<RestFilterOp, WhereFilterOp>> = {
135
+ "eq": "==",
136
+ "neq": "!=",
137
+ "gt": ">",
138
+ "gte": ">=",
139
+ "lt": "<",
140
+ "lte": "<=",
141
+ "in": "in",
142
+ "nin": "not-in",
143
+ "cs": "array-contains",
144
+ "csa": "array-contains-any"
145
+ };
146
+
147
+ /** All canonical operator strings for runtime validation. */
148
+ const CANONICAL_OPS: ReadonlySet<string> = new Set<WhereFilterOp>([
149
+ "<", "<=", "==", "!=", ">=", ">",
150
+ "in", "not-in",
151
+ "array-contains", "array-contains-any"
152
+ ]);
153
+
154
+ /**
155
+ * Resolve any operator string (canonical or REST short-code) to its
156
+ * canonical `WhereFilterOp` form. Returns `undefined` for unknown operators.
157
+ *
158
+ * @example
159
+ * toCanonicalOp("==") // "=="
160
+ * toCanonicalOp("eq") // "=="
161
+ * toCanonicalOp("cs") // "array-contains"
162
+ * toCanonicalOp("xyz") // undefined
163
+ */
164
+ export function toCanonicalOp(op: string): WhereFilterOp | undefined {
165
+ if (CANONICAL_OPS.has(op)) return op as WhereFilterOp;
166
+ return (REST_TO_CANONICAL as Record<string, WhereFilterOp | undefined>)[op];
167
+ }
@@ -1,15 +1,16 @@
1
1
  export * from "./entities";
2
+ export * from "./filter-operators";
2
3
  export * from "./chips";
3
4
 
4
5
  export * from "./properties";
5
6
  export * from "./collections";
6
7
  export * from "./relations";
8
+ export * from "./policy";
7
9
 
8
10
  export * from "./locales";
9
11
  export * from "./entity_link_builder";
10
12
  export * from "./user_management_delegate";
11
13
  export * from "./entity_callbacks";
12
- export * from "./entity_overrides";
13
14
  export * from "./export_import";
14
15
  export * from "./modify_collections";
15
16
  export * from "./formex";
@@ -23,10 +24,12 @@ export * from "./entity_actions";
23
24
  export * from "./property_config";
24
25
  export * from "./entity_views";
25
26
  export * from "./data_source";
27
+ export * from "./storage_source";
26
28
  export * from "./cron";
27
- export * from "./backend_hooks";
28
29
  export * from "./component_ref";
29
30
  export * from "./auth_adapter";
30
31
  export * from "./database_adapter";
31
32
  export * from "./breadcrumbs";
32
33
  export * from "./component_overrides";
34
+ export * from "./api_keys";
35
+
@@ -311,7 +311,7 @@ export interface PluginFormActionProps<USER extends User = User, EC extends Enti
311
311
  disabled: boolean;
312
312
  formContext?: FormContext;
313
313
  context: RebaseContext<USER>;
314
- openEntityMode: "side_panel" | "full_screen" | "split" | "dialog";
314
+ openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
315
315
  }
316
316
 
317
317
  /**
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Structured, engine-agnostic policy expressions.
3
+ *
4
+ * A {@link PolicyExpression} is the single source of truth for a row-level
5
+ * security condition. It is compiled to Postgres `USING`/`WITH CHECK` SQL
6
+ * (authoritative enforcement) and independently evaluated in JavaScript (to
7
+ * drive the admin UI, and — in future — to enforce on engines without native
8
+ * RLS such as MongoDB). Because both the SQL and the JS decision derive from
9
+ * the *same* expression, the UI matches database enforcement by construction —
10
+ * no drift between two hand-written implementations.
11
+ *
12
+ * The only escape hatch that cannot be evaluated client-side is the
13
+ * {@link RawPolicyExpression} node (`{ kind: "raw" }`): it preserves full
14
+ * PostgreSQL power but, being arbitrary SQL, is treated as *unknown* by the
15
+ * JavaScript evaluator (never silently allowed) and reflected exactly in the UI
16
+ * via server-computed capability flags.
17
+ *
18
+ * @group Models
19
+ */
20
+ export type PolicyExpression =
21
+ | TruePolicyExpression
22
+ | FalsePolicyExpression
23
+ | AndPolicyExpression
24
+ | OrPolicyExpression
25
+ | NotPolicyExpression
26
+ | ComparePolicyExpression
27
+ | RolesOverlapPolicyExpression
28
+ | RolesContainPolicyExpression
29
+ | AuthenticatedPolicyExpression
30
+ | RawPolicyExpression;
31
+
32
+ /** Always allows. Compiles to `true`. @group Models */
33
+ export interface TruePolicyExpression {
34
+ kind: "true";
35
+ }
36
+
37
+ /** Always denies. Compiles to `false`. @group Models */
38
+ export interface FalsePolicyExpression {
39
+ kind: "false";
40
+ }
41
+
42
+ /** Logical AND — every operand must pass. @group Models */
43
+ export interface AndPolicyExpression {
44
+ kind: "and";
45
+ operands: PolicyExpression[];
46
+ }
47
+
48
+ /** Logical OR — at least one operand must pass. @group Models */
49
+ export interface OrPolicyExpression {
50
+ kind: "or";
51
+ operands: PolicyExpression[];
52
+ }
53
+
54
+ /** Logical negation. @group Models */
55
+ export interface NotPolicyExpression {
56
+ kind: "not";
57
+ operand: PolicyExpression;
58
+ }
59
+
60
+ /** Comparison operators available to {@link ComparePolicyExpression}. @group Models */
61
+ export type PolicyCompareOperator = "eq" | "neq" | "lt" | "lte" | "gt" | "gte";
62
+
63
+ /**
64
+ * Compares two operands, e.g. `owner_id = auth.uid()`.
65
+ * @group Models
66
+ */
67
+ export interface ComparePolicyExpression {
68
+ kind: "compare";
69
+ op: PolicyCompareOperator;
70
+ left: PolicyOperand;
71
+ right: PolicyOperand;
72
+ }
73
+
74
+ /**
75
+ * True when the user holds *at least one* of the given application roles.
76
+ * Compiles to `string_to_array(auth.roles(), ',') && ARRAY[...]`.
77
+ * @group Models
78
+ */
79
+ export interface RolesOverlapPolicyExpression {
80
+ kind: "rolesOverlap";
81
+ roles: string[];
82
+ }
83
+
84
+ /**
85
+ * True when the user holds *all* of the given application roles.
86
+ * Compiles to `string_to_array(auth.roles(), ',') @> ARRAY[...]`.
87
+ * @group Models
88
+ */
89
+ export interface RolesContainPolicyExpression {
90
+ kind: "rolesContain";
91
+ roles: string[];
92
+ }
93
+
94
+ /**
95
+ * True when there is an authenticated user (`auth.uid() IS NOT NULL`).
96
+ * @group Models
97
+ */
98
+ export interface AuthenticatedPolicyExpression {
99
+ kind: "authenticated";
100
+ }
101
+
102
+ /**
103
+ * A raw PostgreSQL boolean expression — the full-power escape hatch.
104
+ *
105
+ * Columns can be referenced as `{column_name}`. This is Postgres-only and
106
+ * **server-authoritative**: the JavaScript evaluator cannot evaluate arbitrary
107
+ * SQL, so it treats this node as *unknown* rather than guessing.
108
+ * @group Models
109
+ */
110
+ export interface RawPolicyExpression {
111
+ kind: "raw";
112
+ sql: string;
113
+ }
114
+
115
+ /**
116
+ * An operand referenced by a {@link ComparePolicyExpression}.
117
+ * @group Models
118
+ */
119
+ export type PolicyOperand =
120
+ | FieldPolicyOperand
121
+ | LiteralPolicyOperand
122
+ | AuthUidPolicyOperand
123
+ | AuthRolesPolicyOperand;
124
+
125
+ /** A column value on the row being evaluated. @group Models */
126
+ export interface FieldPolicyOperand {
127
+ kind: "field";
128
+ /** The property/column name (resolved to its DB column when compiled). */
129
+ name: string;
130
+ }
131
+
132
+ /** A constant value. @group Models */
133
+ export interface LiteralPolicyOperand {
134
+ kind: "literal";
135
+ value: string | number | boolean | null;
136
+ }
137
+
138
+ /** The current user's id — compiles to `auth.uid()`. @group Models */
139
+ export interface AuthUidPolicyOperand {
140
+ kind: "authUid";
141
+ }
142
+
143
+ /**
144
+ * The current user's roles as an array — compiles to
145
+ * `string_to_array(auth.roles(), ',')`.
146
+ * @group Models
147
+ */
148
+ export interface AuthRolesPolicyOperand {
149
+ kind: "authRoles";
150
+ }
151
+
152
+ // ── Constructor helpers ──────────────────────────────────────────────
153
+ // Small, dependency-free builders so callers (and the desugaring in
154
+ // `@rebasepro/common`) can assemble expressions without object-literal noise.
155
+
156
+ /** @group Models */
157
+ export const policy = {
158
+ true: (): TruePolicyExpression => ({ kind: "true" }),
159
+ false: (): FalsePolicyExpression => ({ kind: "false" }),
160
+ and: (...operands: PolicyExpression[]): AndPolicyExpression => ({ kind: "and", operands }),
161
+ or: (...operands: PolicyExpression[]): OrPolicyExpression => ({ kind: "or", operands }),
162
+ not: (operand: PolicyExpression): NotPolicyExpression => ({ kind: "not", operand }),
163
+ compare: (left: PolicyOperand, op: PolicyCompareOperator, right: PolicyOperand): ComparePolicyExpression =>
164
+ ({ kind: "compare", op, left, right }),
165
+ rolesOverlap: (roles: string[]): RolesOverlapPolicyExpression => ({ kind: "rolesOverlap", roles }),
166
+ rolesContain: (roles: string[]): RolesContainPolicyExpression => ({ kind: "rolesContain", roles }),
167
+ authenticated: (): AuthenticatedPolicyExpression => ({ kind: "authenticated" }),
168
+ raw: (sql: string): RawPolicyExpression => ({ kind: "raw", sql }),
169
+ field: (name: string): FieldPolicyOperand => ({ kind: "field", name }),
170
+ literal: (value: string | number | boolean | null): LiteralPolicyOperand => ({ kind: "literal", value }),
171
+ authUid: (): AuthUidPolicyOperand => ({ kind: "authUid" }),
172
+ authRoles: (): AuthRolesPolicyOperand => ({ kind: "authRoles" })
173
+ };