@rebasepro/types 0.0.1-canary.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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/components/EntityFormActionsProps.d.ts +17 -0
  4. package/dist/components/EntityFormProps.d.ts +46 -0
  5. package/dist/components/PropertyPreviewProps.d.ts +50 -0
  6. package/dist/components/formex.d.ts +40 -0
  7. package/dist/components/index.d.ts +3 -0
  8. package/dist/controllers/analytics_controller.d.ts +7 -0
  9. package/dist/controllers/auth.d.ts +73 -0
  10. package/dist/controllers/customization_controller.d.ts +50 -0
  11. package/dist/controllers/datasource.d.ts +179 -0
  12. package/dist/controllers/dialogs_controller.d.ts +36 -0
  13. package/dist/controllers/index.d.ts +11 -0
  14. package/dist/controllers/local_config_persistence.d.ts +20 -0
  15. package/dist/controllers/navigation.d.ts +262 -0
  16. package/dist/controllers/side_dialogs_controller.d.ts +67 -0
  17. package/dist/controllers/side_entity_controller.d.ts +90 -0
  18. package/dist/controllers/snackbar.d.ts +24 -0
  19. package/dist/controllers/storage.d.ts +173 -0
  20. package/dist/index.d.ts +5 -0
  21. package/dist/index.es.js +113 -0
  22. package/dist/index.es.js.map +1 -0
  23. package/dist/index.umd.js +117 -0
  24. package/dist/index.umd.js.map +1 -0
  25. package/dist/rebase_context.d.ts +91 -0
  26. package/dist/types/backend.d.ts +254 -0
  27. package/dist/types/chips.d.ts +5 -0
  28. package/dist/types/collections.d.ts +887 -0
  29. package/dist/types/entities.d.ts +140 -0
  30. package/dist/types/entity_actions.d.ts +98 -0
  31. package/dist/types/entity_callbacks.d.ts +173 -0
  32. package/dist/types/entity_link_builder.d.ts +7 -0
  33. package/dist/types/entity_overrides.d.ts +5 -0
  34. package/dist/types/export_import.d.ts +21 -0
  35. package/dist/types/fields.d.ts +221 -0
  36. package/dist/types/index.d.ts +19 -0
  37. package/dist/types/locales.d.ts +4 -0
  38. package/dist/types/modify_collections.d.ts +5 -0
  39. package/dist/types/plugins.d.ts +276 -0
  40. package/dist/types/properties.d.ts +1103 -0
  41. package/dist/types/property_config.d.ts +68 -0
  42. package/dist/types/rebase.d.ts +180 -0
  43. package/dist/types/relations.d.ts +336 -0
  44. package/dist/types/user_management_delegate.d.ts +78 -0
  45. package/dist/types/websockets.d.ts +33 -0
  46. package/dist/users/index.d.ts +2 -0
  47. package/dist/users/roles.d.ts +22 -0
  48. package/dist/users/user.d.ts +42 -0
  49. package/package.json +137 -0
  50. package/src/components/EntityFormActionsProps.tsx +18 -0
  51. package/src/components/EntityFormProps.tsx +52 -0
  52. package/src/components/PropertyPreviewProps.tsx +61 -0
  53. package/src/components/formex.tsx +46 -0
  54. package/src/components/index.ts +3 -0
  55. package/src/controllers/analytics_controller.tsx +57 -0
  56. package/src/controllers/auth.tsx +94 -0
  57. package/src/controllers/customization_controller.tsx +61 -0
  58. package/src/controllers/datasource.ts +218 -0
  59. package/src/controllers/dialogs_controller.tsx +37 -0
  60. package/src/controllers/index.ts +11 -0
  61. package/src/controllers/local_config_persistence.tsx +22 -0
  62. package/src/controllers/navigation.ts +317 -0
  63. package/src/controllers/side_dialogs_controller.tsx +82 -0
  64. package/src/controllers/side_entity_controller.tsx +104 -0
  65. package/src/controllers/snackbar.ts +29 -0
  66. package/src/controllers/storage.ts +196 -0
  67. package/src/index.ts +5 -0
  68. package/src/rebase_context.tsx +122 -0
  69. package/src/types/backend.ts +385 -0
  70. package/src/types/chips.ts +46 -0
  71. package/src/types/collections.ts +1013 -0
  72. package/src/types/entities.ts +207 -0
  73. package/src/types/entity_actions.tsx +118 -0
  74. package/src/types/entity_callbacks.ts +217 -0
  75. package/src/types/entity_link_builder.ts +8 -0
  76. package/src/types/entity_overrides.tsx +6 -0
  77. package/src/types/export_import.ts +26 -0
  78. package/src/types/fields.tsx +298 -0
  79. package/src/types/index.ts +20 -0
  80. package/src/types/locales.ts +81 -0
  81. package/src/types/modify_collections.tsx +6 -0
  82. package/src/types/plugins.tsx +328 -0
  83. package/src/types/properties.ts +1270 -0
  84. package/src/types/property_config.tsx +93 -0
  85. package/src/types/rebase.tsx +211 -0
  86. package/src/types/relations.ts +351 -0
  87. package/src/types/user_management_delegate.ts +98 -0
  88. package/src/types/websockets.ts +37 -0
  89. package/src/users/index.ts +2 -0
  90. package/src/users/roles.ts +33 -0
  91. package/src/users/user.ts +46 -0
