@rebasepro/common 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.
Files changed (63) hide show
  1. package/README.md +4 -4
  2. package/dist/collections/CollectionRegistry.d.ts +30 -15
  3. package/dist/collections/default-collections.d.ts +255 -2
  4. package/dist/data/buildRebaseData.d.ts +30 -2
  5. package/dist/data/buildRoutedRebaseData.d.ts +14 -9
  6. package/dist/data/filter-dialect.d.ts +75 -0
  7. package/dist/data/query_builder.d.ts +4 -4
  8. package/dist/data/resolveDataSource.d.ts +8 -8
  9. package/dist/data/sort-dialect.d.ts +41 -0
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.es.js +1125 -299
  12. package/dist/index.es.js.map +1 -1
  13. package/dist/index.umd.js +1138 -303
  14. package/dist/index.umd.js.map +1 -1
  15. package/dist/util/builders.d.ts +52 -42
  16. package/dist/util/callbacks.d.ts +8 -3
  17. package/dist/util/collections.d.ts +4 -4
  18. package/dist/util/entities.d.ts +2 -2
  19. package/dist/util/filter-operator-resolution.d.ts +32 -0
  20. package/dist/util/index.d.ts +2 -0
  21. package/dist/util/navigation_from_path.d.ts +4 -4
  22. package/dist/util/navigation_utils.d.ts +3 -3
  23. package/dist/util/parent_references_from_path.d.ts +2 -2
  24. package/dist/util/permissions.d.ts +30 -6
  25. package/dist/util/policy/evaluatePolicy.d.ts +31 -0
  26. package/dist/util/policy/index.d.ts +3 -0
  27. package/dist/util/policy/policyToPostgres.d.ts +22 -0
  28. package/dist/util/policy/securityRuleToConditions.d.ts +24 -0
  29. package/dist/util/policy/sqlToPolicy.d.ts +20 -0
  30. package/dist/util/references.d.ts +2 -2
  31. package/dist/util/relations.d.ts +5 -5
  32. package/dist/util/resolutions.d.ts +2 -2
  33. package/dist/util/storage.d.ts +26 -1
  34. package/package.json +13 -13
  35. package/src/collections/CollectionRegistry.ts +92 -61
  36. package/src/collections/default-collections.ts +4 -4
  37. package/src/data/buildRebaseData.ts +336 -172
  38. package/src/data/buildRoutedRebaseData.ts +22 -16
  39. package/src/data/filter-dialect.ts +403 -0
  40. package/src/data/query_builder.ts +19 -10
  41. package/src/data/resolveDataSource.ts +10 -10
  42. package/src/data/sort-dialect.ts +56 -0
  43. package/src/index.ts +2 -0
  44. package/src/util/builders.ts +87 -84
  45. package/src/util/callbacks.ts +15 -8
  46. package/src/util/collections.ts +4 -4
  47. package/src/util/entities.ts +4 -4
  48. package/src/util/filter-operator-resolution.ts +81 -0
  49. package/src/util/index.ts +2 -0
  50. package/src/util/navigation_from_path.ts +4 -4
  51. package/src/util/navigation_utils.ts +8 -8
  52. package/src/util/parent_references_from_path.ts +3 -3
  53. package/src/util/permissions.test.ts +7 -5
  54. package/src/util/permissions.ts +90 -163
  55. package/src/util/policy/evaluatePolicy.ts +152 -0
  56. package/src/util/policy/index.ts +3 -0
  57. package/src/util/policy/policyToPostgres.ts +165 -0
  58. package/src/util/policy/securityRuleToConditions.ts +67 -0
  59. package/src/util/policy/sqlToPolicy.ts +88 -0
  60. package/src/util/references.ts +3 -3
  61. package/src/util/relations.ts +19 -20
  62. package/src/util/resolutions.ts +11 -11
  63. package/src/util/storage.ts +34 -1
