@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.
- package/LICENSE +6 -0
- package/README.md +106 -0
- package/build-errors.txt +37 -0
- package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/data/buildRebaseData.d.ts +14 -0
- package/dist/common/src/index.d.ts +3 -0
- package/dist/common/src/util/builders.d.ts +57 -0
- package/dist/common/src/util/callbacks.d.ts +6 -0
- package/dist/common/src/util/collections.d.ts +11 -0
- package/dist/common/src/util/common.d.ts +2 -0
- package/dist/common/src/util/conditions.d.ts +26 -0
- package/dist/common/src/util/entities.d.ts +58 -0
- package/dist/common/src/util/enums.d.ts +3 -0
- package/dist/common/src/util/index.d.ts +16 -0
- package/dist/common/src/util/navigation_from_path.d.ts +34 -0
- package/dist/common/src/util/navigation_utils.d.ts +20 -0
- package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
- package/dist/common/src/util/paths.d.ts +14 -0
- package/dist/common/src/util/permissions.d.ts +5 -0
- package/dist/common/src/util/references.d.ts +2 -0
- package/dist/common/src/util/relations.d.ts +22 -0
- package/dist/common/src/util/resolutions.d.ts +72 -0
- package/dist/common/src/util/storage.d.ts +24 -0
- package/dist/index.es.js +11298 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +11306 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +100 -0
- package/dist/server-postgresql/src/PostgresBootstrapper.d.ts +40 -0
- package/dist/server-postgresql/src/auth/ensure-tables.d.ts +6 -0
- package/dist/server-postgresql/src/auth/services.d.ts +192 -0
- package/dist/server-postgresql/src/cli.d.ts +1 -0
- package/dist/server-postgresql/src/collections/PostgresCollectionRegistry.d.ts +43 -0
- package/dist/server-postgresql/src/connection.d.ts +40 -0
- package/dist/server-postgresql/src/data-transformer.d.ts +58 -0
- package/dist/server-postgresql/src/databasePoolManager.d.ts +20 -0
- package/dist/server-postgresql/src/history/HistoryService.d.ts +71 -0
- package/dist/server-postgresql/src/history/ensure-history-table.d.ts +7 -0
- package/dist/server-postgresql/src/index.d.ts +13 -0
- package/dist/server-postgresql/src/interfaces.d.ts +18 -0
- package/dist/server-postgresql/src/schema/auth-schema.d.ts +868 -0
- package/dist/server-postgresql/src/schema/doctor-cli.d.ts +2 -0
- package/dist/server-postgresql/src/schema/doctor.d.ts +43 -0
- package/dist/server-postgresql/src/schema/generate-drizzle-schema-logic.d.ts +2 -0
- package/dist/server-postgresql/src/schema/generate-drizzle-schema.d.ts +1 -0
- package/dist/server-postgresql/src/schema/introspect-db-logic.d.ts +82 -0
- package/dist/server-postgresql/src/schema/introspect-db.d.ts +1 -0
- package/dist/server-postgresql/src/schema/test-schema.d.ts +24 -0
- package/dist/server-postgresql/src/services/BranchService.d.ts +47 -0
- package/dist/server-postgresql/src/services/EntityFetchService.d.ts +209 -0
- package/dist/server-postgresql/src/services/EntityPersistService.d.ts +41 -0
- package/dist/server-postgresql/src/services/RelationService.d.ts +98 -0
- package/dist/server-postgresql/src/services/entity-helpers.d.ts +38 -0
- package/dist/server-postgresql/src/services/entityService.d.ts +104 -0
- package/dist/server-postgresql/src/services/index.d.ts +4 -0
- package/dist/server-postgresql/src/services/realtimeService.d.ts +188 -0
- package/dist/server-postgresql/src/utils/drizzle-conditions.d.ts +116 -0
- package/dist/server-postgresql/src/websocket.d.ts +5 -0
- package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
- package/dist/types/src/controllers/auth.d.ts +119 -0
- package/dist/types/src/controllers/client.d.ts +170 -0
- package/dist/types/src/controllers/collection_registry.d.ts +45 -0
- package/dist/types/src/controllers/customization_controller.d.ts +60 -0
- package/dist/types/src/controllers/data.d.ts +168 -0
- package/dist/types/src/controllers/data_driver.d.ts +160 -0
- package/dist/types/src/controllers/database_admin.d.ts +11 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
- package/dist/types/src/controllers/effective_role.d.ts +4 -0
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +18 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
- package/dist/types/src/controllers/navigation.d.ts +213 -0
- package/dist/types/src/controllers/registry.d.ts +54 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
- package/dist/types/src/controllers/snackbar.d.ts +24 -0
- package/dist/types/src/controllers/storage.d.ts +171 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/rebase_context.d.ts +105 -0
- package/dist/types/src/types/backend.d.ts +536 -0
- package/dist/types/src/types/builders.d.ts +15 -0
- package/dist/types/src/types/chips.d.ts +5 -0
- package/dist/types/src/types/collections.d.ts +856 -0
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +64 -0
- package/dist/types/src/types/entities.d.ts +145 -0
- package/dist/types/src/types/entity_actions.d.ts +98 -0
- package/dist/types/src/types/entity_callbacks.d.ts +173 -0
- package/dist/types/src/types/entity_link_builder.d.ts +7 -0
- package/dist/types/src/types/entity_overrides.d.ts +10 -0
- package/dist/types/src/types/entity_views.d.ts +61 -0
- package/dist/types/src/types/export_import.d.ts +21 -0
- package/dist/types/src/types/index.d.ts +23 -0
- package/dist/types/src/types/locales.d.ts +4 -0
- package/dist/types/src/types/modify_collections.d.ts +5 -0
- package/dist/types/src/types/plugins.d.ts +279 -0
- package/dist/types/src/types/properties.d.ts +1176 -0
- package/dist/types/src/types/property_config.d.ts +70 -0
- package/dist/types/src/types/relations.d.ts +336 -0
- package/dist/types/src/types/slots.d.ts +252 -0
- package/dist/types/src/types/translations.d.ts +870 -0
- package/dist/types/src/types/user_management_delegate.d.ts +121 -0
- package/dist/types/src/types/websockets.d.ts +78 -0
- package/dist/types/src/users/index.d.ts +2 -0
- package/dist/types/src/users/roles.d.ts +22 -0
- package/dist/types/src/users/user.d.ts +46 -0
- package/drizzle-test/0000_woozy_junta.sql +6 -0
- package/drizzle-test/0001_youthful_arachne.sql +1 -0
- package/drizzle-test/0002_lively_dragon_lord.sql +2 -0
- package/drizzle-test/0003_mean_king_cobra.sql +2 -0
- package/drizzle-test/meta/0000_snapshot.json +47 -0
- package/drizzle-test/meta/0001_snapshot.json +48 -0
- package/drizzle-test/meta/0002_snapshot.json +38 -0
- package/drizzle-test/meta/0003_snapshot.json +48 -0
- package/drizzle-test/meta/_journal.json +34 -0
- package/drizzle-test-out/0000_tan_trauma.sql +6 -0
- package/drizzle-test-out/0001_rapid_drax.sql +1 -0
- package/drizzle-test-out/meta/0000_snapshot.json +44 -0
- package/drizzle-test-out/meta/0001_snapshot.json +54 -0
- package/drizzle-test-out/meta/_journal.json +20 -0
- package/drizzle.test.config.ts +10 -0
- package/jest-all.log +3128 -0
- package/jest.log +49 -0
- package/package.json +92 -0
- package/scratch.ts +41 -0
- package/src/PostgresBackendDriver.ts +1008 -0
- package/src/PostgresBootstrapper.ts +231 -0
- package/src/auth/ensure-tables.ts +381 -0
- package/src/auth/services.ts +799 -0
- package/src/cli.ts +648 -0
- package/src/collections/PostgresCollectionRegistry.ts +96 -0
- package/src/connection.ts +84 -0
- package/src/data-transformer.ts +608 -0
- package/src/databasePoolManager.ts +85 -0
- package/src/history/HistoryService.ts +248 -0
- package/src/history/ensure-history-table.ts +45 -0
- package/src/index.ts +13 -0
- package/src/interfaces.ts +60 -0
- package/src/schema/auth-schema.ts +169 -0
- package/src/schema/doctor-cli.ts +47 -0
- package/src/schema/doctor.ts +595 -0
- package/src/schema/generate-drizzle-schema-logic.ts +765 -0
- package/src/schema/generate-drizzle-schema.ts +151 -0
- package/src/schema/introspect-db-logic.ts +542 -0
- package/src/schema/introspect-db.ts +211 -0
- package/src/schema/test-schema.ts +11 -0
- package/src/services/BranchService.ts +237 -0
- package/src/services/EntityFetchService.ts +1576 -0
- package/src/services/EntityPersistService.ts +349 -0
- package/src/services/RelationService.ts +1274 -0
- package/src/services/entity-helpers.ts +147 -0
- package/src/services/entityService.ts +211 -0
- package/src/services/index.ts +13 -0
- package/src/services/realtimeService.ts +1034 -0
- package/src/utils/drizzle-conditions.ts +1000 -0
- package/src/websocket.ts +518 -0
- package/test/auth-services.test.ts +661 -0
- package/test/batch-many-to-many-regression.test.ts +573 -0
- package/test/branchService.test.ts +367 -0
- package/test/data-transformer-hardening.test.ts +417 -0
- package/test/data-transformer.test.ts +175 -0
- package/test/doctor.test.ts +182 -0
- package/test/drizzle-conditions.test.ts +895 -0
- package/test/entityService.errors.test.ts +367 -0
- package/test/entityService.relations.test.ts +1008 -0
- package/test/entityService.subcollection-search.test.ts +566 -0
- package/test/entityService.test.ts +1035 -0
- package/test/generate-drizzle-schema.test.ts +988 -0
- package/test/historyService.test.ts +141 -0
- package/test/introspect-db-generation.test.ts +436 -0
- package/test/introspect-db-utils.test.ts +389 -0
- package/test/n-plus-one-regression.test.ts +314 -0
- package/test/postgresDataDriver.test.ts +648 -0
- package/test/realtimeService.test.ts +307 -0
- package/test/relation-pipeline-gaps.test.ts +637 -0
- package/test/relations.test.ts +1115 -0
- package/test/unmapped-tables-safety.test.ts +345 -0
- package/test-drizzle-bug.ts +18 -0
- package/test-drizzle-out/0000_cultured_freak.sql +7 -0
- package/test-drizzle-out/0001_tiresome_professor_monster.sql +1 -0
- package/test-drizzle-out/meta/0000_snapshot.json +55 -0
- package/test-drizzle-out/meta/0001_snapshot.json +63 -0
- package/test-drizzle-out/meta/_journal.json +20 -0
- package/test-drizzle-prompt.sh +2 -0
- package/test-policy-prompt.sh +3 -0
- package/test-programmatic.ts +30 -0
- package/test-programmatic2.ts +59 -0
- package/test-schema-no-policies.ts +12 -0
- package/test_drizzle_mock.js +3 -0
- package/test_find_changed.mjs +32 -0
- package/test_hash.js +14 -0
- package/test_output.txt +3145 -0
- package/tsconfig.json +49 -0
- package/tsconfig.prod.json +20 -0
- 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";
|