@rebasepro/server-postgresql 0.0.1-canary.09e5ec5

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 (196) hide show
  1. package/LICENSE +6 -0
  2. package/README.md +106 -0
  3. package/build-errors.txt +37 -0
  4. package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
  5. package/dist/common/src/collections/index.d.ts +1 -0
  6. package/dist/common/src/data/buildRebaseData.d.ts +14 -0
  7. package/dist/common/src/index.d.ts +3 -0
  8. package/dist/common/src/util/builders.d.ts +57 -0
  9. package/dist/common/src/util/callbacks.d.ts +6 -0
  10. package/dist/common/src/util/collections.d.ts +11 -0
  11. package/dist/common/src/util/common.d.ts +2 -0
  12. package/dist/common/src/util/conditions.d.ts +26 -0
  13. package/dist/common/src/util/entities.d.ts +58 -0
  14. package/dist/common/src/util/enums.d.ts +3 -0
  15. package/dist/common/src/util/index.d.ts +16 -0
  16. package/dist/common/src/util/navigation_from_path.d.ts +34 -0
  17. package/dist/common/src/util/navigation_utils.d.ts +20 -0
  18. package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
  19. package/dist/common/src/util/paths.d.ts +14 -0
  20. package/dist/common/src/util/permissions.d.ts +5 -0
  21. package/dist/common/src/util/references.d.ts +2 -0
  22. package/dist/common/src/util/relations.d.ts +22 -0
  23. package/dist/common/src/util/resolutions.d.ts +72 -0
  24. package/dist/common/src/util/storage.d.ts +24 -0
  25. package/dist/index.es.js +11298 -0
  26. package/dist/index.es.js.map +1 -0
  27. package/dist/index.umd.js +11306 -0
  28. package/dist/index.umd.js.map +1 -0
  29. package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +100 -0
  30. package/dist/server-postgresql/src/PostgresBootstrapper.d.ts +40 -0
  31. package/dist/server-postgresql/src/auth/ensure-tables.d.ts +6 -0
  32. package/dist/server-postgresql/src/auth/services.d.ts +192 -0
  33. package/dist/server-postgresql/src/cli.d.ts +1 -0
  34. package/dist/server-postgresql/src/collections/PostgresCollectionRegistry.d.ts +43 -0
  35. package/dist/server-postgresql/src/connection.d.ts +40 -0
  36. package/dist/server-postgresql/src/data-transformer.d.ts +58 -0
  37. package/dist/server-postgresql/src/databasePoolManager.d.ts +20 -0
  38. package/dist/server-postgresql/src/history/HistoryService.d.ts +71 -0
  39. package/dist/server-postgresql/src/history/ensure-history-table.d.ts +7 -0
  40. package/dist/server-postgresql/src/index.d.ts +13 -0
  41. package/dist/server-postgresql/src/interfaces.d.ts +18 -0
  42. package/dist/server-postgresql/src/schema/auth-schema.d.ts +868 -0
  43. package/dist/server-postgresql/src/schema/doctor-cli.d.ts +2 -0
  44. package/dist/server-postgresql/src/schema/doctor.d.ts +43 -0
  45. package/dist/server-postgresql/src/schema/generate-drizzle-schema-logic.d.ts +2 -0
  46. package/dist/server-postgresql/src/schema/generate-drizzle-schema.d.ts +1 -0
  47. package/dist/server-postgresql/src/schema/introspect-db-logic.d.ts +82 -0
  48. package/dist/server-postgresql/src/schema/introspect-db.d.ts +1 -0
  49. package/dist/server-postgresql/src/schema/test-schema.d.ts +24 -0
  50. package/dist/server-postgresql/src/services/BranchService.d.ts +47 -0
  51. package/dist/server-postgresql/src/services/EntityFetchService.d.ts +209 -0
  52. package/dist/server-postgresql/src/services/EntityPersistService.d.ts +41 -0
  53. package/dist/server-postgresql/src/services/RelationService.d.ts +98 -0
  54. package/dist/server-postgresql/src/services/entity-helpers.d.ts +38 -0
  55. package/dist/server-postgresql/src/services/entityService.d.ts +104 -0
  56. package/dist/server-postgresql/src/services/index.d.ts +4 -0
  57. package/dist/server-postgresql/src/services/realtimeService.d.ts +188 -0
  58. package/dist/server-postgresql/src/utils/drizzle-conditions.d.ts +116 -0
  59. package/dist/server-postgresql/src/websocket.d.ts +5 -0
  60. package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
  61. package/dist/types/src/controllers/auth.d.ts +119 -0
  62. package/dist/types/src/controllers/client.d.ts +170 -0
  63. package/dist/types/src/controllers/collection_registry.d.ts +45 -0
  64. package/dist/types/src/controllers/customization_controller.d.ts +60 -0
  65. package/dist/types/src/controllers/data.d.ts +168 -0
  66. package/dist/types/src/controllers/data_driver.d.ts +160 -0
  67. package/dist/types/src/controllers/database_admin.d.ts +11 -0
  68. package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
  69. package/dist/types/src/controllers/effective_role.d.ts +4 -0
  70. package/dist/types/src/controllers/email.d.ts +34 -0
  71. package/dist/types/src/controllers/index.d.ts +18 -0
  72. package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
  73. package/dist/types/src/controllers/navigation.d.ts +213 -0
  74. package/dist/types/src/controllers/registry.d.ts +54 -0
  75. package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
  76. package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
  77. package/dist/types/src/controllers/snackbar.d.ts +24 -0
  78. package/dist/types/src/controllers/storage.d.ts +171 -0
  79. package/dist/types/src/index.d.ts +4 -0
  80. package/dist/types/src/rebase_context.d.ts +105 -0
  81. package/dist/types/src/types/backend.d.ts +536 -0
  82. package/dist/types/src/types/builders.d.ts +15 -0
  83. package/dist/types/src/types/chips.d.ts +5 -0
  84. package/dist/types/src/types/collections.d.ts +856 -0
  85. package/dist/types/src/types/cron.d.ts +102 -0
  86. package/dist/types/src/types/data_source.d.ts +64 -0
  87. package/dist/types/src/types/entities.d.ts +145 -0
  88. package/dist/types/src/types/entity_actions.d.ts +98 -0
  89. package/dist/types/src/types/entity_callbacks.d.ts +173 -0
  90. package/dist/types/src/types/entity_link_builder.d.ts +7 -0
  91. package/dist/types/src/types/entity_overrides.d.ts +10 -0
  92. package/dist/types/src/types/entity_views.d.ts +61 -0
  93. package/dist/types/src/types/export_import.d.ts +21 -0
  94. package/dist/types/src/types/index.d.ts +23 -0
  95. package/dist/types/src/types/locales.d.ts +4 -0
  96. package/dist/types/src/types/modify_collections.d.ts +5 -0
  97. package/dist/types/src/types/plugins.d.ts +279 -0
  98. package/dist/types/src/types/properties.d.ts +1176 -0
  99. package/dist/types/src/types/property_config.d.ts +70 -0
  100. package/dist/types/src/types/relations.d.ts +336 -0
  101. package/dist/types/src/types/slots.d.ts +252 -0
  102. package/dist/types/src/types/translations.d.ts +870 -0
  103. package/dist/types/src/types/user_management_delegate.d.ts +121 -0
  104. package/dist/types/src/types/websockets.d.ts +78 -0
  105. package/dist/types/src/users/index.d.ts +2 -0
  106. package/dist/types/src/users/roles.d.ts +22 -0
  107. package/dist/types/src/users/user.d.ts +46 -0
  108. package/drizzle-test/0000_woozy_junta.sql +6 -0
  109. package/drizzle-test/0001_youthful_arachne.sql +1 -0
  110. package/drizzle-test/0002_lively_dragon_lord.sql +2 -0
  111. package/drizzle-test/0003_mean_king_cobra.sql +2 -0
  112. package/drizzle-test/meta/0000_snapshot.json +47 -0
  113. package/drizzle-test/meta/0001_snapshot.json +48 -0
  114. package/drizzle-test/meta/0002_snapshot.json +38 -0
  115. package/drizzle-test/meta/0003_snapshot.json +48 -0
  116. package/drizzle-test/meta/_journal.json +34 -0
  117. package/drizzle-test-out/0000_tan_trauma.sql +6 -0
  118. package/drizzle-test-out/0001_rapid_drax.sql +1 -0
  119. package/drizzle-test-out/meta/0000_snapshot.json +44 -0
  120. package/drizzle-test-out/meta/0001_snapshot.json +54 -0
  121. package/drizzle-test-out/meta/_journal.json +20 -0
  122. package/drizzle.test.config.ts +10 -0
  123. package/jest-all.log +3128 -0
  124. package/jest.log +49 -0
  125. package/package.json +92 -0
  126. package/scratch.ts +41 -0
  127. package/src/PostgresBackendDriver.ts +1008 -0
  128. package/src/PostgresBootstrapper.ts +231 -0
  129. package/src/auth/ensure-tables.ts +381 -0
  130. package/src/auth/services.ts +799 -0
  131. package/src/cli.ts +648 -0
  132. package/src/collections/PostgresCollectionRegistry.ts +96 -0
  133. package/src/connection.ts +84 -0
  134. package/src/data-transformer.ts +608 -0
  135. package/src/databasePoolManager.ts +85 -0
  136. package/src/history/HistoryService.ts +248 -0
  137. package/src/history/ensure-history-table.ts +45 -0
  138. package/src/index.ts +13 -0
  139. package/src/interfaces.ts +60 -0
  140. package/src/schema/auth-schema.ts +169 -0
  141. package/src/schema/doctor-cli.ts +47 -0
  142. package/src/schema/doctor.ts +595 -0
  143. package/src/schema/generate-drizzle-schema-logic.ts +765 -0
  144. package/src/schema/generate-drizzle-schema.ts +151 -0
  145. package/src/schema/introspect-db-logic.ts +542 -0
  146. package/src/schema/introspect-db.ts +211 -0
  147. package/src/schema/test-schema.ts +11 -0
  148. package/src/services/BranchService.ts +237 -0
  149. package/src/services/EntityFetchService.ts +1576 -0
  150. package/src/services/EntityPersistService.ts +349 -0
  151. package/src/services/RelationService.ts +1274 -0
  152. package/src/services/entity-helpers.ts +147 -0
  153. package/src/services/entityService.ts +211 -0
  154. package/src/services/index.ts +13 -0
  155. package/src/services/realtimeService.ts +1034 -0
  156. package/src/utils/drizzle-conditions.ts +1000 -0
  157. package/src/websocket.ts +518 -0
  158. package/test/auth-services.test.ts +661 -0
  159. package/test/batch-many-to-many-regression.test.ts +573 -0
  160. package/test/branchService.test.ts +367 -0
  161. package/test/data-transformer-hardening.test.ts +417 -0
  162. package/test/data-transformer.test.ts +175 -0
  163. package/test/doctor.test.ts +182 -0
  164. package/test/drizzle-conditions.test.ts +895 -0
  165. package/test/entityService.errors.test.ts +367 -0
  166. package/test/entityService.relations.test.ts +1008 -0
  167. package/test/entityService.subcollection-search.test.ts +566 -0
  168. package/test/entityService.test.ts +1035 -0
  169. package/test/generate-drizzle-schema.test.ts +988 -0
  170. package/test/historyService.test.ts +141 -0
  171. package/test/introspect-db-generation.test.ts +436 -0
  172. package/test/introspect-db-utils.test.ts +389 -0
  173. package/test/n-plus-one-regression.test.ts +314 -0
  174. package/test/postgresDataDriver.test.ts +648 -0
  175. package/test/realtimeService.test.ts +307 -0
  176. package/test/relation-pipeline-gaps.test.ts +637 -0
  177. package/test/relations.test.ts +1115 -0
  178. package/test/unmapped-tables-safety.test.ts +345 -0
  179. package/test-drizzle-bug.ts +18 -0
  180. package/test-drizzle-out/0000_cultured_freak.sql +7 -0
  181. package/test-drizzle-out/0001_tiresome_professor_monster.sql +1 -0
  182. package/test-drizzle-out/meta/0000_snapshot.json +55 -0
  183. package/test-drizzle-out/meta/0001_snapshot.json +63 -0
  184. package/test-drizzle-out/meta/_journal.json +20 -0
  185. package/test-drizzle-prompt.sh +2 -0
  186. package/test-policy-prompt.sh +3 -0
  187. package/test-programmatic.ts +30 -0
  188. package/test-programmatic2.ts +59 -0
  189. package/test-schema-no-policies.ts +12 -0
  190. package/test_drizzle_mock.js +3 -0
  191. package/test_find_changed.mjs +32 -0
  192. package/test_hash.js +14 -0
  193. package/test_output.txt +3145 -0
  194. package/tsconfig.json +49 -0
  195. package/tsconfig.prod.json +20 -0
  196. package/vite.config.ts +82 -0
