@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,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,43 @@
1
+ import { EntityCollection, Property } from "@rebasepro/types";
2
+ export type IssueSeverity = "error" | "warning" | "info";
3
+ export interface DoctorIssue {
4
+ severity: IssueSeverity;
5
+ category: "missing_table" | "missing_column" | "type_mismatch" | "missing_constraint" | "schema_stale" | "missing_enum" | "enum_value_mismatch" | "missing_foreign_key";
6
+ table?: string;
7
+ column?: string;
8
+ expected?: string;
9
+ actual?: string;
10
+ message: string;
11
+ fix: string;
12
+ }
13
+ export interface DoctorReport {
14
+ collectionsToSchema: {
15
+ passed: boolean;
16
+ issues: DoctorIssue[];
17
+ };
18
+ schemaToDatabase: {
19
+ passed: boolean;
20
+ issues: DoctorIssue[];
21
+ };
22
+ summary: {
23
+ passed: number;
24
+ warnings: number;
25
+ errors: number;
26
+ };
27
+ }
28
+ export declare function getExpectedColumnType(prop: Property): string | null;
29
+ export declare function loadCollections(collectionsPath: string): Promise<EntityCollection[]>;
30
+ export declare function checkCollectionsVsSchema(collections: EntityCollection[], schemaFilePath: string): Promise<{
31
+ passed: boolean;
32
+ issues: DoctorIssue[];
33
+ }>;
34
+ export declare function checkCollectionsVsDatabase(collections: EntityCollection[], databaseUrl: string): Promise<{
35
+ passed: boolean;
36
+ issues: DoctorIssue[];
37
+ }>;
38
+ export declare function renderReport(report: DoctorReport): void;
39
+ export declare function runDoctor(options: {
40
+ collectionsPath: string;
41
+ schemaPath: string;
42
+ databaseUrl?: string;
43
+ }): Promise<DoctorReport>;
@@ -0,0 +1,2 @@
1
+ import { EntityCollection } from "@rebasepro/types";
2
+ export declare const generateSchema: (collections: EntityCollection[], stripPolicies?: boolean) => Promise<string>;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Introspection logic — pure functions and the pipeline that transforms
3
+ * raw PostgreSQL metadata into Rebase collection definition files.
4
+ *
5
+ * This module contains NO side-effects: no fs writes, no pg.Client creation,
6
+ * no process.exit. It is imported by introspect-db.ts (the CLI entry-point)
7
+ * and consumed directly by tests.
8
+ */
9
+ export interface TableRow {
10
+ table_name: string;
11
+ }
12
+ export interface TableColumn {
13
+ table_name: string;
14
+ column_name: string;
15
+ data_type: string;
16
+ udt_name: string;
17
+ is_nullable: string;
18
+ column_default: string | null;
19
+ }
20
+ export interface EnumValue {
21
+ enum_name: string;
22
+ enum_value: string;
23
+ sort_order: number;
24
+ }
25
+ export interface PrimaryKeyRow {
26
+ table_name: string;
27
+ column_name: string;
28
+ }
29
+ export interface ForeignKeyRow {
30
+ table_name: string;
31
+ column_name: string;
32
+ foreign_table_name: string;
33
+ foreign_column_name: string;
34
+ }
35
+ export interface TableMeta {
36
+ name: string;
37
+ columns: TableColumn[];
38
+ pks: string[];
39
+ fks: ForeignKeyRow[];
40
+ }
41
+ export declare function singularize(word: string): string;
42
+ /**
43
+ * Convert a snake_case name to a human-readable Title Case label.
44
+ * e.g. "created_at" -> "Created At", "customer_id" -> "Customer Id"
45
+ */
46
+ export declare function humanize(snakeName: string): string;
47
+ /**
48
+ * Convert a snake_case table name to a camelCase + "Collection" variable name.
49
+ * e.g. "company_token" -> "companyTokenCollection"
50
+ */
51
+ export declare function toCollectionVarName(tableName: string): string;
52
+ export declare function getIconForTable(tableName: string): string;
53
+ /**
54
+ * Map a PostgreSQL data type to a Rebase property type.
55
+ */
56
+ export declare function mapPgType(dataType: string): string;
57
+ export declare function buildEnumMap(enumValues: EnumValue[]): Map<string, string[]>;
58
+ export declare function buildTablesMap(tables: TableRow[], columns: TableColumn[], pks: PrimaryKeyRow[], fks: ForeignKeyRow[]): Map<string, TableMeta>;
59
+ export declare function identifyJoinTables(tablesMap: Map<string, TableMeta>): Set<string>;
60
+ export interface GeneratedFile {
61
+ tableName: string;
62
+ fileName: string;
63
+ content: string;
64
+ }
65
+ /**
66
+ * Generate the full TypeScript file content for a single collection.
67
+ * Pure function — no I/O.
68
+ */
69
+ export declare function generateCollectionFile(tableName: string, meta: TableMeta, allFks: ForeignKeyRow[], joinTables: Set<string>, tablesMap: Map<string, TableMeta>, enumMap: Map<string, string[]>): string;
70
+ /**
71
+ * Generate the content for an index.ts file that re-exports all collections.
72
+ */
73
+ export declare function generateIndexContent(fileNames: string[]): string;
74
+ /**
75
+ * Merge new exports into existing index.ts content.
76
+ * Returns the merged content string.
77
+ */
78
+ export declare function mergeIndexContent(existingContent: string, newFileNames: string[]): string;
79
+ /**
80
+ * Safely extract the host portion of a database URL for logging.
81
+ */
82
+ export declare function safeHostFromUrl(url: string): string;
@@ -0,0 +1,24 @@
1
+ export declare const testTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "test";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "id";
7
+ tableName: "test";
8
+ dataType: "string";
9
+ columnType: "PgText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ };
23
+ dialect: "pg";
24
+ }>;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * BranchService
3
+ *
4
+ * Manages database branching by creating/deleting PostgreSQL databases
5
+ * using `CREATE DATABASE ... TEMPLATE`. Branch metadata is stored in the
6
+ * `rebase.branches` table in the default (main) database, following the
7
+ * same `rebase` schema convention used by entity_history, auth, etc.
8
+ */
9
+ import { BranchInfo } from "@rebasepro/types";
10
+ import { DrizzleClient } from "../interfaces";
11
+ import { DatabasePoolManager } from "../databasePoolManager";
12
+ export declare class BranchService {
13
+ private db;
14
+ private poolManager;
15
+ constructor(db: DrizzleClient, poolManager: DatabasePoolManager);
16
+ /**
17
+ * Ensure the `rebase.branches` metadata table exists in the default database.
18
+ * Idempotent — safe to call on every startup.
19
+ */
20
+ ensureBranchMetadataTable(): Promise<void>;
21
+ /**
22
+ * Create a new branch database by templating the source database.
23
+ *
24
+ * Uses `CREATE DATABASE ... TEMPLATE` for an instant, full-fidelity copy
25
+ * of both schema and data.
26
+ *
27
+ * @param name User-facing branch name (e.g., "feature_auth")
28
+ * @param options.source Source database to clone; defaults to the main database.
29
+ */
30
+ createBranch(name: string, options?: {
31
+ source?: string;
32
+ }): Promise<BranchInfo>;
33
+ /**
34
+ * Delete a branch database and remove its metadata.
35
+ * Cannot delete the main/default database.
36
+ */
37
+ deleteBranch(name: string): Promise<void>;
38
+ /**
39
+ * List all branches recorded in the metadata table.
40
+ * Optionally fetches database sizes from pg_database.
41
+ */
42
+ listBranches(): Promise<BranchInfo[]>;
43
+ /**
44
+ * Get info about a specific branch.
45
+ */
46
+ getBranchInfo(name: string): Promise<BranchInfo | undefined>;
47
+ }
@@ -0,0 +1,209 @@
1
+ import { SQL } from "drizzle-orm";
2
+ import { PgTable } from "drizzle-orm/pg-core";
3
+ import { Entity, FilterValues } from "@rebasepro/types";
4
+ import { RelationService } from "./RelationService";
5
+ import { DrizzleClient } from "../interfaces";
6
+ import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
7
+ /**
8
+ * Service for handling all entity read operations.
9
+ * Handles fetching, searching, counting, and filtering entities.
10
+ */
11
+ export declare class EntityFetchService {
12
+ private db;
13
+ private registry;
14
+ private relationService;
15
+ constructor(db: DrizzleClient, registry: PostgresCollectionRegistry);
16
+ /**
17
+ * Get the relational query builder for a given table name.
18
+ * Safely narrows the DrizzleClient union type to access db.query[tableName].
19
+ */
20
+ private getQueryBuilder;
21
+ /**
22
+ * Build filter conditions from FilterValues
23
+ * Delegates to DrizzleConditionBuilder.buildFilterConditions
24
+ */
25
+ buildFilterConditions<M extends Record<string, unknown>>(filter: FilterValues<Extract<keyof M, string>>, table: PgTable<any>, collectionPath: string): SQL[];
26
+ /**
27
+ * Resolves the correct Drizzle column for sorting.
28
+ * Automatically maps owning relation property keys to their underlying foreign key column.
29
+ */
30
+ private resolveOrderByField;
31
+ /**
32
+ * Build the `with` config for Drizzle's relational query API.
33
+ * Converts collection relations to a Drizzle-compatible `with` object.
34
+ *
35
+ * When `include` is provided, only those relations are loaded.
36
+ * When `include` is absent, ALL relations are loaded (CMS path).
37
+ *
38
+ * Automatically detects many-to-many junction tables and nests
39
+ * the target relation so actual entity data is returned.
40
+ */
41
+ private buildWithConfig;
42
+ /**
43
+ * Detect if a many-to-many relation uses a junction table in the Drizzle schema.
44
+ */
45
+ private isJunctionRelation;
46
+ /**
47
+ * Get the Drizzle relation name on the junction table that points to the actual target entity.
48
+ * For example, for posts_tags junction, this returns "tag_id" (the relation pointing to tags).
49
+ */
50
+ private getJunctionTargetRelationName;
51
+ /**
52
+ * Convert a db.query result row (with nested relation objects) to an Entity<M>.
53
+ * Handles:
54
+ * - The { id, path, values } wrapping
55
+ * - Type normalization (dates, numbers, NaN) via normalizeDbValues
56
+ * - Converting nested relation objects to { id, path, __type: "relation" } for CMS
57
+ * - Flattening junction-table many-to-many results
58
+ */
59
+ private drizzleResultToEntity;
60
+ /**
61
+ * Post-fetch joinPath relations for a single entity.
62
+ * joinPath relations cannot be expressed via Drizzle's `with` config,
63
+ * so they must be loaded separately after the primary query.
64
+ */
65
+ private resolveJoinPathRelations;
66
+ /**
67
+ * Post-fetch joinPath relations for a batch of entities.
68
+ * Uses batch fetching to avoid N+1 queries for list views.
69
+ */
70
+ private resolveJoinPathRelationsBatch;
71
+ /**
72
+ * Resolves joinPath relations for raw REST rows and directly injects them.
73
+ * Uses RelationService to query the database and maps results back to the flattened objects.
74
+ */
75
+ private resolveJoinPathRelationsBatchRest;
76
+ /**
77
+ * Convert a db.query result row to a flat REST-style object with populated relations.
78
+ */
79
+ private drizzleResultToRestRow;
80
+ /**
81
+ * Build db.query-compatible options from standard fetch options.
82
+ * Handles filter, search, orderBy, limit, and cursor-based pagination.
83
+ */
84
+ private buildDrizzleQueryOptions;
85
+ /**
86
+ * Extract cursor pagination conditions from startAfter options.
87
+ */
88
+ private buildCursorConditions;
89
+ /**
90
+ * Fetch a single entity by ID
91
+ */
92
+ fetchEntity<M extends Record<string, unknown>>(collectionPath: string, entityId: string | number, databaseId?: string): Promise<Entity<M> | undefined>;
93
+ /**
94
+ * Unified method to fetch entities with optional search functionality
95
+ */
96
+ fetchEntitiesWithConditions<M extends Record<string, unknown>>(collectionPath: string, options?: {
97
+ filter?: FilterValues<Extract<keyof M, string>>;
98
+ orderBy?: string;
99
+ order?: "desc" | "asc";
100
+ limit?: number;
101
+ offset?: number;
102
+ startAfter?: Record<string, unknown>;
103
+ searchString?: string;
104
+ databaseId?: string;
105
+ }): Promise<Entity<M>[]>;
106
+ /**
107
+ * Fallback path used when db.query is unavailable.
108
+ * The primary path uses drizzleResultToEntity which handles relation
109
+ * mapping without N+1 queries.
110
+ *
111
+ * Process raw database results into Entity objects with relations.
112
+ */
113
+ private processEntityResults;
114
+ /**
115
+ * Fetch a collection of entities
116
+ */
117
+ fetchCollection<M extends Record<string, unknown>>(collectionPath: string, options?: {
118
+ filter?: FilterValues<Extract<keyof M, string>>;
119
+ orderBy?: string;
120
+ order?: "desc" | "asc";
121
+ limit?: number;
122
+ offset?: number;
123
+ startAfter?: Record<string, unknown>;
124
+ searchString?: string;
125
+ databaseId?: string;
126
+ }): Promise<Entity<M>[]>;
127
+ /**
128
+ * Search entities by text
129
+ */
130
+ searchEntities<M extends Record<string, unknown>>(collectionPath: string, searchString: string, options?: {
131
+ filter?: FilterValues<Extract<keyof M, string>>;
132
+ orderBy?: string;
133
+ order?: "desc" | "asc";
134
+ limit?: number;
135
+ databaseId?: string;
136
+ }): Promise<Entity<M>[]>;
137
+ /**
138
+ * Fetch collection from multi-segment path
139
+ */
140
+ private fetchCollectionFromPath;
141
+ /**
142
+ * Count entities in a collection
143
+ */
144
+ countEntities<M extends Record<string, unknown>>(collectionPath: string, options?: {
145
+ filter?: FilterValues<Extract<keyof M, string>>;
146
+ searchString?: string;
147
+ databaseId?: string;
148
+ }): Promise<number>;
149
+ /**
150
+ * Count entities from multi-segment path
151
+ */
152
+ private countEntitiesFromPath;
153
+ /**
154
+ * Check if a field value is unique
155
+ */
156
+ checkUniqueField(collectionPath: string, fieldName: string, value: unknown, excludeEntityId?: string, _databaseId?: string): Promise<boolean>;
157
+ /**
158
+ * Get the RelationService instance for external use
159
+ */
160
+ getRelationService(): RelationService;
161
+ /**
162
+ * Fetch a collection of entities with optional relation includes.
163
+ * When `include` is provided, only the specified relations are populated
164
+ * with full entity data (not just { id, path, __type }).
165
+ * When `include` is absent, no relation queries are made (fast path).
166
+ *
167
+ * @param include - Array of relation keys to populate, or ["*"] for all
168
+ */
169
+ fetchCollectionForRest<M extends Record<string, unknown>>(collectionPath: string, options?: {
170
+ filter?: FilterValues<Extract<keyof M, string>>;
171
+ orderBy?: string;
172
+ order?: "desc" | "asc";
173
+ limit?: number;
174
+ offset?: number;
175
+ startAfter?: Record<string, unknown>;
176
+ searchString?: string;
177
+ databaseId?: string;
178
+ }, include?: string[]): Promise<Record<string, unknown>[]>;
179
+ /**
180
+ * Fetch a single entity with optional relation includes for REST API.
181
+ */
182
+ fetchEntityForRest<M extends Record<string, unknown>>(collectionPath: string, entityId: string | number, include?: string[], databaseId?: string): Promise<Record<string, unknown> | null>;
183
+ /**
184
+ * Fetch raw rows without any relation processing (for REST fast path)
185
+ */
186
+ private fetchEntitiesWithConditionsRaw;
187
+ /**
188
+ * Check if the Drizzle instance has the relational query API available
189
+ * for a given collection path.
190
+ * Note: Primary path now uses inline `getQueryBuilder()` checks.
191
+ */
192
+ private hasDrizzleQueryAPI;
193
+ /**
194
+ * Attempt to use Drizzle's relational query API (db.query.<table>.findMany)
195
+ * for efficient JOIN-based relation loading.
196
+ * Returns null if the API is not available or the query fails.
197
+ * Note: Primary path now uses `buildWithConfig` + `buildDrizzleQueryOptions`.
198
+ */
199
+ private fetchWithDrizzleQuery;
200
+ /**
201
+ * Fallback path used when db.query is unavailable.
202
+ * The primary path uses db.query.findMany with `with` config, which
203
+ * loads all relations in a single query.
204
+ *
205
+ * Batch fetch many-to-many related entities for multiple parent IDs.
206
+ * Groups results by parent ID to avoid N+1.
207
+ */
208
+ private batchFetchManyRelatedEntities;
209
+ }
@@ -0,0 +1,41 @@
1
+ import { Entity } from "@rebasepro/types";
2
+ import { RelationService } from "./RelationService";
3
+ import { EntityFetchService } from "./EntityFetchService";
4
+ import { DrizzleClient } from "../interfaces";
5
+ import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
6
+ /**
7
+ * Service for handling all entity write operations.
8
+ * Handles saving, deleting, and updating entities.
9
+ */
10
+ export declare class EntityPersistService {
11
+ private db;
12
+ private registry;
13
+ private relationService;
14
+ private fetchService;
15
+ constructor(db: DrizzleClient, registry: PostgresCollectionRegistry);
16
+ /**
17
+ * Delete an entity by ID
18
+ */
19
+ deleteEntity(collectionPath: string, entityId: string | number, _databaseId?: string): Promise<void>;
20
+ /**
21
+ * Save an entity (create or update)
22
+ */
23
+ saveEntity<M extends Record<string, unknown>>(collectionPath: string, values: Partial<M>, entityId?: string | number, databaseId?: string): Promise<Entity<M>>;
24
+ /**
25
+ * Get the RelationService instance for external use
26
+ */
27
+ getRelationService(): RelationService;
28
+ /**
29
+ * Get the FetchService instance for external use
30
+ */
31
+ getFetchService(): EntityFetchService;
32
+ /**
33
+ * Translate raw PostgreSQL / Drizzle errors into user-friendly messages.
34
+ */
35
+ private toUserFriendlyError;
36
+ /**
37
+ * Extract the underlying PostgreSQL error from a Drizzle wrapper.
38
+ * Drizzle wraps PG errors in a `cause` property.
39
+ */
40
+ private extractPgError;
41
+ }
@@ -0,0 +1,98 @@
1
+ import { DrizzleClient } from "../interfaces";
2
+ import { Entity, EntityCollection, FilterValues, Relation } from "@rebasepro/types";
3
+ import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
4
+ /**
5
+ * Service for handling all relation-related operations.
6
+ * Handles fetching, updating, and managing entity relations.
7
+ */
8
+ export declare class RelationService {
9
+ private db;
10
+ private registry;
11
+ constructor(db: DrizzleClient, registry: PostgresCollectionRegistry);
12
+ /**
13
+ * Fetch entities related to a parent entity through a specific relation
14
+ */
15
+ fetchRelatedEntities<M extends Record<string, unknown>>(parentCollectionPath: string, parentEntityId: string | number, relationKey: string, options?: {
16
+ filter?: FilterValues<Extract<keyof M, string>>;
17
+ orderBy?: string;
18
+ order?: "desc" | "asc";
19
+ limit?: number;
20
+ startAfter?: Record<string, unknown>;
21
+ searchString?: string;
22
+ databaseId?: string;
23
+ }): Promise<Entity<M>[]>;
24
+ /**
25
+ * Fetch entities using join paths for complex relations
26
+ */
27
+ fetchEntitiesUsingJoins<M extends Record<string, unknown>>(parentCollection: EntityCollection, parentEntityId: string | number, relation: Relation, options?: {
28
+ filter?: FilterValues<Extract<keyof M, string>>;
29
+ orderBy?: string;
30
+ order?: "desc" | "asc";
31
+ limit?: number;
32
+ startAfter?: Record<string, unknown>;
33
+ searchString?: string;
34
+ databaseId?: string;
35
+ }): Promise<Entity<M>[]>;
36
+ /**
37
+ * Count related entities for a parent entity
38
+ */
39
+ countRelatedEntities<M extends Record<string, unknown>>(parentCollectionPath: string, parentEntityId: string | number, relationKey: string, options?: {
40
+ filter?: FilterValues<Extract<keyof M, string>>;
41
+ databaseId?: string;
42
+ }): Promise<number>;
43
+ /**
44
+ * Batch fetch related entities for multiple parent entities to avoid N+1 queries
45
+ */
46
+ batchFetchRelatedEntities(parentCollectionPath: string, parentEntityIds: (string | number)[], _relationKey: string, relation: Relation): Promise<Map<string, Entity<Record<string, unknown>>>>;
47
+ /**
48
+ * Batch fetch many-cardinality related entities for multiple parent entities.
49
+ * Returns a Map<parentId, Entity[]> instead of Map<parentId, Entity>.
50
+ * Uses a single SQL query with IN clause to avoid N+1.
51
+ */
52
+ batchFetchRelatedEntitiesMany(parentCollectionPath: string, parentEntityIds: (string | number)[], _relationKey: string, relation: Relation): Promise<Map<string, Entity<Record<string, unknown>>[]>>;
53
+ /**
54
+ * Update many-to-many and junction relations
55
+ */
56
+ updateRelationsUsingJoins<M extends Record<string, unknown>>(tx: DrizzleClient, collection: EntityCollection, entityId: string | number, relationValues: Partial<M>): Promise<void>;
57
+ /**
58
+ * Update inverse relations (where FK is on the target table)
59
+ */
60
+ updateInverseRelations(tx: DrizzleClient, sourceCollection: EntityCollection, sourceEntityId: string | number, inverseRelationUpdates: Array<{
61
+ relationKey: string;
62
+ relation: Relation;
63
+ newValue: unknown;
64
+ currentEntityId?: string | number;
65
+ }>): Promise<void>;
66
+ /**
67
+ * Handle inverse relations with joinPath
68
+ */
69
+ private updateInverseJoinPathRelation;
70
+ /**
71
+ * Handle many-to-many inverse relation updates using junction tables
72
+ */
73
+ private updateManyToManyInverseRelation;
74
+ /**
75
+ * Update one-to-one relations that use joinPath
76
+ */
77
+ updateJoinPathOneToOneRelations(tx: DrizzleClient, parentCollection: EntityCollection, parentEntityId: string | number, updates: Array<{
78
+ relationKey: string;
79
+ relation: Relation;
80
+ newTargetId: string | number | null;
81
+ }>): Promise<void>;
82
+ /**
83
+ * Resolve joinPath write mapping for one-to-one relations
84
+ */
85
+ resolveJoinPathWriteMapping(parentCollection: EntityCollection, relation: Relation): {
86
+ targetFKColName: string;
87
+ parentSourceColName: string;
88
+ };
89
+ /**
90
+ * Handle junction table creation for many-to-many path-based saves
91
+ */
92
+ handleJunctionTableCreation(tx: DrizzleClient, newEntityId: string | number, junctionTableInfo: {
93
+ parentCollection: EntityCollection;
94
+ parentId: string | number;
95
+ relation: Relation;
96
+ relationKey: string;
97
+ }): Promise<void>;
98
+ }
@@ -0,0 +1,38 @@
1
+ import { PgTable, AnyPgColumn } from "drizzle-orm/pg-core";
2
+ import { EntityCollection } from "@rebasepro/types";
3
+ import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
4
+ /**
5
+ * Shared helper functions for entity operations.
6
+ * These are used by EntityFetchService, EntityPersistService, and RelationService.
7
+ *
8
+ * All functions that need collection/table lookups require an explicit
9
+ * `PostgresCollectionRegistry` instance — there is no global singleton.
10
+ */
11
+ /**
12
+ * Interface for Drizzle column metadata introspection.
13
+ * Replaces unsafe `as unknown as Record<string, unknown>` double-cast chains.
14
+ */
15
+ export interface DrizzleColumnMeta {
16
+ columnType?: string;
17
+ dataType?: string;
18
+ primary?: boolean;
19
+ }
20
+ /** Safely extract Drizzle column metadata from a column object. */
21
+ export declare function getColumnMeta(col: AnyPgColumn): DrizzleColumnMeta;
22
+ export declare function getCollectionByPath(collectionPath: string, registry: PostgresCollectionRegistry): EntityCollection;
23
+ export declare function getTableForCollection(collection: EntityCollection, registry: PostgresCollectionRegistry): PgTable<any>;
24
+ export declare function getPrimaryKeys(collection: EntityCollection, registry: PostgresCollectionRegistry): {
25
+ fieldName: string;
26
+ type: "string" | "number";
27
+ isUUID?: boolean;
28
+ }[];
29
+ export declare function parseIdValues(idValue: string | number, primaryKeys: {
30
+ fieldName: string;
31
+ type: "string" | "number";
32
+ isUUID?: boolean;
33
+ }[]): Record<string, string | number>;
34
+ export declare function buildCompositeId(values: Record<string, unknown>, primaryKeys: {
35
+ fieldName: string;
36
+ type: "string" | "number";
37
+ isUUID?: boolean;
38
+ }[]): string;