@rebasepro/common 0.10.0 → 0.10.1-canary.0a881d4

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ArrayProperty,
3
- AuthController,
3
+ AuthState,
4
4
  CollectionConfig,
5
5
  EnumValueConfig,
6
6
  EnumValues,
@@ -36,7 +36,7 @@ export type ResolvePropertyProps<M extends Record<string, unknown> = Record<stri
36
36
  index?: number,
37
37
  propertyConfigs?: Record<string, PropertyConfig>;
38
38
  ignoreMissingFields?: boolean;
39
- authController: AuthController;
39
+ authController: AuthState;
40
40
  }
41
41
 
42
42
  export function resolveProperty<M extends Record<string, unknown> = Record<string, unknown>>(props: ResolvePropertyProps<M>): Property | null {
@@ -191,7 +191,7 @@ export function resolveProperties<M extends Record<string, unknown>>({
191
191
  index?: number,
192
192
  propertyConfigs?: Record<string, PropertyConfig>;
193
193
  ignoreMissingFields?: boolean;
194
- authController: AuthController;
194
+ authController: AuthState;
195
195
  }): Properties {
196
196
  return Object.entries<Property>(properties as Record<string, Property>)
197
197
  .map(([key, property]) => {
@@ -226,7 +226,7 @@ export function resolveArrayProperties<M>({
226
226
  index?: number,
227
227
  propertyConfigs?: Record<string, PropertyConfig>;
228
228
  ignoreMissingFields?: boolean;
229
- authController: AuthController;
229
+ authController: AuthState;
230
230
  }): Property[] {
231
231
  const propertyValue = propertyKey ? getIn(props.values, propertyKey) : undefined;
232
232
 
@@ -280,8 +280,16 @@ export function resolveArrayProperties<M>({
280
280
  }).filter(e => Boolean(e)) as Property[]
281
281
  : [];
282
282
  return resolvedProperties;
283
- } else if (!("Field" in (property.ui || {}) && property.ui?.Field)) {
284
- throw Error(`The array property (${propertyKey}) needs to declare an 'of' or a 'oneOf' property, or provide a custom \`Field\` component`);
283
+ } else if (!property.columnType) {
284
+ // An array with neither `of`/`oneOf` nor a `columnType` describes no element
285
+ // type, so nothing can be generated or rendered from it.
286
+ //
287
+ // The escape hatch used to be `ui.Field` — "a custom component can render
288
+ // anything" — which made a *presentation* field decide whether a schema was
289
+ // valid, in code the Postgres generator runs. `columnType` is the same escape
290
+ // hatch stated as data: `columnType: "text[]"` says what the column holds,
291
+ // which is what both the generator and the form actually need.
292
+ throw Error(`The array property (${propertyKey}) needs to declare an 'of' or a 'oneOf' property, or a \`columnType\` such as "text[]"`);
285
293
  } else {
286
294
  return [];
287
295
  }
@@ -304,7 +312,7 @@ export function getArrayResolvedProperties({
304
312
  entityId?: string | number;
305
313
  index?: number;
306
314
  propertyConfigs?: Record<string, PropertyConfig>;
307
- authController: AuthController;
315
+ authController: AuthState;
308
316
  }) {
309
317
 
310
318
  const of = property.of;
@@ -1,32 +0,0 @@
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[];
@@ -1,34 +0,0 @@
1
- import { CollectionConfig } from "@rebasepro/types";
2
- type EntityCustomView<M extends Record<string, unknown> = Record<string, unknown>> = {
3
- key: string;
4
- [key: string]: unknown;
5
- };
6
- export type NavigationViewInternal<M extends Record<string, unknown> = Record<string, unknown>> = NavigationViewEntityInternal<M> | NavigationViewCollectionInternal<M> | NavigationViewEntityCustomInternal<M>;
7
- export interface NavigationViewEntityInternal<M extends Record<string, unknown>> {
8
- type: "entity";
9
- entityId: string | number;
10
- slug: string;
11
- path: string;
12
- parentCollection: CollectionConfig<M>;
13
- }
14
- export interface NavigationViewCollectionInternal<M extends Record<string, unknown>> {
15
- type: "collection";
16
- id: string;
17
- slug: string;
18
- path: string;
19
- collection: CollectionConfig<M>;
20
- }
21
- export interface NavigationViewEntityCustomInternal<M extends Record<string, unknown>> {
22
- type: "custom_view";
23
- slug: string;
24
- path: string;
25
- entityId: string | number;
26
- view: EntityCustomView<M>;
27
- }
28
- export declare function getNavigationEntriesFromPath(props: {
29
- path: string;
30
- collections: CollectionConfig[] | undefined;
31
- currentFullPath?: string;
32
- contextEntityViews?: EntityCustomView[];
33
- }): NavigationViewInternal[];
34
- export {};
@@ -1,20 +0,0 @@
1
- import { CollectionConfig } from "@rebasepro/types";
2
- export declare function removeInitialAndTrailingSlashes(s: string): string;
3
- export declare function removeInitialSlash(s: string): string;
4
- export declare function removeTrailingSlash(s: string): string;
5
- export declare function addInitialSlash(s: string): string;
6
- export declare function getLastSegment(path: string): string;
7
- export declare function resolveCollectionPathIds(path: string, allCollections: CollectionConfig[]): string;
8
- /**
9
- * Find the corresponding view at any depth for a given path.
10
- * Note that path or segments of the paths can be collection aliases.
11
- * @param slugOrPath
12
- * @param collections
13
- */
14
- export declare function getCollectionBySlugWithin(slugOrPath: string, collections: CollectionConfig[]): CollectionConfig | undefined;
15
- /**
16
- * Get the subcollection combinations from a path:
17
- * "sites/es/locales" => ["sites/es/locales", "sites"]
18
- * @param subpaths
19
- */
20
- export declare function getCollectionPathsCombinations(subpaths: string[]): string[];
@@ -1,6 +0,0 @@
1
- import { CollectionConfig, EntityReference } from "@rebasepro/types";
2
- export declare function getParentReferencesFromPath(props: {
3
- path: string;
4
- collections: CollectionConfig[] | undefined;
5
- currentFullPath?: string;
6
- }): EntityReference[];
@@ -1,2 +0,0 @@
1
- import { CollectionConfig } from "@rebasepro/types";
2
- export declare function getEntityImagePreviewPropertyKey<M extends Record<string, unknown>>(collection: CollectionConfig<M>): string | undefined;
@@ -1,81 +0,0 @@
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
- }
@@ -1,122 +0,0 @@
1
- import { CollectionConfig } from "@rebasepro/types";
2
- type EntityCustomView<M extends Record<string, unknown> = Record<string, unknown>> = { key: string; [key: string]: unknown };
3
- import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
4
- import { getSubcollections } from "./resolutions";
5
-
6
- export type NavigationViewInternal<M extends Record<string, unknown> = Record<string, unknown>> =
7
- | NavigationViewEntityInternal<M>
8
- | NavigationViewCollectionInternal<M>
9
- | NavigationViewEntityCustomInternal<M>;
10
-
11
- export interface NavigationViewEntityInternal<M extends Record<string, unknown>> {
12
- type: "entity";
13
- entityId: string | number;
14
- slug: string;
15
- path: string;
16
- parentCollection: CollectionConfig<M>;
17
- }
18
-
19
- export interface NavigationViewCollectionInternal<M extends Record<string, unknown>> {
20
- type: "collection";
21
- id: string;
22
- slug: string;
23
- path: string;
24
- collection: CollectionConfig<M>;
25
- }
26
-
27
- export interface NavigationViewEntityCustomInternal<M extends Record<string, unknown>> {
28
- type: "custom_view";
29
- slug: string;
30
- path: string;
31
- entityId: string | number;
32
- view: EntityCustomView<M>;
33
- }
34
-
35
- export function getNavigationEntriesFromPath(props: {
36
- path: string,
37
- collections: CollectionConfig[] | undefined,
38
- currentFullPath?: string,
39
- contextEntityViews?: EntityCustomView[]
40
- }): NavigationViewInternal[] {
41
-
42
- const {
43
- path,
44
- collections = [],
45
- currentFullPath
46
- } = props;
47
-
48
- const subpaths = removeInitialAndTrailingSlashes(path).split("/");
49
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
50
-
51
- const result: NavigationViewInternal[] = [];
52
- for (let i = 0; i < subpathCombinations.length; i++) {
53
- const subpathCombination = subpathCombinations[i];
54
-
55
- const collection = collections && collections.find((entry) => entry.slug === subpathCombination);
56
-
57
- if (collection) {
58
- const collectionPath = currentFullPath && currentFullPath.length > 0
59
- ? (currentFullPath + "/" + collection.slug)
60
- : collection.slug;
61
- result.push({
62
- type: "collection",
63
- id: collection.slug,
64
- slug: collectionPath,
65
- path: collectionPath,
66
- collection
67
- });
68
- const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
69
- const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
70
- if (nextSegments.length > 0) {
71
- const entityId = nextSegments[0];
72
- const path = collectionPath + "/" + entityId;
73
- result.push({
74
- type: "entity",
75
- entityId,
76
- slug: collectionPath,
77
- path,
78
- parentCollection: collection
79
- });
80
- if (nextSegments.length > 1) {
81
- const newPath = nextSegments.slice(1).join("/");
82
- if (!collection) {
83
- throw Error("collection not found resolving path: " + collection);
84
- }
85
- const entityViews = collection.entityViews;
86
- const customView = entityViews && entityViews
87
- .map((entry) => resolveEntityView(entry, props.contextEntityViews))
88
- .filter((v): v is EntityCustomView => v != null)
89
- .find((entry) => entry.key === newPath);
90
- const subcollections = getSubcollections(collection);
91
- if (customView) {
92
- result.push({
93
- type: "custom_view",
94
- slug: collectionPath,
95
- entityId: entityId,
96
- path: path + "/" + customView.key,
97
- view: customView
98
- });
99
- } else if (subcollections) {
100
- result.push(...getNavigationEntriesFromPath({
101
- path: newPath,
102
- collections: subcollections,
103
- currentFullPath: path,
104
- contextEntityViews: props.contextEntityViews
105
- }));
106
- }
107
- }
108
- }
109
- break;
110
- }
111
-
112
- }
113
- return result;
114
- }
115
-
116
- function resolveEntityView(entityView: string | EntityCustomView, contextEntityViews?: EntityCustomView[]): EntityCustomView | undefined {
117
- if (typeof entityView === "string") {
118
- return contextEntityViews?.find((entry) => entry.key === entityView);
119
- } else {
120
- return entityView;
121
- }
122
- }
@@ -1,170 +0,0 @@
1
- import { CollectionConfig } from "@rebasepro/types";
2
-
3
- import { getSubcollections } from "./resolutions";
4
-
5
- export function removeInitialAndTrailingSlashes(s: string): string {
6
- return removeInitialSlash(removeTrailingSlash(s));
7
- }
8
-
9
- export function removeInitialSlash(s: string) {
10
- if (s.startsWith("/"))
11
- return s.slice(1);
12
- else return s;
13
- }
14
-
15
- export function removeTrailingSlash(s: string) {
16
- if (s.endsWith("/"))
17
- return s.slice(0, -1);
18
- else return s;
19
- }
20
-
21
- export function addInitialSlash(s: string) {
22
- if (s.startsWith("/"))
23
- return s;
24
- else return `/${s}`;
25
- }
26
-
27
- export function getLastSegment(path: string) {
28
- const cleanPath = removeInitialAndTrailingSlashes(path);
29
- if (cleanPath.includes("/")) {
30
- const segments = cleanPath.split("/");
31
- return segments[segments.length - 1];
32
- }
33
- return cleanPath;
34
- }
35
-
36
- export function resolveCollectionPathIds(path: string, allCollections: CollectionConfig[]): string {
37
- let remainingPath = removeInitialAndTrailingSlashes(path);
38
- if (!remainingPath) {
39
- return "";
40
- }
41
-
42
- let currentCollections: CollectionConfig[] | undefined = allCollections;
43
- const resolvedPathParts: string[] = [];
44
-
45
- while (remainingPath.length > 0) {
46
- if (!currentCollections || currentCollections.length === 0) {
47
- // We have remaining path segments but no more collections to match against
48
- console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
49
- resolvedPathParts.push(remainingPath);
50
- remainingPath = ""; // Stop processing
51
- break;
52
- }
53
-
54
- let foundMatch = false;
55
- // Sort potential matches by length descending to prioritize longer matches (e.g., "a/b" over "a")
56
- const potentialMatches: { col: CollectionConfig; match: string; }[] = currentCollections
57
- .flatMap(col => [{
58
- col,
59
- match: col.slug
60
- }])
61
- .filter(p => p.match && remainingPath.startsWith(p.match))
62
- .sort((a, b) => b.match.length - a.match.length);
63
-
64
- if (potentialMatches.length > 0) {
65
- const {
66
- col: foundCollection,
67
- match: matchString
68
- } = potentialMatches[0];
69
-
70
- resolvedPathParts.push(foundCollection.slug); // Use the defined path
71
- remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
72
-
73
- // Check if we are at the end of the path
74
- if (remainingPath.length === 0) {
75
- foundMatch = true;
76
- break; // Path ends with a collection segment
77
- }
78
-
79
- // The next segment must be a entity ID
80
- const idSeparatorIndex = remainingPath.indexOf("/");
81
- let entityId: string | number;
82
- if (idSeparatorIndex > -1) {
83
- entityId = remainingPath.substring(0, idSeparatorIndex);
84
- remainingPath = remainingPath.substring(idSeparatorIndex + 1);
85
- } else {
86
- // This should not happen if the original path is valid (odd segments)
87
- // but handle it defensively: assume the rest is the ID
88
- entityId = remainingPath;
89
- remainingPath = "";
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
- // Even if it ends here, we still need to push the ID
92
- }
93
-
94
- resolvedPathParts.push(entityId); // Append entity ID
95
- currentCollections = getSubcollections(foundCollection); // Move to subcollections
96
- foundMatch = true;
97
-
98
- if (!currentCollections && remainingPath.length > 0) {
99
- // Warn if the path continues but no subcollections were defined
100
- console.warn(`resolveCollectionPathIds: Path continues after entity ID "${entityId}", but no subcollections are defined for the preceding collection "${foundCollection.slug}" in path "${path}". Appending remaining original path.`);
101
- resolvedPathParts.push(remainingPath); // Append the rest
102
- remainingPath = ""; // Stop processing
103
- break;
104
- }
105
-
106
- }
107
-
108
- if (!foundMatch) {
109
- // Collection definition not found for the start of the remaining path
110
- console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
111
- resolvedPathParts.push(remainingPath); // Append the rest
112
- remainingPath = ""; // Stop processing
113
- break;
114
- }
115
- }
116
-
117
- return resolvedPathParts.join("/");
118
- }
119
-
120
- /**
121
- * Find the corresponding view at any depth for a given path.
122
- * Note that path or segments of the paths can be collection aliases.
123
- * @param slugOrPath
124
- * @param collections
125
- */
126
- export function getCollectionBySlugWithin(slugOrPath: string, collections: CollectionConfig[]): CollectionConfig | undefined {
127
-
128
- const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
129
- if (subpaths.length % 2 === 0) {
130
- throw Error(`getCollectionBySlug: Collection paths must have an odd number of segments: ${slugOrPath}`);
131
- }
132
-
133
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
134
- let result: CollectionConfig | undefined;
135
- for (let i = 0; i < subpathCombinations.length; i++) {
136
- const subpathCombination = subpathCombinations[i];
137
- const navigationEntry = collections && collections
138
- .sort((a, b) => (a.slug ?? "").localeCompare(b.slug ?? ""))
139
- .find((entry) => entry.slug === subpathCombination);
140
-
141
- if (navigationEntry) {
142
-
143
- if (subpathCombination === slugOrPath) {
144
- result = navigationEntry;
145
- } else if (getSubcollections(navigationEntry).length > 0) {
146
- const newPath = slugOrPath.replace(subpathCombination, "").split("/").slice(2).join("/");
147
- if (newPath.length > 0)
148
- result = getCollectionBySlugWithin(newPath, getSubcollections(navigationEntry));
149
- }
150
- }
151
- if (result) break;
152
- }
153
- return result;
154
- }
155
-
156
- /**
157
- * Get the subcollection combinations from a path:
158
- * "sites/es/locales" => ["sites/es/locales", "sites"]
159
- * @param subpaths
160
- */
161
- export function getCollectionPathsCombinations(subpaths: string[]): string[] {
162
- const entries = subpaths.length > 0 && subpaths.length % 2 === 0 ? subpaths.splice(0, subpaths.length - 1) : subpaths;
163
-
164
- const length = entries.length;
165
- const result: string[] = [];
166
- for (let i = length; i > 0; i = i - 2) {
167
- result.push(entries.slice(0, i).join("/"));
168
- }
169
- return result;
170
- }
@@ -1,58 +0,0 @@
1
- import { CollectionConfig, EntityReference } from "@rebasepro/types";
2
- import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
3
- import { getSubcollections } from "./resolutions";
4
-
5
- export function getParentReferencesFromPath(props: {
6
- path: string,
7
- collections: CollectionConfig[] | undefined,
8
- currentFullPath?: string,
9
- }): EntityReference[] {
10
-
11
- const {
12
- path,
13
- collections = [],
14
- currentFullPath
15
- } = props;
16
-
17
- const subpaths = removeInitialAndTrailingSlashes(path).split("/");
18
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
19
-
20
- const result: EntityReference[] = [];
21
- for (let i = 0; i < subpathCombinations.length; i++) {
22
- const subpathCombination = subpathCombinations[i];
23
-
24
- const collection: CollectionConfig | undefined = collections && collections.find((entry) => entry.slug === subpathCombination);
25
-
26
- // If we find a collection, we add the reference and continue
27
- if (collection) {
28
- const collectionPath = currentFullPath && currentFullPath.length > 0
29
- ? (currentFullPath + "/" + collection.slug) // Use the current full path if provided
30
- : collection.slug;
31
-
32
- const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
33
- const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
34
- if (nextSegments.length > 0) {
35
- const entityId = nextSegments[0];
36
- const path = collectionPath + "/" + entityId;
37
- result.push(new EntityReference({ id: entityId,
38
- path: collectionPath }));
39
- if (nextSegments.length > 1) {
40
- const newPath = nextSegments.slice(1).join("/");
41
- if (!collection) {
42
- throw Error("collection not found resolving path: " + collection);
43
- }
44
- if (getSubcollections(collection).length > 0) {
45
- result.push(...getParentReferencesFromPath({
46
- path: newPath,
47
- collections: getSubcollections(collection),
48
- currentFullPath: path
49
- }));
50
- }
51
- }
52
- }
53
- break;
54
- }
55
-
56
- }
57
- return result;
58
- }
@@ -1,48 +0,0 @@
1
- import { CollectionConfig } from "@rebasepro/types";
2
-
3
- export function getEntityImagePreviewPropertyKey<M extends Record<string, unknown>>(collection: CollectionConfig<M>): string | undefined {
4
-
5
- // find first storage property of type image
6
- for (const key in collection.properties) {
7
- const property = collection.properties[key];
8
- if (property.type === "string" && property.storage?.acceptedFiles?.includes("image/*")) {
9
- return key;
10
- }
11
- }
12
- // alternatively, look for the first array of images
13
- for (const key in collection.properties) {
14
- const property = collection.properties[key];
15
- if (property.type === "array" && !Array.isArray(property.of) && property.of?.type === "string" && property.of.storage?.acceptedFiles?.includes("image/*")) {
16
- return key;
17
- }
18
- }
19
- // also check for URL properties with image preview type
20
- for (const key in collection.properties) {
21
- const property = collection.properties[key];
22
- if (property.type === "string" && property.ui?.url === "image") {
23
- return key;
24
- }
25
- }
26
- // and arrays of URL properties with image preview type
27
- for (const key in collection.properties) {
28
- const property = collection.properties[key];
29
- if (property.type === "array" && property.of && !Array.isArray(property.of) && property.of.type === "string" && property.of.ui?.url === "image") {
30
- return key;
31
- }
32
- }
33
- // fallback: any storage property without explicit acceptedFiles (e.g. a generic "picture" field)
34
- for (const key in collection.properties) {
35
- const property = collection.properties[key];
36
- if (property.type === "string" && property.storage && !property.storage.acceptedFiles) {
37
- return key;
38
- }
39
- }
40
- // fallback: any array of storage properties without explicit acceptedFiles
41
- for (const key in collection.properties) {
42
- const property = collection.properties[key];
43
- if (property.type === "array" && !Array.isArray(property.of) && property.of?.type === "string" && property.of.storage && !property.of.storage.acceptedFiles) {
44
- return key;
45
- }
46
- }
47
- return undefined;
48
- }