@@ -0,0 +1,147 @@
1
+ import { PgTable, AnyPgColumn } from "drizzle-orm/pg-core";
2
+ import { EntityCollection, Property } from "@rebasepro/types";
3
+ import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
4
+ import { getTableName } from "@rebasepro/common";
5
+
6
+ /**
7
+ * Shared helper functions for entity operations.
8
+ * These are used by EntityFetchService, EntityPersistService, and RelationService.
9
+ *
10
+ * All functions that need collection/table lookups require an explicit
11
+ * `PostgresCollectionRegistry` instance — there is no global singleton.
12
+ */
13
+
14
+ /**
15
+ * Interface for Drizzle column metadata introspection.
16
+ * Replaces unsafe `as unknown as Record<string, unknown>` double-cast chains.
17
+ */
18
+ export interface DrizzleColumnMeta {
19
+ columnType?: string;
20
+ dataType?: string;
21
+ primary?: boolean;
22
+ }
23
+
24
+ /** Safely extract Drizzle column metadata from a column object. */
25
+ export function getColumnMeta(col: AnyPgColumn): DrizzleColumnMeta {
26
+ const raw = col as unknown as Record<string | symbol, unknown>;
27
+ return {
28
+ columnType: typeof raw.columnType === "string" ? raw.columnType : undefined,
29
+ dataType: typeof raw.dataType === "string" ? raw.dataType : undefined,
30
+ primary: typeof raw.primary === "boolean" ? raw.primary : undefined
31
+ };
32
+ }
33
+
34
+ export function getCollectionByPath(collectionPath: string, registry: PostgresCollectionRegistry): EntityCollection {
35
+ const collection = registry.getCollectionByPath(collectionPath);
36
+ if (!collection) {
37
+ const registered = registry.getCollections().map(c => c.slug).join(", ");
38
+ throw new Error(`Collection not found: ${collectionPath}. Registered collections: [${registered}]`);
39
+ }
40
+ return collection;
41
+ }
42
+
43
+ export function getTableForCollection(collection: EntityCollection, registry: PostgresCollectionRegistry): PgTable<any> {
44
+ const tableName = getTableName(collection);
45
+ const table = registry.getTable(tableName);
46
+ if (!table) {
47
+ throw new Error(`Table not found for collection '${collection.slug}' (table: ${tableName})`);
48
+ }
49
+ return table;
50
+ }
51
+
52
+ export function getPrimaryKeys(collection: EntityCollection, registry: PostgresCollectionRegistry): { fieldName: string; type: "string" | "number"; isUUID?: boolean }[] {
53
+ const table = getTableForCollection(collection, registry);
54
+
55
+ // Fallback to explicitly defined isId properties
56
+ if (collection.properties) {
57
+ const idProps = Object.entries(collection.properties)
58
+ .filter(([_, prop]) => "isId" in (prop as object) && Boolean((prop as { isId?: unknown }).isId))
59
+ .map(([key, prop]) => ({
60
+ fieldName: key,
61
+ type: prop.type === "number" ? "number" as const : "string" as const,
62
+ isUUID: (prop as { isId?: unknown }).isId === "uuid"
63
+ }));
64
+
65
+ if (idProps.length > 0) {
66
+ return idProps;
67
+ }
68
+ }
69
+
70
+ // Otherwise infer from Drizzle schema
71
+ const keys: { fieldName: string; type: "string" | "number"; isUUID?: boolean }[] = [];
72
+ for (const [key, colRaw] of Object.entries(table)) {
73
+ const col = colRaw as AnyPgColumn;
74
+ if (col && typeof col === "object" && "primary" in col && col.primary) {
75
+ const meta = getColumnMeta(col);
76
+ const type = col.dataType === "number" || meta.columnType === "PgSerial" || meta.columnType === "PgInteger" ? "number" : "string";
77
+ const isUUID = meta.columnType === "PgUUID";
78
+ keys.push({ fieldName: key, type, isUUID });
79
+ }
80
+ }
81
+
82
+ // Default to 'id' if no primary keys are found and it exists in the schema
83
+ // This maintains backwards compatibility
84
+ if (keys.length === 0 && "id" in table) {
85
+ const idCol = table["id" as keyof typeof table] as AnyPgColumn;
86
+ const idMeta = getColumnMeta(idCol);
87
+ const type = idCol.dataType === "number" || idMeta.columnType === "PgSerial" || idMeta.columnType === "PgInteger" ? "number" : "string";
88
+ const isUUID = idMeta.columnType === "PgUUID";
89
+ keys.push({ fieldName: "id", type, isUUID });
90
+ }
91
+
92
+ return keys;
93
+ }
94
+
95
+ export function parseIdValues(idValue: string | number, primaryKeys: { fieldName: string; type: "string" | "number"; isUUID?: boolean }[]): Record<string, string | number> {
96
+ const result: Record<string, string | number> = {};
97
+
98
+ if (primaryKeys.length === 0) {
99
+ return result;
100
+ }
101
+
102
+ if (primaryKeys.length === 1) {
103
+ const pk = primaryKeys[0];
104
+ if (pk.type === "number" && !pk.isUUID) {
105
+ const parsed = typeof idValue === "number" ? idValue : parseInt(String(idValue), 10);
106
+ if (isNaN(parsed)) {
107
+ throw new Error(`Invalid numeric ID: ${idValue}`);
108
+ }
109
+ result[pk.fieldName] = parsed;
110
+ } else {
111
+ result[pk.fieldName] = String(idValue);
112
+ }
113
+ return result;
114
+ }
115
+
116
+ // Composite key - split by :::
117
+ const parts = String(idValue).split(":::");
118
+ if (parts.length !== primaryKeys.length) {
119
+ throw new Error(`Composite ID parts mismatch. Expected ${primaryKeys.length}, got ${parts.length} for ID: ${idValue}`);
120
+ }
121
+
122
+ for (let i = 0; i < primaryKeys.length; i++) {
123
+ const pk = primaryKeys[i];
124
+ const val = parts[i];
125
+ if (pk.type === "number" && !pk.isUUID) {
126
+ const parsed = parseInt(val, 10);
127
+ if (isNaN(parsed)) {
128
+ throw new Error(`Invalid numeric ID component: ${val}`);
129
+ }
130
+ result[pk.fieldName] = parsed;
131
+ } else {
132
+ result[pk.fieldName] = val;
133
+ }
134
+ }
135
+
136
+ return result;
137
+ }
138
+
139
+ export function buildCompositeId(values: Record<string, unknown>, primaryKeys: { fieldName: string; type: "string" | "number"; isUUID?: boolean }[]): string {
140
+ if (primaryKeys.length === 0) {
141
+ return "";
142
+ }
143
+ if (primaryKeys.length === 1) {
144
+ return String(values[primaryKeys[0].fieldName] ?? "");
145
+ }
146
+ return primaryKeys.map(pk => String(values[pk.fieldName] ?? "")).join(":::");
147
+ }
@@ -0,0 +1,211 @@
1
+ // import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
+ import { Entity, FilterValues } from "@rebasepro/types";
3
+ import { EntityFetchService } from "./EntityFetchService";
4
+ import { EntityPersistService } from "./EntityPersistService";
5
+ import { RelationService } from "./RelationService";
6
+ import { EntityRepository, FetchCollectionOptions, SearchOptions, CountOptions, DrizzleClient } from "../interfaces";
7
+ import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
8
+
9
+ // Re-export data transformer functions for external use
10
+ export { sanitizeAndConvertDates, serializeDataToServer, parseDataFromServer } from "../data-transformer";
11
+
12
+ // Re-export service classes for direct use
13
+ export { EntityFetchService } from "./EntityFetchService";
14
+ export { EntityPersistService } from "./EntityPersistService";
15
+ export { RelationService } from "./RelationService";
16
+
17
+ // Re-export interfaces
18
+ export * from "../interfaces";
19
+
20
+ /**
21
+ * EntityService - Facade for entity operations.
22
+ *
23
+ * This class provides a unified API for entity CRUD operations by delegating
24
+ * to specialized services:
25
+ * - EntityFetchService: Read operations (fetch, search, count)
26
+ * - EntityPersistService: Write operations (save, delete)
27
+ * - RelationService: Relation operations (fetch related, update relations)
28
+ *
29
+ * Implements the EntityRepository interface for database abstraction.
30
+ */
31
+ export class EntityService implements EntityRepository {
32
+ private fetchService: EntityFetchService;
33
+ private persistService: EntityPersistService;
34
+
35
+ constructor(private db: DrizzleClient, private registry: PostgresCollectionRegistry) {
36
+ this.fetchService = new EntityFetchService(db, registry);
37
+ this.persistService = new EntityPersistService(db, registry);
38
+ }
39
+
40
+ // =============================================================
41
+ // READ OPERATIONS - Delegated to EntityFetchService
42
+ // =============================================================
43
+
44
+ /**
45
+ * Fetch a single entity by ID
46
+ */
47
+ async fetchEntity<M extends Record<string, unknown>>(
48
+ collectionPath: string,
49
+ entityId: string | number,
50
+ databaseId?: string
51
+ ): Promise<Entity<M> | undefined> {
52
+ return this.fetchService.fetchEntity<M>(collectionPath, entityId, databaseId);
53
+ }
54
+
55
+ /**
56
+ * Fetch a collection of entities with optional filtering, ordering, and pagination
57
+ */
58
+ async fetchCollection<M extends Record<string, unknown>>(
59
+ collectionPath: string,
60
+ options: {
61
+ filter?: FilterValues<Extract<keyof M, string>>;
62
+ orderBy?: string;
63
+ order?: "desc" | "asc";
64
+ limit?: number;
65
+ offset?: number;
66
+ startAfter?: Record<string, unknown>;
67
+ searchString?: string;
68
+ databaseId?: string;
69
+ } = {}
70
+ ): Promise<Entity<M>[]> {
71
+ return this.fetchService.fetchCollection<M>(collectionPath, options);
72
+ }
73
+
74
+ /**
75
+ * Search entities by text
76
+ */
77
+ async searchEntities<M extends Record<string, unknown>>(
78
+ collectionPath: string,
79
+ searchString: string,
80
+ options: {
81
+ filter?: FilterValues<Extract<keyof M, string>>;
82
+ orderBy?: string;
83
+ order?: "desc" | "asc";
84
+ limit?: number;
85
+ databaseId?: string;
86
+ } = {}
87
+ ): Promise<Entity<M>[]> {
88
+ return this.fetchService.searchEntities<M>(collectionPath, searchString, options);
89
+ }
90
+
91
+ /**
92
+ * Count entities in a collection
93
+ */
94
+ async countEntities<M extends Record<string, unknown>>(
95
+ collectionPath: string,
96
+ options: {
97
+ filter?: FilterValues<Extract<keyof M, string>>;
98
+ searchString?: string;
99
+ databaseId?: string;
100
+ } = {}
101
+ ): Promise<number> {
102
+ return this.fetchService.countEntities<M>(collectionPath, options);
103
+ }
104
+
105
+ /**
106
+ * Check if a field value is unique in a collection
107
+ */
108
+ async checkUniqueField(
109
+ collectionPath: string,
110
+ fieldName: string,
111
+ value: unknown,
112
+ excludeEntityId?: string,
113
+ databaseId?: string
114
+ ): Promise<boolean> {
115
+ return this.fetchService.checkUniqueField(collectionPath, fieldName, value, excludeEntityId, databaseId);
116
+ }
117
+
118
+ /**
119
+ * Fetch entities related to a parent entity
120
+ */
121
+ async fetchRelatedEntities<M extends Record<string, unknown>>(
122
+ parentCollectionPath: string,
123
+ parentEntityId: string | number,
124
+ relationKey: string,
125
+ options: {
126
+ filter?: FilterValues<Extract<keyof M, string>>;
127
+ orderBy?: string;
128
+ order?: "desc" | "asc";
129
+ limit?: number;
130
+ startAfter?: Record<string, unknown>;
131
+ searchString?: string;
132
+ databaseId?: string;
133
+ } = {}
134
+ ): Promise<Entity<M>[]> {
135
+ return this.fetchService.getRelationService().fetchRelatedEntities<M>(
136
+ parentCollectionPath,
137
+ parentEntityId,
138
+ relationKey,
139
+ options
140
+ );
141
+ }
142
+
143
+ // =============================================================
144
+ // WRITE OPERATIONS - Delegated to EntityPersistService
145
+ // =============================================================
146
+
147
+ /**
148
+ * Save an entity (create or update)
149
+ */
150
+ async saveEntity<M extends Record<string, unknown>>(
151
+ collectionPath: string,
152
+ values: Partial<M>,
153
+ entityId?: string | number,
154
+ databaseId?: string
155
+ ): Promise<Entity<M>> {
156
+ return this.persistService.saveEntity<M>(collectionPath, values, entityId, databaseId);
157
+ }
158
+
159
+ /**
160
+ * Delete an entity by ID
161
+ */
162
+ async deleteEntity(
163
+ collectionPath: string,
164
+ entityId: string | number,
165
+ databaseId?: string
166
+ ): Promise<void> {
167
+ return this.persistService.deleteEntity(collectionPath, entityId, databaseId);
168
+ }
169
+
170
+
171
+ /**
172
+ * Execute raw SQL
173
+ */
174
+ async executeSql(sqlText: string): Promise<Record<string, unknown>[]> {
175
+ if (process.env.NODE_ENV !== "production") {
176
+ console.debug("Executing raw SQL:", sqlText);
177
+ }
178
+ const { sql } = await import("drizzle-orm");
179
+ const result = await this.db.execute(sql.raw(sqlText));
180
+ const rows = result.rows;
181
+ if (process.env.NODE_ENV !== "production") {
182
+ console.debug(`SQL executed successfully. Returned ${Array.isArray(rows) ? rows.length : "non-array"} rows.`);
183
+ }
184
+ return rows as Record<string, unknown>[];
185
+ }
186
+
187
+ // =============================================================
188
+ // SERVICE ACCESSORS
189
+ // =============================================================
190
+
191
+ /**
192
+ * Get the underlying EntityFetchService for advanced use
193
+ */
194
+ getFetchService(): EntityFetchService {
195
+ return this.fetchService;
196
+ }
197
+
198
+ /**
199
+ * Get the underlying EntityPersistService for advanced use
200
+ */
201
+ getPersistService(): EntityPersistService {
202
+ return this.persistService;
203
+ }
204
+
205
+ /**
206
+ * Get the underlying RelationService for advanced use
207
+ */
208
+ getRelationService(): RelationService {
209
+ return this.fetchService.getRelationService();
210
+ }
211
+ }
@@ -0,0 +1,13 @@
1
+ // Re-export all service classes
2
+ export { EntityFetchService } from "./EntityFetchService";
3
+ export { EntityPersistService } from "./EntityPersistService";
4
+ export { RelationService } from "./RelationService";
5
+
6
+ // Re-export helper functions
7
+ export {
8
+ getCollectionByPath,
9
+ getTableForCollection,
10
+ getPrimaryKeys,
11
+ parseIdValues,
12
+ buildCompositeId
13
+ } from "./entity-helpers";