@@ -0,0 +1,93 @@
1
+ import React from "react";
2
+ import { ArrayProperty, MapProperty, Property } from "./properties";
3
+
4
+ import { BooleanProperty, DateProperty, GeopointProperty, NumberProperty, ReferenceProperty, RelationProperty, StringProperty } from "./properties";
5
+
6
+ export type ConfigProperty =
7
+ | (Omit<StringProperty, "name"> & { name?: string })
8
+ | (Omit<NumberProperty, "name"> & { name?: string })
9
+ | (Omit<BooleanProperty, "name"> & { name?: string })
10
+ | (Omit<DateProperty, "name"> & { name?: string })
11
+ | (Omit<GeopointProperty, "name"> & { name?: string })
12
+ | (Omit<ReferenceProperty, "name"> & { name?: string })
13
+ | (Omit<RelationProperty, "name"> & { name?: string })
14
+ | (Omit<ArrayProperty, "name" | "of" | "oneOf"> & {
15
+ name?: string;
16
+ of?: ConfigProperty | ConfigProperty[];
17
+ oneOf?: { properties: Record<string, ConfigProperty>; typeField?: string; valueField?: string; propertiesOrder?: string[] }
18
+ })
19
+ | (Omit<MapProperty, "name" | "properties"> & {
20
+ name?: string;
21
+ properties?: Record<string, ConfigProperty>
22
+ });
23
+
24
+ /**
25
+ * This is the configuration object for a property.
26
+ * These configs are generated by default for the properties defined in your
27
+ * collections' configuration, but you can define your own to be used.
28
+ */
29
+ export type PropertyConfig = {
30
+
31
+ /**
32
+ * Key used to identify this property config.
33
+ */
34
+ key: PropertyConfigId | string;
35
+
36
+ /**
37
+ * Name of this field type.
38
+ * This is not the name of the property.
39
+ */
40
+ name: string;
41
+
42
+ /**
43
+ * Default config for the property.
44
+ * This property or builder will be used as the base values for the resulting property.
45
+ * You can also use a builder function to generate the base property.
46
+ */
47
+ property: ConfigProperty;
48
+
49
+ /**
50
+ * Optional icon to be used in the field selector.
51
+ * Use a 24x24 component, in order not to break the layout.
52
+ * Any Rebase icon can be used.
53
+ */
54
+ Icon?: React.ComponentType;
55
+
56
+ /**
57
+ * CSS color, used only in some plugins like the field selector.
58
+ * e.g. "#2d7ff9"
59
+ */
60
+ color?: string;
61
+
62
+ /**
63
+ * Description of this field type.
64
+ */
65
+ description?: string;
66
+
67
+ }
68
+
69
+ export type PropertyConfigId =
70
+ "text_field" |
71
+ "multiline" |
72
+ "markdown" |
73
+ "url" |
74
+ "email" |
75
+ "user_select" |
76
+ "select" |
77
+ "multi_select" |
78
+ "number_input" |
79
+ "number_select" |
80
+ "multi_number_select" |
81
+ "file_upload" |
82
+ "multi_file_upload" |
83
+ "group" |
84
+ "key_value" |
85
+ "reference" |
86
+ "reference_as_string" |
87
+ "multi_references" |
88
+ "relation" |
89
+ "switch" |
90
+ "date_time" |
91
+ "repeat" |
92
+ "custom_array" |
93
+ "block";
@@ -0,0 +1,211 @@
1
+ import React from "react";
2
+ import { EntityCollection, EntityCustomView } from "./collections";
3
+ import { PropertyConfig } from "./property_config";
4
+ import { Locale } from "./locales";
5
+ import { EntityLinkBuilder } from "./entity_link_builder";
6
+ import { RebasePlugin } from "./plugins";
7
+ import { EntityAction } from "./entity_actions";
8
+ import { User } from "../users";
9
+ import {
10
+ AuthController, CMSAnalyticsEvent, CMSView, DataSource, StorageSource,
11
+ UserConfigurationPersistence, CollectionRegistryController, CMSUrlController, NavigationStateController
12
+ } from "../controllers";
13
+ import { RebaseContext } from "../rebase_context";
14
+ import { UserManagementDelegate } from "./user_management_delegate";
15
+
16
+ /**
17
+ * Controller to simulate different roles when dev mode is active.
18
+ * @group Models
19
+ */
20
+ export interface EffectiveRoleController {
21
+ effectiveRole: string | null;
22
+ setEffectiveRole: (role: string | null) => void;
23
+ }
24
+
25
+ /**
26
+ * Use this callback to build entity collections dynamically.
27
+ * You can use the user to decide which collections to show.
28
+ * You can also use the data source to fetch additional data to build the
29
+ * collections.
30
+ * Note: you can use any type of synchronous or asynchronous code here,
31
+ * including fetching data from external sources, like using the Firestore
32
+ * APIs directly, or a REST API.
33
+ * @group Models
34
+ */
35
+ export type EntityCollectionsBuilder<EC extends EntityCollection = EntityCollection> = (params: {
36
+ user: User | null,
37
+ authController: AuthController,
38
+ dataSource: DataSource
39
+ }) => EC[] | Promise<EC[]>;
40
+
41
+ /**
42
+ * Use this callback to build custom views dynamically.
43
+ * You can use the user to decide which views to show.
44
+ * You can also use the data source to fetch additional data to build the
45
+ * views. Note: you can use any type of synchronous or asynchronous code here,
46
+ * including fetching data from external sources, like using the Firestore
47
+ * APIs directly, or a REST API.
48
+ * @group Models
49
+ */
50
+ export type CMSViewsBuilder = (params: {
51
+ user: User | null,
52
+ authController: AuthController,
53
+ dataSource: DataSource
54
+ }) => CMSView[] | Promise<CMSView[]>;
55
+
56
+ /**
57
+ * @group Models
58
+ */
59
+ export type RebaseProps<USER extends User> = {
60
+
61
+ /**
62
+ * Use this function to return the components you want to render under
63
+ * Rebase
64
+ * @param props
65
+ */
66
+ children: (props: {
67
+ /**
68
+ * Context of the app
69
+ */
70
+ context: RebaseContext;
71
+ /**
72
+ * Is one of the main processes, auth and navigation resolving, currently
73
+ * loading. If you are building your custom implementation, you probably
74
+ * want to show a loading indicator if this flag is `true`
75
+ */
76
+ loading: boolean;
77
+ }) => React.ReactNode;
78
+
79
+ /**
80
+ * If you have a custom API key, you can use it here.
81
+ */
82
+ apiKey?: string;
83
+
84
+ /**
85
+ * Base URL for the backend API (e.g. "http://localhost:3001").
86
+ * When provided, this is available via `useApiConfig()` to any hook
87
+ * in the tree, reducing repetitive `apiUrl` threading.
88
+ */
89
+ apiUrl?: string;
90
+
91
+ /**
92
+ * Record of custom form fields to be used in the CMS.
93
+ * You can use the key to reference the custom field in
94
+ * the `propertyConfig` prop of a property in a collection.
95
+ */
96
+ propertyConfigs?: Record<string, PropertyConfig>;
97
+
98
+ /**
99
+ * This controller is in charge of resolving the collection and entity paths.
100
+ */
101
+ collectionRegistryController: CollectionRegistryController;
102
+
103
+ /**
104
+ * This controller is in charge of resolving the URL configurations map and building paths
105
+ */
106
+ cmsUrlController: CMSUrlController;
107
+
108
+ /**
109
+ * This controller is in charge of resolving the navigation views and state
110
+ */
111
+ navigationStateController: NavigationStateController;
112
+
113
+ /**
114
+ * List of additional custom views for entities.
115
+ * You can use the key to reference the custom view in
116
+ * the `entityViews` prop of a collection.
117
+ *
118
+ * You can also define an entity view from the UI.
119
+ */
120
+ entityViews?: EntityCustomView[];
121
+
122
+ /**
123
+ * List of actions that can be performed on entities.
124
+ * These actions are displayed in the entity view and in the collection view.
125
+ * You can later reuse these actions in the `entityActions` prop of a collection,
126
+ * by specifying the `key` of the action.
127
+ */
128
+ entityActions?: EntityAction[];
129
+
130
+ /**
131
+ * Format of the dates in the CMS.
132
+ * Defaults to 'MMMM dd, yyyy, HH:mm:ss'
133
+ */
134
+ dateTimeFormat?: string;
135
+
136
+ /**
137
+ * Locale of the CMS, currently only affecting dates
138
+ */
139
+ locale?: Locale;
140
+
141
+ /**
142
+ * Connector to your database
143
+ */
144
+ dataSource: DataSource;
145
+
146
+ /**
147
+ * Connector to your file upload/fetch implementation
148
+ */
149
+ storageSource: StorageSource;
150
+
151
+ /**
152
+ * Delegate for implementing your auth operations.
153
+ */
154
+ authController: AuthController<USER>;
155
+
156
+ /**
157
+ * Use this controller to access the configuration that is stored locally,
158
+ * and not defined in code
159
+ */
160
+ userConfigPersistence?: UserConfigurationPersistence;
161
+
162
+ /**
163
+ * Use plugins to modify the behaviour of the CMS.
164
+ * DEPRECATED: use the `plugins` prop in the `useBuildNavigationController` instead.
165
+ * This prop will work as a fallback for the `plugins` prop in the `useBuildNavigationController`.
166
+ * @deprecated
167
+ */
168
+ plugins?: RebasePlugin<any, any, any>[];
169
+
170
+ /**
171
+ * Callback used to get analytics events from the CMS
172
+ */
173
+ onAnalyticsEvent?: (event: CMSAnalyticsEvent, data?: object) => void;
174
+
175
+ /**
176
+ * Optional link builder you can add to generate a button in your entity forms.
177
+ * The function must return a URL that gets opened when the button is clicked
178
+ */
179
+ entityLinkBuilder?: EntityLinkBuilder;
180
+
181
+ /**
182
+ * You can use this props to provide your own user management implementation.
183
+ * Note that this will not affect the UI, but it will be used to show user information
184
+ * in various places of the CMS, for example, to show who created or modified an entity,
185
+ * or to assign ownership of an entity.
186
+ *
187
+ * You can also use this data to be retrieved in your custom properties,
188
+ * for example, to show a list of users in a dropdown.
189
+ *
190
+ * If you are using the Rebase user management plugin, this
191
+ * prop will be implemented automatically.
192
+ */
193
+ userManagement?: UserManagementDelegate;
194
+
195
+ components?: {
196
+
197
+ /**
198
+ * Component to render when a reference is missing
199
+ */
200
+ missingReference?: React.ComponentType<{
201
+ path: string,
202
+ }>;
203
+
204
+ };
205
+
206
+ /**
207
+ * Controller to simulate different roles when dev mode is active.
208
+ */
209
+ effectiveRoleController?: EffectiveRoleController;
210
+
211
+ };
@@ -0,0 +1,351 @@
1
+ import { EntityCollection } from "./collections";
2
+
3
+ /**
4
+ * @group Models
5
+ */
6
+ export type OnAction = "cascade" | "restrict" | "no action" | "set null" | "set default";
7
+
8
+ /**
9
+ * Extended relation that combines base relation with Rebase UI config
10
+ * @group Models
11
+ */
12
+ export interface Relation {
13
+ /**
14
+ * The application-level name for this relationship.
15
+ * If not provided, it will be inferred from the target collection path.
16
+ * @example "posts"
17
+ */
18
+ relationName?: string;
19
+
20
+ /**
21
+ * The final collection you want to retrieve records from.
22
+ */
23
+ target: () => EntityCollection;
24
+
25
+ /**
26
+ * The nature of the relationship, determining if one or many records are returned.
27
+ */
28
+ cardinality: "one" | "many";
29
+
30
+ /**
31
+ * Which side owns the persistence for this relationship.
32
+ * - "owning": The foreign key (for one-to-one/many-to-one) or the junction table (for many-to-many) is managed by this collection.
33
+ * - "inverse": The foreign key is on the target collection's table. This side of the relation is typically read-only.
34
+ * Defaults to "owning".
35
+ */
36
+ direction?: "owning" | "inverse";
37
+
38
+ /**
39
+ * The name of the inverse relation.
40
+ * This is only needed when the inverse relation is not the same as the relation name.
41
+ * For example, if the relation name is "posts", the inverse relation name might be "author".
42
+ */
43
+ inverseRelationName?: string
44
+
45
+ /**
46
+ * Column on THIS table that stores the foreign key to the target.
47
+ * Required when `direction` is "owning" and `cardinality` is "one".
48
+ * @example "author_id"
49
+ */
50
+ localKey?: string;
51
+
52
+ /**
53
+ * Column on the TARGET table that stores the foreign key to this entity.
54
+ * Required when `direction` is "inverse".
55
+ * @example "post_id"
56
+ */
57
+ foreignKeyOnTarget?: string;
58
+
59
+ /**
60
+ * Defines the junction table for a many-to-many relationship.
61
+ * Required when `cardinality` is "many" and `direction` is "owning".
62
+ *
63
+ * @example Simple many-to-many between Users and Roles:
64
+ * ```typescript
65
+ * // Users collection
66
+ * {
67
+ * relations: [{
68
+ * relationName: "roles",
69
+ * target: () => rolesCollection,
70
+ * cardinality: "many",
71
+ * through: {
72
+ * table: "user_roles", // Junction table name
73
+ * sourceColumn: "user_id", // Column that references this collection's ID
74
+ * targetColumn: "role_id" // Column that references target collection's ID
75
+ * }
76
+ * }]
77
+ * }
78
+ *
79
+ * // This creates a junction table like:
80
+ * // CREATE TABLE user_roles (
81
+ * // user_id INTEGER REFERENCES users(id),
82
+ * // role_id INTEGER REFERENCES roles(id),
83
+ * // PRIMARY KEY (user_id, role_id)
84
+ * // );
85
+ * ```
86
+ *
87
+ * @example Many-to-many with additional junction table data:
88
+ * ```typescript
89
+ * // Students and Courses with enrollment date
90
+ * {
91
+ * relations: [{
92
+ * relationName: "courses",
93
+ * target: () => coursesCollection,
94
+ * cardinality: "many",
95
+ * through: {
96
+ * table: "enrollments",
97
+ * sourceColumn: "student_id",
98
+ * targetColumn: "course_id"
99
+ * }
100
+ * }]
101
+ * }
102
+ *
103
+ * // Junction table can have additional columns:
104
+ * // CREATE TABLE enrollments (
105
+ * // student_id INTEGER REFERENCES students(id),
106
+ * // course_id INTEGER REFERENCES courses(id),
107
+ * // enrolled_at TIMESTAMP DEFAULT NOW(),
108
+ * // grade VARCHAR(2),
109
+ * // PRIMARY KEY (student_id, course_id)
110
+ * // );
111
+ * ```
112
+ */
113
+ through?: {
114
+ table: string;
115
+ sourceColumn: string; // FK to "this" collection's PK
116
+ targetColumn: string; // FK to the target collection's PK
117
+ };
118
+
119
+ /**
120
+ * An explicit, ordered array of JOINs to perform to get from the source
121
+ * to the target. Used for multi-hop relations, composite keys, or when you need
122
+ * fine-grained control over the join logic.
123
+ *
124
+ * When `joinPath` is provided, it overrides all other relation configuration
125
+ * (localKey, foreignKeyOnTarget, through) and gives you complete control
126
+ * over how tables are joined together.
127
+ *
128
+ * @example Simple one-to-one join (equivalent to localKey):
129
+ * ```typescript
130
+ * // Posts -> Authors relationship
131
+ * {
132
+ * relationName: "author",
133
+ * target: () => authorsCollection,
134
+ * cardinality: "one",
135
+ * joinPath: [
136
+ * {
137
+ * table: "authors",
138
+ * on: {
139
+ * from: "author_id", // Column on posts table
140
+ * to: "id" // Column on authors table
141
+ * }
142
+ * }
143
+ * ]
144
+ * }
145
+ *
146
+ * // Generates: SELECT * FROM posts JOIN authors ON posts.author_id = authors.id
147
+ * ```
148
+ *
149
+ * @example Multi-hop relationship (3 tables):
150
+ * ```typescript
151
+ * // Users -> Permissions through Roles
152
+ * {
153
+ * relationName: "permissions",
154
+ * target: () => permissionsCollection,
155
+ * cardinality: "many",
156
+ * joinPath: [
157
+ * {
158
+ * table: "user_roles",
159
+ * on: {
160
+ * from: "id", // users.id
161
+ * to: "user_id" // user_roles.user_id
162
+ * }
163
+ * },
164
+ * {
165
+ * table: "roles",
166
+ * on: {
167
+ * from: "role_id", // user_roles.role_id
168
+ * to: "id" // roles.id
169
+ * }
170
+ * },
171
+ * {
172
+ * table: "role_permissions",
173
+ * on: {
174
+ * from: "id", // roles.id
175
+ * to: "role_id" // role_permissions.role_id
176
+ * }
177
+ * },
178
+ * {
179
+ * table: "permissions",
180
+ * on: {
181
+ * from: "permission_id", // role_permissions.permission_id
182
+ * to: "id" // permissions.id
183
+ * }
184
+ * }
185
+ * ]
186
+ * }
187
+ *
188
+ * // Generates:
189
+ * // SELECT * FROM users
190
+ * // JOIN user_roles ON users.id = user_roles.user_id
191
+ * // JOIN roles ON user_roles.role_id = roles.id
192
+ * // JOIN role_permissions ON roles.id = role_permissions.role_id
193
+ * // JOIN permissions ON role_permissions.permission_id = permissions.id
194
+ * ```
195
+ *
196
+ * @example Composite key relationship:
197
+ * ```typescript
198
+ * // Orders -> Customer by company_code + region
199
+ * {
200
+ * relationName: "customer",
201
+ * target: () => customersCollection,
202
+ * cardinality: "one",
203
+ * joinPath: [
204
+ * {
205
+ * table: "customers",
206
+ * on: {
207
+ * from: ["company_code", "region_id"], // Multiple columns from orders
208
+ * to: ["code", "region_id"] // Multiple columns on customers
209
+ * }
210
+ * }
211
+ * ]
212
+ * }
213
+ *
214
+ * // Generates:
215
+ * // SELECT * FROM orders
216
+ * // JOIN customers ON orders.company_code = customers.code
217
+ * // AND orders.region_id = customers.region_id
218
+ * ```
219
+ *
220
+ * @example Self-referencing with intermediate table:
221
+ * ```typescript
222
+ * // Users -> Friends (many-to-many self-reference)
223
+ * {
224
+ * relationName: "friends",
225
+ * target: () => usersCollection, // Same collection
226
+ * cardinality: "many",
227
+ * joinPath: [
228
+ * {
229
+ * table: "friendships",
230
+ * on: {
231
+ * from: "id", // users.id
232
+ * to: "user_id" // friendships.user_id
233
+ * }
234
+ * },
235
+ * {
236
+ * table: "users",
237
+ * on: {
238
+ * from: "friend_id", // friendships.friend_id
239
+ * to: "id" // users.id (target)
240
+ * }
241
+ * }
242
+ * ]
243
+ * }
244
+ * ```
245
+ *
246
+ * @example Complex business logic join:
247
+ * ```typescript
248
+ * // Products -> Active Suppliers (only current, non-expired contracts)
249
+ * {
250
+ * relationName: "activeSuppliers",
251
+ * target: () => suppliersCollection,
252
+ * cardinality: "many",
253
+ * joinPath: [
254
+ * {
255
+ * table: "product_supplier_contracts",
256
+ * on: {
257
+ * from: "id", // products.id
258
+ * to: "product_id" // contracts.product_id
259
+ * }
260
+ * },
261
+ * {
262
+ * table: "suppliers",
263
+ * on: {
264
+ * from: "supplier_id", // contracts.supplier_id
265
+ * to: "id" // suppliers.id
266
+ * }
267
+ * }
268
+ * ]
269
+ * // Note: Additional WHERE conditions for active/non-expired
270
+ * // would be handled in the query logic, not in joinPath
271
+ * }
272
+ * ```
273
+ */
274
+ joinPath?: JoinStep[];
275
+
276
+ /**
277
+ * Action to perform on update.
278
+ */
279
+ onUpdate?: OnAction;
280
+ /**
281
+ * Action to perform on delete.
282
+ */
283
+ onDelete?: OnAction;
284
+
285
+ overrides?: Partial<EntityCollection>;
286
+
287
+ validation?: {
288
+ required?: boolean;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Defines a single, explicit step in a multi-join path.
294
+ *
295
+ * Each step represents one JOIN operation in the sequence. The `from` columns
296
+ * refer to the previous table in the chain (or the source table for the first step),
297
+ * and the `to` columns refer to the current table being joined.
298
+ *
299
+ * @example Single column join:
300
+ * ```typescript
301
+ * {
302
+ * table: "authors",
303
+ * on: {
304
+ * from: "author_id", // Column from previous table (e.g., posts.author_id)
305
+ * to: "id" // Column from current table (authors.id)
306
+ * }
307
+ * }
308
+ * ```
309
+ *
310
+ * @example Multi-column composite key join:
311
+ * ```typescript
312
+ * {
313
+ * table: "order_items",
314
+ * on: {
315
+ * from: ["order_id", "store_id"], // Multiple columns from previous table
316
+ * to: ["order_id", "store_id"] // Corresponding columns in current table
317
+ * }
318
+ * }
319
+ * ```
320
+ */
321
+ export interface JoinStep {
322
+ /**
323
+ * The database table name to join TO in this step.
324
+ * This is the table you're joining into, not the table you're joining from.
325
+ *
326
+ * @example "authors", "user_roles", "product_categories"
327
+ */
328
+ table: string;
329
+
330
+ /**
331
+ * The join condition for this step. Defines how the previous table
332
+ * connects to the current table.
333
+ *
334
+ * - `from`: Column name(s) on the PREVIOUS table in the join chain
335
+ * - `to`: Column name(s) on the CURRENT table (specified in `table`)
336
+ *
337
+ * For the first step, `from` refers to the source collection's table.
338
+ * For subsequent steps, `from` refers to the table from the previous step.
339
+ *
340
+ * Both `from` and `to` support:
341
+ * - Single column: `"user_id"`
342
+ * - Multiple columns: `["company_id", "region_id"]` for composite keys
343
+ *
344
+ * When using arrays, both `from` and `to` must have the same length,
345
+ * and columns are matched by position (index 0 with index 0, etc.).
346
+ */
347
+ on: {
348
+ from: string | string[];
349
+ to: string | string[];
350
+ };
351
+ }