@@ -1,57 +1,67 @@
1
- import { AdditionalFieldDelegate, ArrayProperty, BooleanProperty, DateProperty, EntityCallbacks, EntityCollection, EnumValueConfig, EnumValues, GeopointProperty, MapProperty, NumberProperty, Properties, Property, ReferenceProperty, StringProperty, User } from "@rebasepro/types";
1
+ import { ArrayProperty, BooleanProperty, DateProperty, CollectionConfig, FirebaseCollectionConfig, FirebaseProperties, GeopointProperty, InferEntityType, MapProperty, MongoDBCollectionConfig, MongoProperties, NumberProperty, PostgresCollectionConfig, PostgresProperties, Property, ReferenceProperty, StringProperty, User } from "@rebasepro/types";
2
2
  /**
3
- * Identity function we use to defeat the type system of Typescript and build
4
- * collection views with all its properties
5
- * @param collection
3
+ * @deprecated Use {@link defineCollection} instead it infers property
4
+ * types automatically (autocomplete on `titleProperty`, `sort`,
5
+ * `propertiesOrder`, callbacks) without manual generics.
6
+ * `buildCollection` is kept for FireCMS migration compatibility and will
7
+ * be removed before 1.0.
8
+ *
6
9
  * @group Builder
7
10
  */
8
- export declare function buildCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(collection: EntityCollection<M, USER>): EntityCollection<M, USER>;
11
+ export declare function buildCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(collection: CollectionConfig<M, USER>): CollectionConfig<M, USER>;
9
12
  /**
10
- * Identity function we use to defeat the type system of Typescript and preserve
11
- * the property keys.
12
- * @param property
13
+ * Define a PostgreSQL-backed collection with full type inference.
14
+ *
15
+ * The `const P` generic captures literal property types from your
16
+ * `properties` object, which enables autocomplete on `titleProperty`,
17
+ * `sort`, `propertiesOrder`, `fixedFilter`, and entity callbacks.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const products = defineCollection({
22
+ * name: "Products",
23
+ * slug: "products",
24
+ * table: "products",
25
+ * properties: {
26
+ * name: { name: "Name", type: "string", validation: { required: true } },
27
+ * price: { name: "Price", type: "number" },
28
+ * },
29
+ * titleProperty: "name", // ✅ autocomplete: "name" | "price"
30
+ * sort: ["price", "asc"], // ✅ autocomplete on first element
31
+ * });
32
+ * ```
33
+ *
13
34
  * @group Builder
14
35
  */
15
- export declare function buildProperty<T, P extends Property = Property>(property: P): P extends StringProperty ? StringProperty : P extends NumberProperty ? NumberProperty : P extends BooleanProperty ? BooleanProperty : P extends DateProperty ? DateProperty : P extends GeopointProperty ? GeopointProperty : P extends ReferenceProperty ? ReferenceProperty : P extends ArrayProperty ? ArrayProperty : P extends MapProperty ? MapProperty : never;
16
- /**
17
- * Identity function we use to defeat the type system of Typescript and preserve
18
- * the properties keys.
19
- * @param properties
20
- * @group Builder
21
- */
22
- export declare function buildProperties<M extends Record<string, unknown>>(properties: Properties): Properties;
36
+ export declare function defineCollection<const P extends PostgresProperties, USER extends User = User>(collection: Omit<PostgresCollectionConfig<InferEntityType<P>, USER>, "properties"> & {
37
+ properties: P;
38
+ }): PostgresCollectionConfig<InferEntityType<P>, USER> & {
39
+ properties: P;
40
+ };
23
41
  /**
24
- * Identity function we use to defeat the type system of Typescript and preserve
25
- * the properties keys.
26
- * @param propertiesOrBuilder
42
+ * Define a Firestore-backed collection with full type inference.
27
43
  * @group Builder
28
44
  */
29
- export declare function buildPropertiesOrBuilder<M extends Record<string, unknown>>(propertiesOrBuilder: Properties): Properties;
45
+ export declare function defineCollection<const P extends FirebaseProperties, USER extends User = User>(collection: Omit<FirebaseCollectionConfig<InferEntityType<P>, USER>, "properties"> & {
46
+ properties: P;
47
+ }): FirebaseCollectionConfig<InferEntityType<P>, USER> & {
48
+ properties: P;
49
+ };
30
50
  /**
31
- * Identity function we use to defeat the type system of Typescript and preserve
32
- * the properties keys.
33
- * @param enumValues
51
+ * Define a MongoDB-backed collection with full type inference.
34
52
  * @group Builder
35
53
  */
