@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,15 +1,18 @@
1
1
  import {
2
- AdditionalFieldDelegate,
3
2
  ArrayProperty,
4
3
  BooleanProperty,
5
4
  DateProperty,
6
- EntityCallbacks,
7
- EntityCollection,
8
- EnumValueConfig,
9
- EnumValues,
5
+ CollectionConfig,
6
+ FirebaseCollectionConfig,
7
+ FirebaseProperties,
10
8
  GeopointProperty,
9
+ InferEntityType,
11
10
  MapProperty,
12
- NumberProperty, Properties,
11
+ MongoDBCollectionConfig,
12
+ MongoProperties,
13
+ NumberProperty,
14
+ PostgresCollectionConfig,
15
+ PostgresProperties,
13
16
  Property,
14
17
  ReferenceProperty,
15
18
  StringProperty,
@@ -18,112 +21,112 @@ import {
18
21
 
19
22
 
20
23
  /**
21
- * Identity function we use to defeat the type system of Typescript and build
22
- * collection views with all its properties
23
- * @param collection
24
+ * @deprecated Use {@link defineCollection} instead it infers property
25
+ * types automatically (autocomplete on `titleProperty`, `sort`,
26
+ * `propertiesOrder`, callbacks) without manual generics.
27
+ * `buildCollection` is kept for FireCMS migration compatibility and will
28
+ * be removed before 1.0.
29
+ *
24
30
  * @group Builder
25
31
  */
26
32
  export function buildCollection<
27
33
  M extends Record<string, unknown> = Record<string, unknown>,
28
34
  USER extends User = User>
29
35
  (
30
- collection: EntityCollection<M, USER>
31
- ): EntityCollection<M, USER> {
36
+ collection: CollectionConfig<M, USER>
37
+ ): CollectionConfig<M, USER> {
32
38
  return collection;
33
39
  }
34
40
 
35
- /**
36
- * Identity function we use to defeat the type system of Typescript and preserve
37
- * the property keys.
38
- * @param property
39
- * @group Builder
40
- */
41
- export function buildProperty<T, P extends Property = Property>(
42
- property: P
43
- ):
44
- P extends StringProperty ? StringProperty :
45
- P extends NumberProperty ? NumberProperty :
46
- P extends BooleanProperty ? BooleanProperty :
47
- P extends DateProperty ? DateProperty :
48
- P extends GeopointProperty ? GeopointProperty :
49
- P extends ReferenceProperty ? ReferenceProperty :
50
- P extends ArrayProperty ? ArrayProperty :
51
- P extends MapProperty ? MapProperty : never {
52
-
53
- // SAFETY: Identity function — P is a subtype of the conditional return type by definition
54
- return property as unknown as ReturnType<typeof buildProperty<T, P>>;
55
- }
41
+ // ── defineCollection ─────────────────────────────────────────────────────
42
+ // A smarter builder that uses `const` type-parameter inference (TS 5.0+)
43
+ // to capture literal property types automatically. This gives you
44
+ // autocomplete on `titleProperty`, `sort`, `propertiesOrder`, `fixedFilter`,
45
+ // callbacks, etc. — without writing `as const` or passing manual generics.
56
46
 
57
47
  /**
58
- * Identity function we use to defeat the type system of Typescript and preserve
59
- * the properties keys.
60
- * @param properties
48
+ * Define a PostgreSQL-backed collection with full type inference.
49
+ *
50
+ * The `const P` generic captures literal property types from your
51
+ * `properties` object, which enables autocomplete on `titleProperty`,
52
+ * `sort`, `propertiesOrder`, `fixedFilter`, and entity callbacks.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const products = defineCollection({
57
+ * name: "Products",
58
+ * slug: "products",
59
+ * table: "products",
60
+ * properties: {
61
+ * name: { name: "Name", type: "string", validation: { required: true } },
62
+ * price: { name: "Price", type: "number" },
63
+ * },
64
+ * titleProperty: "name", // ✅ autocomplete: "name" | "price"
65
+ * sort: ["price", "asc"], // ✅ autocomplete on first element
66
+ * });
67
+ * ```
68
+ *
61
69
  * @group Builder
62
70
  */
63
- export function buildProperties<M extends Record<string, unknown>>(
64
- properties: Properties
65
- ): Properties {
66
- return properties;
67
- }
71
+ export function defineCollection<
72
+ const P extends PostgresProperties,
73
+ USER extends User = User
74
+ >(
75
+ collection: Omit<PostgresCollectionConfig<InferEntityType<P>, USER>, "properties"> & { properties: P }
76
+ ): PostgresCollectionConfig<InferEntityType<P>, USER> & { properties: P };
68
77
 
69
78
  /**
70
- * Identity function we use to defeat the type system of Typescript and preserve
71
- * the properties keys.
72
- * @param propertiesOrBuilder
79
+ * Define a Firestore-backed collection with full type inference.
73
80
  * @group Builder
74
81
  */
75
- export function buildPropertiesOrBuilder<M extends Record<string, unknown>>(
76
- propertiesOrBuilder: Properties
77
- ): Properties {
78
- return propertiesOrBuilder;
79
- }
82
+ export function defineCollection<
83
+ const P extends FirebaseProperties,
84
+ USER extends User = User
85
+ >(
86
+ collection: Omit<FirebaseCollectionConfig<InferEntityType<P>, USER>, "properties"> & { properties: P }
87
+ ): FirebaseCollectionConfig<InferEntityType<P>, USER> & { properties: P };
80
88
 
81
89
  /**
82
- * Identity function we use to defeat the type system of Typescript and preserve
83
- * the properties keys.
84
- * @param enumValues
90
+ * Define a MongoDB-backed collection with full type inference.
85
91
  * @group Builder
86
92
  */
87
- export function buildEnum(
88
- enumValues: EnumValues
89
- ): EnumValues {
90
- return enumValues;
91
- }
93
+ export function defineCollection<
94
+ const P extends MongoProperties,
95
+ USER extends User = User
96
+ >(
97
+ collection: Omit<MongoDBCollectionConfig<InferEntityType<P>, USER>, "properties"> & { properties: P }
98
+ ): MongoDBCollectionConfig<InferEntityType<P>, USER> & { properties: P };
92
99
 
93
100
  /**
94
- * Identity function we use to defeat the type system of Typescript and preserve
95
- * the properties keys.
96
- * @param enumValueConfig
97
- * @group Builder
101
+ * Implementation delegates to the correct overload at the type level.
102
+ * At runtime this is a plain identity function.
98
103
  */
99
- export function buildEnumValueConfig(
100
- enumValueConfig: EnumValueConfig
101
- ): EnumValueConfig {
102
- return enumValueConfig;
104
+ export function defineCollection(
105
+ collection: CollectionConfig
106
+ ): CollectionConfig {
107
+ return collection;
103
108
  }
104
109
 
105
110
  /**
106
- * Identity function we use to defeat the type system of Typescript and preserve
107
- * the properties keys.
108
- * @param callbacks
111
+ * @deprecated Use plain typed property objects with {@link defineCollection}
112
+ * instead `defineCollection` infers property types automatically, making
113
+ * this wrapper unnecessary. `buildProperty` is kept for FireCMS migration
114
+ * compatibility and will be removed before 1.0.
115
+ *
109
116
  * @group Builder
110
117
  */
111
- export function buildEntityCallbacks<M extends Record<string, unknown> = Record<string, unknown>>(
112
- callbacks: EntityCallbacks<M>
113
- ): EntityCallbacks<M> {
114
- return callbacks;
115
- }
118
+ export function buildProperty<T, P extends Property = Property>(
119
+ property: P
120
+ ):
121
+ P extends StringProperty ? StringProperty :
122
+ P extends NumberProperty ? NumberProperty :
123
+ P extends BooleanProperty ? BooleanProperty :
124
+ P extends DateProperty ? DateProperty :
125
+ P extends GeopointProperty ? GeopointProperty :
126
+ P extends ReferenceProperty ? ReferenceProperty :
127
+ P extends ArrayProperty ? ArrayProperty :
128
+ P extends MapProperty ? MapProperty : never {
116
129
 
117
- /**
118
- * Identity function we use to defeat the type system of Typescript and build
119
- * additional field delegates views with all its properties
120
- * @param additionalFieldDelegate
121
- * @group Builder
122
- */
123
- export function buildAdditionalFieldDelegate<M extends Record<string, unknown>, USER extends User = User>(
124
- additionalFieldDelegate: AdditionalFieldDelegate<M, USER>
125
- ): AdditionalFieldDelegate<M, USER> {
126
- return additionalFieldDelegate;
130
+ // SAFETY: Identity function — P is a subtype of the conditional return type by definition
131
+ return property as unknown as ReturnType<typeof buildProperty<T, P>>;
127
132
  }
128
-
129
-
@@ -1,4 +1,11 @@
1
- import { EntityCallbacks, Properties } from "@rebasepro/types";
1
+ import { CollectionCallbacks, Properties, RebaseCallContext } from "@rebasepro/types";
2
+
3
+ /**
4
+ * Context passed to entity lifecycle callbacks.
5
+ * @group Models
6
+ */
7
+ export type EntityCallbackContext = RebaseCallContext;
8
+
2
9
 
3
10
  /**
4
11
  * Helper function to recursively check if there are any callbacks in the properties.
@@ -78,24 +85,24 @@ async function processProperties(
78
85
 
79
86
  /**
80
87
  * Helper function to extract field-level PropertyCallbacks from a properties schema
81
- * and wrap them into an EntityCallbacks object recursively.
88
+ * and wrap them into an CollectionCallbacks object recursively.
82
89
  */
83
- export const buildPropertyCallbacks = (properties: Properties): EntityCallbacks | undefined => {
90
+ export const buildPropertyCallbacks = (properties: Properties): CollectionCallbacks | undefined => {
84
91
  if (!properties) return undefined;
85
92
 
86
- const propertyCallbacks: EntityCallbacks = {};
93
+ const propertyCallbacks: CollectionCallbacks = {};
87
94
 
88
95
  if (hasPropertyCallbacks(properties, "afterRead")) {
89
96
  propertyCallbacks.afterRead = async (props) => {
97
+ const row = props.row;
90
98
  const processedValues = await processProperties(
91
99
  properties,
92
- props.entity.values as Record<string, unknown>,
93
- props.entity.values as Record<string, unknown>,
100
+ row,
101
+ row,
94
102
  props as unknown,
95
103
  "afterRead"
96
104
  );
97
- return { ...props.entity,
98
- values: processedValues };
105
+ return { ...props.row, ...processedValues };
99
106
  };
100
107
  }
101
108
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DefaultSelectedViewBuilder,
3
3
  DefaultSelectedViewParams,
4
- EntityCollection,
4
+ CollectionConfig,
5
5
  Properties,
6
6
  Property
7
7
  } from "@rebasepro/types";
@@ -99,7 +99,7 @@ export function resolveDefaultSelectedView(
99
99
  }
100
100
 
101
101
 
102
- export function getLocalChangesBackup(collection: EntityCollection) {
102
+ export function getLocalChangesBackup(collection: CollectionConfig) {
103
103
  if (!collection.localChangesBackup) {
104
104
  return "manual_apply";
105
105
  }
@@ -108,12 +108,12 @@ export function getLocalChangesBackup(collection: EntityCollection) {
108
108
  }
109
109
 
110
110
  /**
111
- * Returns the primary keys for an entity collection by inspecting the properties
111
+ * Returns the primary keys for a entity collection by inspecting the properties
112
112
  * and finding any properties with `isId`.
113
113
  * Fallbacks to `["id"]` if no properties are marked as `isId: true`.
114
114
  * @param collection
115
115
  */
116
- export function getPrimaryKeys<M extends Record<string, unknown>>(collection: EntityCollection<M>): Extract<keyof M, string>[] {
116
+ export function getPrimaryKeys<M extends Record<string, unknown>>(collection: CollectionConfig<M>): Extract<keyof M, string>[] {
117
117
  const properties = collection.properties;
118
118
  if (!properties) {
119
119
  return ["id"] as Extract<keyof M, string>[];
@@ -81,7 +81,7 @@ export function getDefaultValueFortype(type: DataType): unknown {
81
81
  }
82
82
 
83
83
  /**
84
- * Update the automatic values in an entity before save
84
+ * Update the automatic values in a entity before save
85
85
  * @group Driver
86
86
  */
87
87
  export function updateDateAutoValues<M extends Record<string, unknown>>({
@@ -117,7 +117,7 @@ export function updateDateAutoValues<M extends Record<string, unknown>>({
117
117
  }
118
118
 
119
119
  /**
120
- * Add missing required fields, expected in the collection, to the values of an entity
120
+ * Add missing required fields, expected in the collection, to the values of a entity
121
121
  * @param values
122
122
  * @param properties
123
123
  * @group Driver
@@ -148,7 +148,7 @@ export function getReferenceFrom<M extends Record<string, unknown>>(entity: Enti
148
148
  }
149
149
 
150
150
  export function getRelationFrom<M extends Record<string, unknown>>(entity: Entity<M>): EntityRelation {
151
- return new EntityRelation(entity.id, entity.path, entity);
151
+ return new EntityRelation(entity.id, entity.path, entity as unknown as Record<string, unknown>);
152
152
  }
153
153
 
154
154
  /**
@@ -179,7 +179,7 @@ export function normalizeToEntityRelation(value: unknown, propertyType?: string)
179
179
  return new EntityRelation(
180
180
  obj.id as string | number,
181
181
  obj.path as string,
182
- obj.data as Entity | undefined
182
+ obj.data as Record<string, unknown> | undefined
183
183
  );
184
184
  }
185
185
 
@@ -0,0 +1,81 @@
1
+ import {
2
+ ALL_WHERE_FILTER_OPS,
3
+ DataType,
4
+ getDataSourceCapabilities,
5
+ Property,
6
+ WhereFilterOp
7
+ } from "@rebasepro/types";
8
+
9
+ /**
10
+ * Default operators offered per property type, before engine capabilities and
11
+ * per-property narrowing are applied. These mirror what the built-in filter
12
+ * fields can render.
13
+ */
14
+ const COMPARISON_OPS: readonly WhereFilterOp[] = ["==", "!=", ">", ">=", "<", "<="];
15
+ const NULL_CHECK_OPS: readonly WhereFilterOp[] = ["is-null", "is-not-null"];
16
+ const MEMBERSHIP_OPS: readonly WhereFilterOp[] = ["in", "not-in"];
17
+ const PATTERN_OPS: readonly WhereFilterOp[] = ["like", "ilike", "not-like", "not-ilike"];
18
+
19
+ const DEFAULT_OPS_BY_TYPE: Partial<Record<DataType, readonly WhereFilterOp[]>> = {
20
+ string: [...COMPARISON_OPS, ...MEMBERSHIP_OPS, ...PATTERN_OPS, ...NULL_CHECK_OPS],
21
+ number: [...COMPARISON_OPS, ...MEMBERSHIP_OPS, ...NULL_CHECK_OPS],
22
+ date: [...COMPARISON_OPS, ...NULL_CHECK_OPS],
23
+ boolean: ["==", "!=", ...NULL_CHECK_OPS],
24
+ reference: ["==", "!=", ...MEMBERSHIP_OPS, ...NULL_CHECK_OPS],
25
+ relation: ["==", "!=", ...MEMBERSHIP_OPS, ...NULL_CHECK_OPS]
26
+ // geopoint, map, vector, binary, array (as a container): not filterable
27
+ // through the generic filter UI.
28
+ };
29
+
30
+ /** Operators offered when the property is an *array of* a filterable type. */
31
+ const ARRAY_OPS: readonly WhereFilterOp[] = ["array-contains", "array-contains-any"];
32
+
33
+ export interface ResolveFilterOperatorsParams {
34
+ /**
35
+ * The property to filter on. For array properties, pass the **item**
36
+ * property (`property.of`) together with `isArray: true` — the same
37
+ * convention the filter field dispatchers use.
38
+ */
39
+ property: Property;
40
+ /** True when filtering an array of `property`. */
41
+ isArray?: boolean;
42
+ /**
43
+ * The engine backing the collection (`collection.engine`, e.g.
44
+ * `"postgres"`, `"firestore"`). Falls back to the default engine's
45
+ * capabilities when omitted.
46
+ */
47
+ engine?: string;
48
+ }
49
+
50
+ /**
51
+ * Resolve which filter operators the UI should offer for a property.
52
+ *
53
+ * The result is the **intersection** of three sets:
54
+ * 1. what the engine can execute — {@link DataSourceCapabilities.filterOperators}
55
+ * (e.g. Firestore cannot run the LIKE family);
56
+ * 2. what makes sense for the property type (e.g. no `>` on booleans);
57
+ * 3. the developer's optional narrowing — `property.ui.filterOperators`.
58
+ *
59
+ * Returns an empty array when the property is not filterable (either by
60
+ * type, or because the developer disabled it with `filterOperators: []`).
61
+ *
62
+ * @group Models
63
+ */
64
+ export function resolveFilterOperators({
65
+ property,
66
+ isArray,
67
+ engine
68
+ }: ResolveFilterOperatorsParams): WhereFilterOp[] {
69
+ const typeDefaults: readonly WhereFilterOp[] = isArray
70
+ ? ARRAY_OPS
71
+ : DEFAULT_OPS_BY_TYPE[property.type] ?? [];
72
+ if (typeDefaults.length === 0) return [];
73
+
74
+ const engineOps = new Set(getDataSourceCapabilities(engine).filterOperators ?? ALL_WHERE_FILTER_OPS);
75
+
76
+ const narrowing = property.ui?.filterOperators;
77
+ const narrowingSet = narrowing !== undefined ? new Set(narrowing) : undefined;
78
+
79
+ return typeDefaults.filter(op =>
80
+ engineOps.has(op) && (narrowingSet === undefined || narrowingSet.has(op)));
81
+ }
package/src/util/index.ts CHANGED
@@ -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>> = { key: string; [key: string]: unknown };
3
3
  import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
4
4
  import { getSubcollections } from "./resolutions";
@@ -13,7 +13,7 @@ export interface NavigationViewEntityInternal<M extends Record<string, unknown>>
13
13
  entityId: string | number;
14
14
  slug: string;
15
15
  path: string;
16
- parentCollection: EntityCollection<M>;
16
+ parentCollection: CollectionConfig<M>;
17
17
  }
18
18
 
19
19
  export interface NavigationViewCollectionInternal<M extends Record<string, unknown>> {
@@ -21,7 +21,7 @@ export interface NavigationViewCollectionInternal<M extends Record<string, unkno
21
21
  id: string;
22
22
  slug: string;
23
23
  path: string;
24
- collection: EntityCollection<M>;
24
+ collection: CollectionConfig<M>;
25
25
  }
26
26
 
27
27
  export interface NavigationViewEntityCustomInternal<M extends Record<string, unknown>> {
@@ -34,7 +34,7 @@ export interface NavigationViewEntityCustomInternal<M extends Record<string, unk
34
34
 
35
35
  export function getNavigationEntriesFromPath(props: {
36
36
  path: string,
37
- collections: EntityCollection[] | undefined,
37
+ collections: CollectionConfig[] | undefined,
38
38
  currentFullPath?: string,
39
39
  contextEntityViews?: EntityCustomView[]
40
40
  }): NavigationViewInternal[] {
@@ -1,4 +1,4 @@
1
- import { EntityCollection } from "@rebasepro/types";
1
+ import { CollectionConfig } from "@rebasepro/types";
2
2
 
3
3
  import { getSubcollections } from "./resolutions";
4
4
 
@@ -33,13 +33,13 @@ export function getLastSegment(path: string) {
33
33
  return cleanPath;
34
34
  }
35
35
 
36
- export function resolveCollectionPathIds(path: string, allCollections: EntityCollection[]): string {
36
+ export function resolveCollectionPathIds(path: string, allCollections: CollectionConfig[]): string {
37
37
  let remainingPath = removeInitialAndTrailingSlashes(path);
38
38
  if (!remainingPath) {
39
39
  return "";
40
40
  }
41
41
 
42
- let currentCollections: EntityCollection[] | undefined = allCollections;
42
+ let currentCollections: CollectionConfig[] | undefined = allCollections;
43
43
  const resolvedPathParts: string[] = [];
44
44
 
45
45
  while (remainingPath.length > 0) {
@@ -53,7 +53,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
53
53
 
54
54
  let foundMatch = false;
55
55
  // Sort potential matches by length descending to prioritize longer matches (e.g., "a/b" over "a")
56
- const potentialMatches: { col: EntityCollection; match: string; }[] = currentCollections
56
+ const potentialMatches: { col: CollectionConfig; match: string; }[] = currentCollections
57
57
  .flatMap(col => [{
58
58
  col,
59
59
  match: col.slug
@@ -76,7 +76,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
76
76
  break; // Path ends with a collection segment
77
77
  }
78
78
 
79
- // The next segment must be an entity ID
79
+ // The next segment must be a entity ID
80
80
  const idSeparatorIndex = remainingPath.indexOf("/");
81
81
  let entityId: string | number;
82
82
  if (idSeparatorIndex > -1) {
@@ -87,7 +87,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
87
87
  // but handle it defensively: assume the rest is the ID
88
88
  entityId = remainingPath;
89
89
  remainingPath = "";
90
- console.warn(`resolveCollectionPathIds: Path seems to end with an entity ID "${entityId}" instead of a collection segment in original path "${path}". This might indicate an invalid input path.`);
90
+ console.warn(`resolveCollectionPathIds: Path seems to end with a entity ID "${entityId}" instead of a collection segment in original path "${path}". This might indicate an invalid input path.`);
91
91
  // Even if it ends here, we still need to push the ID
92
92
  }
93
93
 
@@ -123,7 +123,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
123
123
  * @param slugOrPath
124
124
  * @param collections
125
125
  */
126
- export function getCollectionBySlugWithin(slugOrPath: string, collections: EntityCollection[]): EntityCollection | undefined {
126
+ export function getCollectionBySlugWithin(slugOrPath: string, collections: CollectionConfig[]): CollectionConfig | undefined {
127
127
 
128
128
  const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
129
129
  if (subpaths.length % 2 === 0) {
@@ -131,7 +131,7 @@ export function getCollectionBySlugWithin(slugOrPath: string, collections: Entit
131
131
  }
132
132
 
133
133
  const subpathCombinations = getCollectionPathsCombinations(subpaths);
134
- let result: EntityCollection | undefined;
134
+ let result: CollectionConfig | undefined;
135
135
  for (let i = 0; i < subpathCombinations.length; i++) {
136
136
  const subpathCombination = subpathCombinations[i];
137
137
  const navigationEntry = collections && collections
@@ -1,10 +1,10 @@
1
- import { EntityCollection, EntityReference } from "@rebasepro/types";
1
+ import { CollectionConfig, EntityReference } from "@rebasepro/types";
2
2
  import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
3
3
  import { getSubcollections } from "./resolutions";
4
4
 
5
5
  export function getParentReferencesFromPath(props: {
6
6
  path: string,
7
- collections: EntityCollection[] | undefined,
7
+ collections: CollectionConfig[] | undefined,
8
8
  currentFullPath?: string,
9
9
  }): EntityReference[] {
10
10
 
@@ -21,7 +21,7 @@ export function getParentReferencesFromPath(props: {
21
21
  for (let i = 0; i < subpathCombinations.length; i++) {
22
22
  const subpathCombination = subpathCombinations[i];
23
23
 
24
- const collection: EntityCollection | undefined = collections && collections.find((entry) => entry.slug === subpathCombination);
24
+ const collection: CollectionConfig | undefined = collections && collections.find((entry) => entry.slug === subpathCombination);
25
25
 
26
26
  // If we find a collection, we add the reference and continue
27
27
  if (collection) {
@@ -1,5 +1,5 @@
1
1
  import { canCreateEntity, canEditEntity, canDeleteEntity, canReadCollection } from "./permissions";
2
- import { EntityCollection, AuthController, Entity, User, SecurityRule } from "@rebasepro/types";
2
+ import { CollectionConfig, AuthController, Entity, User, SecurityRule } from "@rebasepro/types";
3
3
 
4
4
  describe("Permissions Evaluator", () => {
5
5
 
@@ -46,7 +46,7 @@ describe("Permissions Evaluator", () => {
46
46
  user: null
47
47
  };
48
48
 
49
- const createMockCollection = (rules?: SecurityRule[]): EntityCollection => ({
49
+ const createMockCollection = (rules?: SecurityRule[]): CollectionConfig => ({
50
50
  slug: "test",
51
51
  name: "Test",
52
52
  table: "test",
@@ -162,9 +162,10 @@ roles: ["author"] }
162
162
  expect(canReadCollection(collection, mockAuthController)).toBe(true);
163
163
  });
164
164
 
165
- test("11. Empty roles array [] on rule grants access to everyone (public)", () => {
165
+ test("11. Empty roles array [] adds no role restriction (public rule stays public)", () => {
166
166
  const collection = createMockCollection([
167
167
  { operation: "insert",
168
+ access: "public",
168
169
  roles: [] }
169
170
  ]);
170
171
  expect(canCreateEntity(collection, mockAuthController, "test", null)).toBe(true);
@@ -172,9 +173,10 @@ roles: [] }
172
173
  expect(canCreateEntity(collection, adminAuthController, "test", null)).toBe(true);
173
174
  });
174
175
 
175
- test("12. Undefined roles on rule grants access to everyone (public)", () => {
176
+ test("12. Undefined roles on a public rule grants access to everyone", () => {
176
177
  const collection = createMockCollection([
177
- { operation: "insert" }
178
+ { operation: "insert",
179
+ access: "public" }
178
180
  ]);
179
181
  expect(canCreateEntity(collection, mockAuthController, "test", null)).toBe(true);
180
182
  expect(canCreateEntity(collection, unauthenticatedController, "test", null)).toBe(true);