36
- export declare function buildEnum(enumValues: EnumValues): EnumValues;
54
+ export declare function defineCollection<const P extends MongoProperties, USER extends User = User>(collection: Omit<MongoDBCollectionConfig<InferEntityType<P>, USER>, "properties"> & {
55
+ properties: P;
56
+ }): MongoDBCollectionConfig<InferEntityType<P>, USER> & {
57
+ properties: P;
58
+ };
37
59
  /**
38
- * Identity function we use to defeat the type system of Typescript and preserve
39
- * the properties keys.
40
- * @param enumValueConfig
60
+ * @deprecated Use plain typed property objects with {@link defineCollection}
61
+ * instead `defineCollection` infers property types automatically, making
62
+ * this wrapper unnecessary. `buildProperty` is kept for FireCMS migration
63
+ * compatibility and will be removed before 1.0.
64
+ *
41
65
  * @group Builder
42
66
  */
43
- export declare function buildEnumValueConfig(enumValueConfig: EnumValueConfig): EnumValueConfig;
44
- /**
45
- * Identity function we use to defeat the type system of Typescript and preserve
46
- * the properties keys.
47
- * @param callbacks
48
- * @group Builder
49
- */
50
- export declare function buildEntityCallbacks<M extends Record<string, unknown> = Record<string, unknown>>(callbacks: EntityCallbacks<M>): EntityCallbacks<M>;
51
- /**
52
- * Identity function we use to defeat the type system of Typescript and build
53
- * additional field delegates views with all its properties
54
- * @param additionalFieldDelegate
55
- * @group Builder
56
- */
57
- export declare function buildAdditionalFieldDelegate<M extends Record<string, unknown>, USER extends User = User>(additionalFieldDelegate: AdditionalFieldDelegate<M, USER>): AdditionalFieldDelegate<M, USER>;
67
+ export declare function buildProperty<T, P extends Property = Property>(property: P): P extends StringProperty ? StringProperty : P extends NumberProperty ? NumberProperty : P extends BooleanProperty ? BooleanProperty : P extends DateProperty ? DateProperty : P extends GeopointProperty ? GeopointProperty : P extends ReferenceProperty ? ReferenceProperty : P extends ArrayProperty ? ArrayProperty : P extends MapProperty ? MapProperty : never;
@@ -1,6 +1,11 @@
1
- import { EntityCallbacks, Properties } from "@rebasepro/types";
1
+ import { CollectionCallbacks, Properties, RebaseCallContext } from "@rebasepro/types";
2
+ /**
3
+ * Context passed to entity lifecycle callbacks.
4
+ * @group Models
5
+ */
6
+ export type EntityCallbackContext = RebaseCallContext;
2
7
  /**
3
8
  * Helper function to extract field-level PropertyCallbacks from a properties schema
4
- * and wrap them into an EntityCallbacks object recursively.
9
+ * and wrap them into an CollectionCallbacks object recursively.
5
10
  */
6
- export declare const buildPropertyCallbacks: (properties: Properties) => EntityCallbacks | undefined;
11
+ export declare const buildPropertyCallbacks: (properties: Properties) => CollectionCallbacks | undefined;
@@ -1,11 +1,11 @@
1
- import { DefaultSelectedViewBuilder, DefaultSelectedViewParams, EntityCollection, Properties } from "@rebasepro/types";
1
+ import { DefaultSelectedViewBuilder, DefaultSelectedViewParams, CollectionConfig, Properties } from "@rebasepro/types";
2
2
  export declare function sortProperties<M extends Record<string, unknown>>(properties: Properties, propertiesOrder?: string[]): Properties;
3
3
  export declare function resolveDefaultSelectedView(defaultSelectedView: string | DefaultSelectedViewBuilder | undefined, params: DefaultSelectedViewParams): string | undefined;
4
- export declare function getLocalChangesBackup(collection: EntityCollection): "manual_apply" | "auto_apply";
4
+ export declare function getLocalChangesBackup(collection: CollectionConfig): "manual_apply" | "auto_apply";
5
5
  /**
6
- * Returns the primary keys for an entity collection by inspecting the properties
6
+ * Returns the primary keys for a entity collection by inspecting the properties
7
7
  * and finding any properties with `isId`.
8
8
  * Fallbacks to `["id"]` if no properties are marked as `isId: true`.
9
9
  * @param collection
10
10
  */
11
- export declare function getPrimaryKeys<M extends Record<string, unknown>>(collection: EntityCollection<M>): Extract<keyof M, string>[];
11
+ export declare function getPrimaryKeys<M extends Record<string, unknown>>(collection: CollectionConfig<M>): Extract<keyof M, string>[];
@@ -6,7 +6,7 @@ export declare function getDefaultValuesFor<M extends Record<string, unknown>>(p
6
6
  export declare function getDefaultValueFor(property?: Property): unknown;
7
7
  export declare function getDefaultValueFortype(type: DataType): unknown;
8
8
  /**
9
- * Update the automatic values in an entity before save
9
+ * Update the automatic values in a entity before save
10
10
  * @group Driver
11
11
  */
12
12
  export declare function updateDateAutoValues<M extends Record<string, unknown>>({ inputValues, properties, status, timestampNowValue }: {
@@ -16,7 +16,7 @@ export declare function updateDateAutoValues<M extends Record<string, unknown>>(
16
16
  timestampNowValue: unknown;
17
17
  }): EntityValues<M>;
18
18
  /**
19
- * Add missing required fields, expected in the collection, to the values of an entity
19
+ * Add missing required fields, expected in the collection, to the values of a entity
20
20
  * @param values
21
21
  * @param properties
22
22
  * @group Driver
@@ -0,0 +1,32 @@
1
+ import { Property, WhereFilterOp } from "@rebasepro/types";
2
+ export interface ResolveFilterOperatorsParams {
3
+ /**
4
+ * The property to filter on. For array properties, pass the **item**
5
+ * property (`property.of`) together with `isArray: true` — the same
6
+ * convention the filter field dispatchers use.
7
+ */
8
+ property: Property;
9
+ /** True when filtering an array of `property`. */
10
+ isArray?: boolean;
11
+ /**
12
+ * The engine backing the collection (`collection.engine`, e.g.
13
+ * `"postgres"`, `"firestore"`). Falls back to the default engine's
14
+ * capabilities when omitted.
15
+ */
16
+ engine?: string;
17
+ }
18
+ /**
19
+ * Resolve which filter operators the UI should offer for a property.
20
+ *
21
+ * The result is the **intersection** of three sets:
22
+ * 1. what the engine can execute — {@link DataSourceCapabilities.filterOperators}
23
+ * (e.g. Firestore cannot run the LIKE family);
24
+ * 2. what makes sense for the property type (e.g. no `>` on booleans);
25
+ * 3. the developer's optional narrowing — `property.ui.filterOperators`.
26
+ *
27
+ * Returns an empty array when the property is not filterable (either by
28
+ * type, or because the developer disabled it with `filterOperators: []`).
29
+ *
30
+ * @group Models
31
+ */
32
+ export declare function resolveFilterOperators({ property, isArray, engine }: ResolveFilterOperatorsParams): WhereFilterOp[];
@@ -4,6 +4,7 @@ export * from "./entities";
4
4
  export * from "./enums";
5
5
  export * from "./paths";
6
6
  export * from "./resolutions";
7
+ export * from "./policy";
7
8
  export * from "./permissions";
8
9
  export * from "./references";
9
10
  export * from "./navigation_from_path";
@@ -14,3 +15,4 @@ export * from "./callbacks";
14
15
  export * from "./relations";
15
16
  export * from "./conditions";
16
17
  export * from "./navigation_utils";
18
+ export * from "./filter-operator-resolution";
@@ -1,4 +1,4 @@
1
- import { EntityCollection } from "@rebasepro/types";
1
+ import { CollectionConfig } from "@rebasepro/types";
2
2
  type EntityCustomView<M extends Record<string, unknown> = Record<string, unknown>> = {
3
3
  key: string;
4
4
  [key: string]: unknown;
@@ -9,14 +9,14 @@ export interface NavigationViewEntityInternal<M extends Record<string, unknown>>
9
9
  entityId: string | number;
10
10
  slug: string;
11
11
  path: string;
12
- parentCollection: EntityCollection<M>;
12
+ parentCollection: CollectionConfig<M>;
13
13
  }
14
14
  export interface NavigationViewCollectionInternal<M extends Record<string, unknown>> {
15
15
  type: "collection";
16
16
  id: string;
17
17
  slug: string;
18
18
  path: string;
19
- collection: EntityCollection<M>;
19
+ collection: CollectionConfig<M>;
20
20
  }
21
21
  export interface NavigationViewEntityCustomInternal<M extends Record<string, unknown>> {
22
22
  type: "custom_view";
@@ -27,7 +27,7 @@ export interface NavigationViewEntityCustomInternal<M extends Record<string, unk
27
27
  }
28
28
  export declare function getNavigationEntriesFromPath(props: {
29
29
  path: string;
30
- collections: EntityCollection[] | undefined;
30
+ collections: CollectionConfig[] | undefined;
31
31
  currentFullPath?: string;
32
32
  contextEntityViews?: EntityCustomView[];
33
33
  }): NavigationViewInternal[];
@@ -1,17 +1,17 @@
1
- import { EntityCollection } from "@rebasepro/types";
1
+ import { CollectionConfig } from "@rebasepro/types";
2
2
  export declare function removeInitialAndTrailingSlashes(s: string): string;
3
3
  export declare function removeInitialSlash(s: string): string;
4
4
  export declare function removeTrailingSlash(s: string): string;
5
5
  export declare function addInitialSlash(s: string): string;
6
6
  export declare function getLastSegment(path: string): string;
7
- export declare function resolveCollectionPathIds(path: string, allCollections: EntityCollection[]): string;
7
+ export declare function resolveCollectionPathIds(path: string, allCollections: CollectionConfig[]): string;
8
8
  /**
9
9
  * Find the corresponding view at any depth for a given path.
10
10
  * Note that path or segments of the paths can be collection aliases.
11
11
  * @param slugOrPath
12
12
  * @param collections
13
13
  */
14
- export declare function getCollectionBySlugWithin(slugOrPath: string, collections: EntityCollection[]): EntityCollection | undefined;
14
+ export declare function getCollectionBySlugWithin(slugOrPath: string, collections: CollectionConfig[]): CollectionConfig | undefined;
15
15
  /**
16
16
  * Get the subcollection combinations from a path:
17
17
  * "sites/es/locales" => ["sites/es/locales", "sites"]
@@ -1,6 +1,6 @@
1
- import { EntityCollection, EntityReference } from "@rebasepro/types";
1
+ import { CollectionConfig, EntityReference } from "@rebasepro/types";
2
2
  export declare function getParentReferencesFromPath(props: {
3
3
  path: string;
4
- collections: EntityCollection[] | undefined;
4
+ collections: CollectionConfig[] | undefined;
5
5
  currentFullPath?: string;
6
6
  }): EntityReference[];
@@ -1,4 +1,4 @@
1
- import { Entity, EntityCollection, User } from "@rebasepro/types";
1
+ import { Entity, CollectionConfig, SecurityOperation, User } from "@rebasepro/types";
2
2
  /**
3
3
  * Minimal auth context for permission checking.
4
4
  * Only requires the user object — avoids forcing callers to construct
@@ -7,8 +7,32 @@ import { Entity, EntityCollection, User } from "@rebasepro/types";
7
7
  export interface AuthContext<USER extends User = User> {
8
8
  user: USER | null;
9
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;
10
+ /**
11
+ * How to resolve a policy result that cannot be decided client-side (a raw-SQL
12
+ * escape-hatch rule, or a row-column reference with no row in hand).
13
+ *
14
+ * - `"allow"` (default): optimistic used for admin-UI gating, where Postgres
15
+ * remains the authoritative gate and hiding a working action is worse than
16
+ * showing one the server may reject.
17
+ * - `"deny"`: fail-closed — used by real enforcement callers (e.g. a driver
18
+ * applying policies in-process), so an undecidable rule never silently allows.
19
+ */
20
+ export type UnknownResolution = "allow" | "deny";
21
+ export interface CheckOperationOptions {
22
+ onUnknown?: UnknownResolution;
23
+ }
24
+ /**
25
+ * Decide whether an operation is permitted for a user on a (possibly null) row,
26
+ * by evaluating the collection's security rules with the shared policy model —
27
+ * the same model compiled to Postgres RLS DDL, so the decision matches database
28
+ * enforcement for every non-raw rule.
29
+ *
30
+ * @param options.onUnknown how to treat rules that cannot be decided
31
+ * client-side (raw SQL, or row predicates with no row). Defaults to `"allow"`
32
+ * for optimistic UI gating; enforcement callers should pass `"deny"`.
33
+ */
34
+ export declare function checkOperation<M extends Record<string, unknown>, USER extends User>(collection: CollectionConfig<M>, authContext: AuthContext<USER>, entity: Entity<M> | null, targetOperation: SecurityOperation, options?: CheckOperationOptions): boolean;
35
+ export declare function canReadCollection<M extends Record<string, unknown>, USER extends User>(collection: CollectionConfig<M>, authContext: AuthContext<USER>): boolean;
36
+ export declare function canEditEntity<M extends Record<string, unknown>, USER extends User>(collection: CollectionConfig<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
37
+ export declare function canCreateEntity<M extends Record<string, unknown>, USER extends User>(collection: CollectionConfig<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
38
+ export declare function canDeleteEntity<M extends Record<string, unknown>, USER extends User>(collection: CollectionConfig<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
@@ -0,0 +1,31 @@
1
+ import { Entity, PolicyExpression } from "@rebasepro/types";
2
+ /**
3
+ * Result of evaluating a policy client-side. `"unknown"` means the expression
4
+ * could not be decided without more information — either a raw-SQL escape-hatch
5
+ * node (which the client deliberately never guesses) or a row-column reference
6
+ * with no entity in hand (e.g. list-level gating). Callers decide how to resolve
7
+ * `"unknown"`: fail-closed for an enforcement decision, optimistic for pure
8
+ * visibility gating.
9
+ */
10
+ export type TriState = boolean | "unknown";
11
+ /**
12
+ * Context for {@link evaluatePolicy}: the acting user (or none) and the row
13
+ * being evaluated (or none, for collection-level gating).
14
+ */
15
+ export interface PolicyEvalContext {
16
+ /** The current user's id, or null/undefined when unauthenticated. */
17
+ uid?: string | null;
18
+ /** The current user's application roles. */
19
+ roles?: string[];
20
+ /** The row being evaluated, or null when no specific row is available. */
21
+ entity: Entity | null;
22
+ }
23
+ /**
24
+ * Evaluates a {@link PolicyExpression} against a user + row, using three-valued
25
+ * (Kleene) logic so that `"unknown"` sub-results propagate soundly.
26
+ *
27
+ * This is the JavaScript twin of {@link policyToPostgres}: both derive from the
28
+ * same expression, so the admin UI matches database enforcement by construction
29
+ * for every non-raw rule.
30
+ */
31
+ export declare function evaluatePolicy(expr: PolicyExpression, ctx: PolicyEvalContext): TriState;
@@ -0,0 +1,3 @@
1
+ export * from "./securityRuleToConditions";
2
+ export * from "./policyToPostgres";
3
+ export * from "./evaluatePolicy";
@@ -0,0 +1,22 @@
1
+ import { CollectionConfig, PolicyExpression } from "@rebasepro/types";
2
+ /**
3
+ * Options for {@link policyToPostgres}.
4
+ */
5
+ export interface PolicyCompileOptions {
6
+ /**
7
+ * Resolve a collection by slug. Required to compile
8
+ * {@link ExistsInPolicyExpression} (`policy.existsIn`) — the compiler needs
9
+ * the joined collection to derive its table name / schema. When omitted, the
10
+ * join table falls back to a snake_cased slug.
11
+ */
12
+ resolveCollection?: (slug: string) => CollectionConfig | undefined;
13
+ }
14
+ /**
15
+ * Compiles a {@link PolicyExpression} to a PostgreSQL boolean SQL string,
16
+ * suitable for a `USING (...)` / `WITH CHECK (...)` clause.
17
+ *
18
+ * This is one of the two consumers of the shared policy model (the other being
19
+ * {@link evaluatePolicy}); the Postgres schema generators call it so that DDL
20
+ * and the admin UI derive from the exact same expression.
21
+ */
22
+ export declare function policyToPostgres(expr: PolicyExpression, collection?: CollectionConfig, options?: PolicyCompileOptions): string;
@@ -0,0 +1,24 @@
1
+ import { PolicyExpression, SecurityRule } from "@rebasepro/types";
2
+ /**
3
+ * The normalized `USING` / `WITH CHECK` conditions for a single security rule,
4
+ * expressed in the engine-agnostic {@link PolicyExpression} model.
5
+ *
6
+ * A `null` clause means "this rule contributes no condition for that clause";
7
+ * consumers apply the default (Postgres denies with `false`).
8
+ */
9
+ export interface RuleConditions {
10
+ usingExpr: PolicyExpression | null;
11
+ withCheckExpr: PolicyExpression | null;
12
+ }
13
+ /**
14
+ * Desugars a {@link SecurityRule} — its `access`/`ownerField`/`roles` shortcuts,
15
+ * structured `condition`/`check`, and raw `using`/`withCheck` — into a single
16
+ * normalized {@link PolicyExpression} pair.
17
+ *
18
+ * **This is the linchpin against drift:** both the Postgres DDL generators and
19
+ * the client-side evaluator consume this one function, so there is exactly one
20
+ * definition of what a rule means. In particular, application `roles` are folded
21
+ * into the expression here (AND'd with the base condition, matching how Postgres
22
+ * generates the clause) rather than being handled separately by each consumer.
23
+ */
24
+ export declare function securityRuleToConditions(rule: SecurityRule): RuleConditions;
@@ -0,0 +1,20 @@
1
+ import { PolicyExpression } from "@rebasepro/types";
2
+ /**
3
+ * A tiny, regex-based SQL "parser" for security rules.
4
+ *
5
+ * This is NOT a full SQL parser. It is designed to handle the subset of SQL
6
+ * commonly used in `USING` and `WITH CHECK` clauses, enough to drive the
7
+ * optimistic client-side UI decision.
8
+ *
9
+ * It handles:
10
+ * - `field = 'literal'`
11
+ * - `field != 'literal'`
12
+ * - `field = current_setting('app.user_id')`
13
+ * - `A AND B`
14
+ * - `true`
15
+ * - `IN (...)` (as optimistic true)
16
+ *
17
+ * For anything it doesn't understand, it returns a `raw` expression, which
18
+ * the evaluator treats as "unknown" (and usually optimistic true).
19
+ */
20
+ export declare function sqlToPolicy(sql: string): PolicyExpression;
@@ -1,2 +1,2 @@
1
- import { EntityCollection } from "@rebasepro/types";
2
- export declare function getEntityImagePreviewPropertyKey<M extends Record<string, unknown>>(collection: EntityCollection<M>): string | undefined;
1
+ import { CollectionConfig } from "@rebasepro/types";
2
+ export declare function getEntityImagePreviewPropertyKey<M extends Record<string, unknown>>(collection: CollectionConfig<M>): string | undefined;
@@ -1,12 +1,12 @@
1
- import { EntityCollection, Property, Relation } from "@rebasepro/types";
2
- export declare function sanitizeRelation(relation: Partial<Relation>, sourceCollection: EntityCollection, resolveCollection?: (slugOrTable: string) => EntityCollection | undefined): Relation;
3
- export declare function resolveCollectionRelations(collection: EntityCollection): Record<string, Relation>;
1
+ import { CollectionConfig, Property, Relation } from "@rebasepro/types";
2
+ export declare function sanitizeRelation(relation: Partial<Relation>, sourceCollection: CollectionConfig, resolveCollection?: (slugOrTable: string) => CollectionConfig | undefined): Relation;
3
+ export declare function resolveCollectionRelations(collection: CollectionConfig): Record<string, Relation>;
4
4
  export declare function resolvePropertyRelation({ propertyKey, property, sourceCollection }: {
5
5
  propertyKey: string;
6
6
  property: Property;
7
- sourceCollection: EntityCollection;
7
+ sourceCollection: CollectionConfig;
8
8
  }): Relation | undefined;
9
- export declare function getTableName(collection: EntityCollection): string;
9
+ export declare function getTableName(collection: CollectionConfig): string;
10
10
  export declare function getTableVarName(tableName: string): string;
11
11
  export declare function getEnumVarName(tableName: string, propName: string): string;
12
12
  export declare function getColumnName(fullColumn: string): string;
@@ -1,4 +1,4 @@
1
- import { ArrayProperty, AuthController, EntityCollection, EnumValueConfig, EnumValues, NumberProperty, Properties, Property, Relation, RelationProperty, StringProperty } from "@rebasepro/types";
1
+ import { ArrayProperty, AuthController, CollectionConfig, EnumValueConfig, EnumValues, NumberProperty, Properties, Property, Relation, RelationProperty, StringProperty } from "@rebasepro/types";
2
2
  type PropertyConfig = {
3
3
  property: unknown;
4
4
  [key: string]: unknown;
@@ -68,5 +68,5 @@ export declare function getArrayResolvedProperties({ propertyKey, propertyValue,
68
68
  authController: AuthController;
69
69
  }): Property[];
70
70
  export declare function resolveEnumValues(input: EnumValues): EnumValueConfig[] | undefined;
71
- export declare function getSubcollections<M extends Record<string, unknown> = Record<string, unknown>>(collection: EntityCollection<M>): EntityCollection<Record<string, unknown>>[];
71
+ export declare function getSubcollections<M extends Record<string, unknown> = Record<string, unknown>>(collection: CollectionConfig<M>): CollectionConfig<Record<string, unknown>>[];
72
72
  export {};
@@ -1,4 +1,29 @@
1
- import { ArrayProperty, EntityValues, StorageConfig, StringProperty, UploadedFileContext } from "@rebasepro/types";
1
+ import { ArrayProperty, EntityValues, StorageConfig, StorageSource, StorageSourceRegistry, StringProperty, UploadedFileContext } from "@rebasepro/types";
2
+ /**
3
+ * Resolve the {@link StorageSource} to use for a property, given the key
4
+ * referenced by `StorageConfig.storageSource`.
5
+ *
6
+ * Resolution priority:
7
+ * 1. No `sourceKey` → the default source (backward compatible).
8
+ * 2. An explicit {@link StorageSourceRegistry} (e.g. `client.storageRegistry`).
9
+ * 3. A `sources` lookup map (e.g. the `StorageSourcesContext`).
10
+ * 4. Fall back to the default source.
11
+ *
12
+ * Shared by the upload hook, the markdown editor, and the read-only previews
13
+ * so the resolution logic lives in one place.
14
+ *
15
+ * @group Storage
16
+ */
17
+ export declare function resolveStorageSource(params: {
18
+ /** Key from `StorageConfig.storageSource`. */
19
+ sourceKey?: string | null;
20
+ /** Built sources keyed by storage-source key (e.g. from context). */
21
+ sources?: Record<string, StorageSource>;
22
+ /** Optional explicit registry — takes precedence over `sources`. */
23
+ registry?: StorageSourceRegistry;
24
+ /** Default source, used when no key is set or the key cannot be resolved. */
25
+ defaultSource: StorageSource;
26
+ }): StorageSource;
2
27
  interface ResolveFilenameStringParams<M extends Record<string, unknown>> {
3
28
  input: string | ((context: UploadedFileContext) => (Promise<string> | string));
4
29
  storage: StorageConfig;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/common",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.9.0",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -29,14 +29,6 @@
29
29
  "headless cms",
30
30
  "content manager"
31
31
  ],
32
- "scripts": {
33
- "watch": "vite build --watch",
34
- "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
35
- "test:lint": "eslint \"src/**\" --quiet",
36
- "test": "jest --passWithNoTests",
37
- "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f",
38
- "generateIcons": "ts-node --esm src/icons/generateIcons.ts"
39
- },
40
32
  "exports": {
41
33
  ".": {
42
34
  "types": "./dist/index.d.ts",
@@ -47,10 +39,10 @@
47
39
  "./package.json": "./package.json"
48
40
  },
49
41
  "dependencies": {
50
- "@rebasepro/types": "workspace:*",
51
- "@rebasepro/utils": "workspace:*",
52
42
  "fast-equals": "6.0.0",
53
- "json-logic-js": "^2.0.5"
43
+ "json-logic-js": "^2.0.5",
44
+ "@rebasepro/types": "0.9.0",
45
+ "@rebasepro/utils": "0.9.0"
54
46
  },
55
47
  "devDependencies": {
56
48
  "@jest/globals": "^30.4.1",
@@ -105,5 +97,13 @@
105
97
  "^@rebasepro/types$": "<rootDir>/../types/src/index.ts",
106
98
  "^@rebasepro/utils$": "<rootDir>/../utils/src/index.ts"
107
99
  }
100
+ },
101
+ "scripts": {
102
+ "watch": "vite build --watch",
103
+ "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
104
+ "test:lint": "eslint \"src/**\" --quiet",
105
+ "test": "jest --passWithNoTests",
106
+ "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f",
107
+ "generateIcons": "ts-node --esm src/icons/generateIcons.ts"
108
108
  }
109
- }
109
+ }