@rebasepro/server-postgresql 0.7.0 → 0.8.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.
- package/build-errors.txt +37 -0
- package/dist/PostgresAdapter.d.ts +6 -0
- package/dist/PostgresBackendDriver.d.ts +118 -0
- package/dist/PostgresBootstrapper.d.ts +46 -0
- package/dist/auth/ensure-tables.d.ts +10 -0
- package/dist/auth/services.d.ts +251 -0
- package/dist/cli-errors.d.ts +29 -0
- package/dist/cli-helpers.d.ts +7 -0
- package/dist/cli.d.ts +1 -0
- package/dist/collections/PostgresCollectionRegistry.d.ts +47 -0
- package/dist/connection.d.ts +65 -0
- package/dist/data-transformer.d.ts +55 -0
- package/dist/databasePoolManager.d.ts +20 -0
- package/dist/history/HistoryService.d.ts +71 -0
- package/dist/history/ensure-history-table.d.ts +7 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.es.js +13560 -0
- package/dist/index.es.js.map +1 -0
- package/dist/interfaces.d.ts +18 -0
- package/dist/schema/auth-default-policies.d.ts +12 -0
- package/dist/schema/auth-schema.d.ts +2376 -0
- package/dist/schema/doctor-cli.d.ts +2 -0
- package/dist/schema/doctor.d.ts +52 -0
- package/dist/schema/generate-drizzle-schema-logic.d.ts +2 -0
- package/dist/schema/generate-drizzle-schema.d.ts +1 -0
- package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -0
- package/dist/schema/generate-postgres-ddl.d.ts +1 -0
- package/dist/schema/introspect-db-inference.d.ts +5 -0
- package/dist/schema/introspect-db-logic.d.ts +118 -0
- package/dist/schema/introspect-db.d.ts +1 -0
- package/dist/schema/test-schema.d.ts +24 -0
- package/dist/services/BranchService.d.ts +47 -0
- package/dist/services/EntityFetchService.d.ts +214 -0
- package/dist/services/EntityPersistService.d.ts +40 -0
- package/dist/services/RelationService.d.ts +98 -0
- package/dist/services/entity-helpers.d.ts +38 -0
- package/dist/services/entityService.d.ts +110 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/realtimeService.d.ts +220 -0
- package/dist/types.d.ts +3 -0
- package/dist/utils/drizzle-conditions.d.ts +138 -0
- package/dist/utils/pg-array-null-patch.d.ts +16 -0
- package/dist/utils/pg-error-utils.d.ts +65 -0
- package/dist/utils/table-classification.d.ts +8 -0
- package/dist/websocket.d.ts +11 -0
- package/package.json +17 -17
- package/src/PostgresBackendDriver.ts +135 -24
- package/src/cli.ts +73 -1
- package/src/collections/PostgresCollectionRegistry.ts +6 -6
- package/src/data-transformer.ts +2 -2
- package/src/schema/auth-default-policies.ts +13 -6
- package/src/schema/generate-drizzle-schema-logic.ts +16 -79
- package/src/schema/generate-postgres-ddl-logic.ts +14 -51
- package/src/services/realtimeService.ts +26 -2
- package/test/entity-callbacks-redaction.test.ts +86 -0
- package/test/postgresDataDriver.test.ts +2 -1
package/build-errors.txt
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
> @rebasepro/server-postgresql@0.0.1 build /Users/francesco/rebase/packages/server-postgresql
|
|
3
|
+
> vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json
|
|
4
|
+
|
|
5
|
+
vite v5.4.21 building for production...
|
|
6
|
+
transforming...
|
|
7
|
+
✓ 328 modules transformed.
|
|
8
|
+
rendering chunks...
|
|
9
|
+
computing gzip size...
|
|
10
|
+
dist/index.es.js 434.54 kB │ gzip: 83.11 kB │ map: 1,031.37 kB
|
|
11
|
+
dist/index.umd.js 459.59 kB │ gzip: 84.58 kB │ map: 1,035.30 kB
|
|
12
|
+
✓ built in 1.23s
|
|
13
|
+
src/auth/services.ts(119,15): error TS2322: Type '{ id: unknown; email: unknown; passwordHash: {} | null; displayName: {} | null; photoUrl: {} | null; provider: unknown; googleId: {} | null; emailVerified: {}; emailVerificationToken: {} | null; emailVerificationSentAt: {} | null; createdAt: unknown; updatedAt: unknown; }[]' is not assignable to type '{ email: string; id: string; createdAt: Date; passwordHash: string | null; displayName: string | null; photoUrl: string | null; provider: string; googleId: string | null; emailVerified: boolean; emailVerificationToken: string | null; emailVerificationSentAt: Date | null; updatedAt: Date; }[]'.
|
|
14
|
+
Type '{ id: unknown; email: unknown; passwordHash: {} | null; displayName: {} | null; photoUrl: {} | null; provider: unknown; googleId: {} | null; emailVerified: {}; emailVerificationToken: {} | null; emailVerificationSentAt: {} | null; createdAt: unknown; updatedAt: unknown; }' is not assignable to type '{ email: string; id: string; createdAt: Date; passwordHash: string | null; displayName: string | null; photoUrl: string | null; provider: string; googleId: string | null; emailVerified: boolean; emailVerificationToken: string | null; emailVerificationSentAt: Date | null; updatedAt: Date; }'.
|
|
15
|
+
Types of property 'email' are incompatible.
|
|
16
|
+
Type 'unknown' is not assignable to type 'string'.
|
|
17
|
+
src/collections/PostgresCollectionRegistry.ts(90,143): error TS2344: Type 'Record<string, unknown>' does not satisfy the constraint 'User'.
|
|
18
|
+
Type 'Record<string, unknown>' is missing the following properties from type 'User': uid, displayName, email, photoURL, and 2 more.
|
|
19
|
+
src/PostgresBootstrapper.ts(201,59): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'Partial<HistoryRetentionConfig> | undefined'.
|
|
20
|
+
Type 'number' has no properties in common with type 'Partial<HistoryRetentionConfig>'.
|
|
21
|
+
src/schema/generate-drizzle-schema-logic.ts(455,121): error TS2344: Type 'Record<string, unknown>' does not satisfy the constraint 'User'.
|
|
22
|
+
Type 'Record<string, unknown>' is missing the following properties from type 'User': uid, displayName, email, photoURL, and 2 more.
|
|
23
|
+
src/services/EntityFetchService.ts(37,72): error TS2344: Type 'Record<string, unknown>' does not satisfy the constraint 'TablesRelationalConfig'.
|
|
24
|
+
'string' index signatures are incompatible.
|
|
25
|
+
Type 'unknown' is not assignable to type 'TableRelationalConfig'.
|
|
26
|
+
src/services/EntityFetchService.ts(39,61): error TS2344: Type 'Record<string, unknown>' does not satisfy the constraint 'TablesRelationalConfig'.
|
|
27
|
+
'string' index signatures are incompatible.
|
|
28
|
+
Type 'unknown' is not assignable to type 'TableRelationalConfig'.
|
|
29
|
+
src/services/realtimeService.ts(621,21): error TS2322: Type 'Record<string, unknown> | undefined' is not assignable to type 'Partial<Record<string, [WhereFilterOp, unknown]>> | undefined'.
|
|
30
|
+
Type 'Record<string, unknown>' is not assignable to type 'Partial<Record<string, [WhereFilterOp, unknown]>>'.
|
|
31
|
+
'string' index signatures are incompatible.
|
|
32
|
+
Type 'unknown' is not assignable to type '[WhereFilterOp, unknown] | undefined'.
|
|
33
|
+
src/services/realtimeService.ts(630,13): error TS2322: Type 'Record<string, unknown> | undefined' is not assignable to type 'Partial<Record<string, [WhereFilterOp, unknown]>> | undefined'.
|
|
34
|
+
Type 'Record<string, unknown>' is not assignable to type 'Partial<Record<string, [WhereFilterOp, unknown]>>'.
|
|
35
|
+
'string' index signatures are incompatible.
|
|
36
|
+
Type 'unknown' is not assignable to type '[WhereFilterOp, unknown] | undefined'.
|
|
37
|
+
ELIFECYCLE Command failed with exit code 2.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DatabaseAdapter } from "@rebasepro/types";
|
|
2
|
+
import type { PostgresDriverConfig } from "./PostgresBootstrapper";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Postgres database adapter for Rebase.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createPostgresAdapter(pgConfig: PostgresDriverConfig): DatabaseAdapter;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { EntityService } from "./services/entityService";
|
|
2
|
+
import { BranchService } from "./services/BranchService";
|
|
3
|
+
import { RealtimeService } from "./services/realtimeService";
|
|
4
|
+
import { DatabasePoolManager } from "./databasePoolManager";
|
|
5
|
+
import { DrizzleClient } from "./interfaces";
|
|
6
|
+
import { DatabaseAdmin, DataDriver, DeleteEntityProps, Entity, EntityCollection, FetchCollectionProps, FetchEntityProps, ListenCollectionProps, ListenEntityProps, RebaseClient, RebaseData, RestFetchService, SaveEntityProps, TableMetadata, User } from "@rebasepro/types";
|
|
7
|
+
import { PostgresCollectionRegistry } from "./collections/PostgresCollectionRegistry";
|
|
8
|
+
import { HistoryService } from "./history/HistoryService";
|
|
9
|
+
export declare class PostgresBackendDriver implements DataDriver {
|
|
10
|
+
db: DrizzleClient;
|
|
11
|
+
readonly registry: PostgresCollectionRegistry;
|
|
12
|
+
poolManager?: DatabasePoolManager | undefined;
|
|
13
|
+
key: string;
|
|
14
|
+
initialised: boolean;
|
|
15
|
+
entityService: EntityService;
|
|
16
|
+
realtimeService: RealtimeService;
|
|
17
|
+
historyService?: HistoryService;
|
|
18
|
+
branchService?: BranchService;
|
|
19
|
+
user?: User;
|
|
20
|
+
data: RebaseData;
|
|
21
|
+
client?: RebaseClient;
|
|
22
|
+
/**
|
|
23
|
+
* Auto-set to `true` when a SET LOCAL ROLE fails with insufficient
|
|
24
|
+
* privileges, so subsequent queries skip the doomed attempt.
|
|
25
|
+
* Mirrors the static `DISABLE_DB_ROLE_SWITCHING` env var but is
|
|
26
|
+
* learned at runtime.
|
|
27
|
+
*/
|
|
28
|
+
private _roleSwitchingDisabled;
|
|
29
|
+
/**
|
|
30
|
+
* When true, realtime notifications are deferred until after the
|
|
31
|
+
* wrapping transaction commits. Set by `withAuth` → `withTransaction`.
|
|
32
|
+
*/
|
|
33
|
+
_deferNotifications: boolean;
|
|
34
|
+
_pendingNotifications: Array<{
|
|
35
|
+
path: string;
|
|
36
|
+
entityId: string;
|
|
37
|
+
entity: Entity | null;
|
|
38
|
+
databaseId?: string;
|
|
39
|
+
}>;
|
|
40
|
+
constructor(db: DrizzleClient, realtimeService: RealtimeService, registry: PostgresCollectionRegistry, user?: User, poolManager?: DatabasePoolManager | undefined, historyService?: HistoryService);
|
|
41
|
+
/**
|
|
42
|
+
* Typed admin capabilities (SQLAdmin + SchemaAdmin + BranchAdmin).
|
|
43
|
+
* Implemented as a getter so method references are resolved at call-time,
|
|
44
|
+
* allowing test spies applied after construction to take effect.
|
|
45
|
+
*/
|
|
46
|
+
get admin(): DatabaseAdmin;
|
|
47
|
+
/**
|
|
48
|
+
* REST-optimised fetch service (include-aware eager-loading).
|
|
49
|
+
* Delegates to the underlying EntityFetchService which already
|
|
50
|
+
* implements the matching method signatures.
|
|
51
|
+
*/
|
|
52
|
+
get restFetchService(): import("./services").EntityFetchService;
|
|
53
|
+
private buildCallContext;
|
|
54
|
+
private resolveCollectionCallbacks;
|
|
55
|
+
fetchCollection<M extends Record<string, unknown>>({ path, collection, filter, limit, offset, startAfter, orderBy, searchString, order, vectorSearch }: FetchCollectionProps<M>): Promise<Entity<M>[]>;
|
|
56
|
+
listenCollection<M extends Record<string, unknown>>({ path, collection, filter, limit, offset, startAfter, orderBy, searchString, order, onUpdate, onError }: ListenCollectionProps<M>): () => void;
|
|
57
|
+
fetchEntity<M extends Record<string, unknown>>({ path, entityId, databaseId, collection }: FetchEntityProps<M>): Promise<Entity<M> | undefined>;
|
|
58
|
+
listenEntity<M extends Record<string, unknown>>({ path, entityId, collection, onUpdate, onError }: ListenEntityProps<M>): () => void;
|
|
59
|
+
saveEntity<M extends Record<string, unknown>>({ path, entityId, values, collection, status }: SaveEntityProps<M>): Promise<Entity<M>>;
|
|
60
|
+
deleteEntity<M extends Record<string, unknown>>({ entity, collection }: DeleteEntityProps<M>): Promise<void>;
|
|
61
|
+
deleteAll(path: string): Promise<void>;
|
|
62
|
+
checkUniqueField(path: string, name: string, value: unknown, entityId?: string, collection?: EntityCollection): Promise<boolean>;
|
|
63
|
+
countEntities<M extends Record<string, unknown>>({ path, collection, filter, searchString }: FetchCollectionProps<M>): Promise<number>;
|
|
64
|
+
private getTargetDb;
|
|
65
|
+
executeSql(sqlText: string, options?: {
|
|
66
|
+
database?: string;
|
|
67
|
+
role?: string;
|
|
68
|
+
params?: unknown[];
|
|
69
|
+
}): Promise<Record<string, unknown>[]>;
|
|
70
|
+
fetchAvailableDatabases(): Promise<string[]>;
|
|
71
|
+
fetchAvailableRoles(): Promise<string[]>;
|
|
72
|
+
fetchCurrentDatabase(): Promise<string | undefined>;
|
|
73
|
+
/**
|
|
74
|
+
* Fetch public tables that are not yet mapped to a collection.
|
|
75
|
+
* Excludes internal tables (_rebase_*, _auth_*, auth tables, etc.)
|
|
76
|
+
* and junction/connection tables used for many-to-many relations.
|
|
77
|
+
*/
|
|
78
|
+
fetchUnmappedTables(mappedPaths?: string[]): Promise<string[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Fetch metadata for a given table from information_schema (columns, policies, constraints).
|
|
81
|
+
*/
|
|
82
|
+
fetchTableMetadata(tableName: string): Promise<TableMetadata>;
|
|
83
|
+
private generateSubscriptionId;
|
|
84
|
+
/**
|
|
85
|
+
* Create a new delegate instance with authenticated context.
|
|
86
|
+
* Starts a transaction and sets the current_user_id and current_user_roles
|
|
87
|
+
* configuration parameters for PostgreSQL Row Level Security.
|
|
88
|
+
*/
|
|
89
|
+
withAuth(user: User): Promise<DataDriver>;
|
|
90
|
+
}
|
|
91
|
+
export declare class AuthenticatedPostgresBackendDriver implements DataDriver {
|
|
92
|
+
delegate: PostgresBackendDriver;
|
|
93
|
+
key: string;
|
|
94
|
+
initialised: boolean;
|
|
95
|
+
user: User;
|
|
96
|
+
data: RebaseData;
|
|
97
|
+
constructor(delegate: PostgresBackendDriver, user: User);
|
|
98
|
+
/**
|
|
99
|
+
* Typed admin capabilities — delegates to the base driver.
|
|
100
|
+
*/
|
|
101
|
+
admin: DatabaseAdmin;
|
|
102
|
+
get restFetchService(): RestFetchService | undefined;
|
|
103
|
+
private withTransaction;
|
|
104
|
+
fetchCollection<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Entity<M>[]>;
|
|
105
|
+
/**
|
|
106
|
+
* Injects the authenticated user's context into the most recently
|
|
107
|
+
* registered realtime subscription so RLS-aware polling can apply.
|
|
108
|
+
*/
|
|
109
|
+
private injectAuthContext;
|
|
110
|
+
listenCollection<M extends Record<string, unknown>>(props: ListenCollectionProps<M>): () => void;
|
|
111
|
+
fetchEntity<M extends Record<string, unknown>>(props: FetchEntityProps<M>): Promise<Entity<M> | undefined>;
|
|
112
|
+
listenEntity<M extends Record<string, unknown>>(props: ListenEntityProps<M>): () => void;
|
|
113
|
+
saveEntity<M extends Record<string, unknown>>(props: SaveEntityProps<M>): Promise<Entity<M>>;
|
|
114
|
+
deleteEntity<M extends Record<string, unknown>>(props: DeleteEntityProps<M>): Promise<void>;
|
|
115
|
+
deleteAll(path: string): Promise<void>;
|
|
116
|
+
checkUniqueField(path: string, name: string, value: unknown, entityId?: string, collection?: EntityCollection): Promise<boolean>;
|
|
117
|
+
countEntities<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number>;
|
|
118
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgresBootstrapper
|
|
3
|
+
*
|
|
4
|
+
* Implements the `BackendBootstrapper` interface for PostgreSQL.
|
|
5
|
+
*/
|
|
6
|
+
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
7
|
+
import { BackendBootstrapper } from "@rebasepro/types";
|
|
8
|
+
import { PostgresBackendDriver } from "./PostgresBackendDriver";
|
|
9
|
+
import { RealtimeService } from "./services/realtimeService";
|
|
10
|
+
import { DatabasePoolManager } from "./databasePoolManager";
|
|
11
|
+
import { PostgresCollectionRegistry } from "./collections/PostgresCollectionRegistry";
|
|
12
|
+
export interface PostgresDriverConfig {
|
|
13
|
+
connectionString?: string;
|
|
14
|
+
adminConnectionString?: string;
|
|
15
|
+
readConnectionString?: string;
|
|
16
|
+
connection?: unknown;
|
|
17
|
+
schema?: {
|
|
18
|
+
tables?: Record<string, unknown>;
|
|
19
|
+
enums?: Record<string, unknown>;
|
|
20
|
+
relations?: Record<string, unknown>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Opaque internals bag that PostgresBootstrapper stores during `initializeDriver()`
|
|
25
|
+
* and re-uses in subsequent lifecycle hooks.
|
|
26
|
+
*/
|
|
27
|
+
export interface PostgresDriverInternals {
|
|
28
|
+
db: NodePgDatabase<any>;
|
|
29
|
+
readDb?: NodePgDatabase<any>;
|
|
30
|
+
registry: PostgresCollectionRegistry;
|
|
31
|
+
realtimeService: RealtimeService;
|
|
32
|
+
driver: PostgresBackendDriver;
|
|
33
|
+
poolManager?: DatabasePoolManager;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Default PostgreSQL bootstrapper.
|
|
37
|
+
*
|
|
38
|
+
* Use it to register Postgres with `initializeRebaseBackend()`:
|
|
39
|
+
* ```typescript
|
|
40
|
+
* initializeRebaseBackend({
|
|
41
|
+
* ...config,
|
|
42
|
+
* bootstrappers: [postgresBootstrapper()]
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function createPostgresBootstrapper(pgConfig: PostgresDriverConfig): BackendBootstrapper;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
|
+
import type { EntityCollection } from "@rebasepro/types";
|
|
3
|
+
/**
|
|
4
|
+
* Auto-create auth tables if they don't exist.
|
|
5
|
+
*
|
|
6
|
+
* @param db — Drizzle database instance
|
|
7
|
+
* @param collection — The collection that represents auth users.
|
|
8
|
+
* When omitted, a default `rebase.users` table is created.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ensureAuthTablesExist(db: NodePgDatabase, collection?: EntityCollection): Promise<void>;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
|
+
import type { RebasePgTable } from "../types";
|
|
3
|
+
import { UserRepository, TokenRepository, MfaRepository, AuthRepository, UserData, CreateUserData, RoleData, CreateRoleData, RefreshTokenInfo, PasswordResetTokenInfo, MagicLinkTokenInfo, UserIdentityData, ListUsersOptions, PaginatedUsersResult, MfaFactor, MfaChallengeInfo, RoleData as Role } from "@rebasepro/server-core";
|
|
4
|
+
export type { Role };
|
|
5
|
+
export interface AuthSchemaTables {
|
|
6
|
+
users: RebasePgTable;
|
|
7
|
+
refreshTokens: RebasePgTable;
|
|
8
|
+
passwordResetTokens: RebasePgTable;
|
|
9
|
+
appConfig: RebasePgTable;
|
|
10
|
+
userIdentities: RebasePgTable;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* PostgreSQL implementation of UserRepository.
|
|
14
|
+
* Handles all user-related database operations using Drizzle ORM.
|
|
15
|
+
*/
|
|
16
|
+
export declare class UserService implements UserRepository {
|
|
17
|
+
private db;
|
|
18
|
+
private usersTable;
|
|
19
|
+
private userIdentitiesTable;
|
|
20
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
21
|
+
private getQualifiedUsersTableName;
|
|
22
|
+
private mapRowToUser;
|
|
23
|
+
private mapPayload;
|
|
24
|
+
createUser(data: CreateUserData): Promise<UserData>;
|
|
25
|
+
getUserById(id: string): Promise<UserData | null>;
|
|
26
|
+
getUserByEmail(email: string): Promise<UserData | null>;
|
|
27
|
+
getUserByIdentity(provider: string, providerId: string): Promise<UserData | null>;
|
|
28
|
+
getUserIdentities(userId: string): Promise<UserIdentityData[]>;
|
|
29
|
+
linkUserIdentity(userId: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void>;
|
|
30
|
+
updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null>;
|
|
31
|
+
deleteUser(id: string): Promise<void>;
|
|
32
|
+
listUsers(): Promise<UserData[]>;
|
|
33
|
+
listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Update user's password hash
|
|
36
|
+
*/
|
|
37
|
+
updatePassword(id: string, passwordHash: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Set email verification status
|
|
40
|
+
*/
|
|
41
|
+
setEmailVerified(id: string, verified: boolean): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Set email verification token
|
|
44
|
+
*/
|
|
45
|
+
setVerificationToken(id: string, token: string | null): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Find user by email verification token
|
|
48
|
+
*/
|
|
49
|
+
getUserByVerificationToken(token: string): Promise<UserData | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Get roles for a user from database (inline TEXT[] column)
|
|
52
|
+
*/
|
|
53
|
+
getUserRoles(userId: string): Promise<Role[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Get role IDs for a user
|
|
56
|
+
*/
|
|
57
|
+
getUserRoleIds(userId: string): Promise<string[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Set roles for a user (replaces existing roles)
|
|
60
|
+
*/
|
|
61
|
+
setUserRoles(userId: string, roleIds: string[]): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Assign a specific role to new user (appends if not present)
|
|
64
|
+
*/
|
|
65
|
+
assignDefaultRole(userId: string, roleId: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Get user with their roles
|
|
68
|
+
*/
|
|
69
|
+
getUserWithRoles(userId: string): Promise<{
|
|
70
|
+
user: UserData;
|
|
71
|
+
roles: Role[];
|
|
72
|
+
} | null>;
|
|
73
|
+
}
|
|
74
|
+
export declare class RefreshTokenService {
|
|
75
|
+
private db;
|
|
76
|
+
private refreshTokensTable;
|
|
77
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
78
|
+
private getQualifiedRefreshTokensTableName;
|
|
79
|
+
createToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
|
|
80
|
+
findByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
|
|
81
|
+
deleteByHash(tokenHash: string): Promise<void>;
|
|
82
|
+
deleteAllForUser(userId: string): Promise<void>;
|
|
83
|
+
listForUser(userId: string): Promise<RefreshTokenInfo[]>;
|
|
84
|
+
deleteById(id: string, userId: string): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Password reset token service
|
|
88
|
+
*/
|
|
89
|
+
export declare class PasswordResetTokenService {
|
|
90
|
+
private db;
|
|
91
|
+
private passwordResetTokensTable;
|
|
92
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
93
|
+
private getQualifiedPasswordResetTokensTableName;
|
|
94
|
+
/**
|
|
95
|
+
* Create a password reset token
|
|
96
|
+
*/
|
|
97
|
+
createToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Find a valid (not expired, not used) token by hash
|
|
100
|
+
*/
|
|
101
|
+
findValidByHash(tokenHash: string): Promise<{
|
|
102
|
+
userId: string;
|
|
103
|
+
expiresAt: Date;
|
|
104
|
+
} | null>;
|
|
105
|
+
/**
|
|
106
|
+
* Mark token as used
|
|
107
|
+
*/
|
|
108
|
+
markAsUsed(tokenHash: string): Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Delete all tokens for a user
|
|
111
|
+
*/
|
|
112
|
+
deleteAllForUser(userId: string): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Clean up expired tokens
|
|
115
|
+
*/
|
|
116
|
+
deleteExpired(): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Magic link token service.
|
|
120
|
+
* Handles magic link token storage for passwordless email login.
|
|
121
|
+
*/
|
|
122
|
+
export declare class MagicLinkTokenService {
|
|
123
|
+
private db;
|
|
124
|
+
private magicLinkTokensTable;
|
|
125
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
126
|
+
private getQualifiedTableName;
|
|
127
|
+
createToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
128
|
+
findValidByHash(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
129
|
+
markAsUsed(tokenHash: string): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* PostgreSQL implementation of TokenRepository.
|
|
133
|
+
* Combines refresh token and password reset token operations.
|
|
134
|
+
*/
|
|
135
|
+
export declare class PostgresTokenRepository implements TokenRepository {
|
|
136
|
+
private db;
|
|
137
|
+
private refreshTokenService;
|
|
138
|
+
private passwordResetTokenService;
|
|
139
|
+
private magicLinkTokenService;
|
|
140
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
141
|
+
createRefreshToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
|
|
142
|
+
findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
|
|
143
|
+
deleteRefreshToken(tokenHash: string): Promise<void>;
|
|
144
|
+
deleteAllRefreshTokensForUser(userId: string): Promise<void>;
|
|
145
|
+
listRefreshTokensForUser(userId: string): Promise<RefreshTokenInfo[]>;
|
|
146
|
+
deleteRefreshTokenById(id: string, userId: string): Promise<void>;
|
|
147
|
+
createPasswordResetToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
148
|
+
findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null>;
|
|
149
|
+
markPasswordResetTokenUsed(tokenHash: string): Promise<void>;
|
|
150
|
+
deleteAllPasswordResetTokensForUser(userId: string): Promise<void>;
|
|
151
|
+
deleteExpiredTokens(): Promise<void>;
|
|
152
|
+
createMagicLinkToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
153
|
+
findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
154
|
+
markMagicLinkTokenUsed(tokenHash: string): Promise<void>;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* PostgreSQL implementation of AuthRepository.
|
|
158
|
+
* Combines user, role, and token repository operations.
|
|
159
|
+
* This provides a convenient single-class interface for all auth operations.
|
|
160
|
+
*/
|
|
161
|
+
export declare class PostgresAuthRepository implements AuthRepository {
|
|
162
|
+
private db;
|
|
163
|
+
private userService;
|
|
164
|
+
private tokenRepository;
|
|
165
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
166
|
+
createUser(data: CreateUserData): Promise<UserData>;
|
|
167
|
+
getUserById(id: string): Promise<UserData | null>;
|
|
168
|
+
getUserByEmail(email: string): Promise<UserData | null>;
|
|
169
|
+
getUserByIdentity(provider: string, providerId: string): Promise<UserData | null>;
|
|
170
|
+
getUserIdentities(userId: string): Promise<UserIdentityData[]>;
|
|
171
|
+
linkUserIdentity(userId: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void>;
|
|
172
|
+
updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null>;
|
|
173
|
+
deleteUser(id: string): Promise<void>;
|
|
174
|
+
listUsers(): Promise<UserData[]>;
|
|
175
|
+
listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
|
|
176
|
+
updatePassword(id: string, passwordHash: string): Promise<void>;
|
|
177
|
+
setEmailVerified(id: string, verified: boolean): Promise<void>;
|
|
178
|
+
setVerificationToken(id: string, token: string | null): Promise<void>;
|
|
179
|
+
getUserByVerificationToken(token: string): Promise<UserData | null>;
|
|
180
|
+
getUserRoles(userId: string): Promise<RoleData[]>;
|
|
181
|
+
getUserRoleIds(userId: string): Promise<string[]>;
|
|
182
|
+
setUserRoles(userId: string, roleIds: string[]): Promise<void>;
|
|
183
|
+
assignDefaultRole(userId: string, roleId: string): Promise<void>;
|
|
184
|
+
getUserWithRoles(userId: string): Promise<{
|
|
185
|
+
user: UserData;
|
|
186
|
+
roles: RoleData[];
|
|
187
|
+
} | null>;
|
|
188
|
+
getRoleById(id: string): Promise<RoleData | null>;
|
|
189
|
+
listRoles(): Promise<RoleData[]>;
|
|
190
|
+
createRole(_data: CreateRoleData): Promise<RoleData>;
|
|
191
|
+
updateRole(id: string, data: Partial<Omit<RoleData, "id">>): Promise<RoleData | null>;
|
|
192
|
+
deleteRole(_id: string): Promise<void>;
|
|
193
|
+
createRefreshToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
|
|
194
|
+
findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
|
|
195
|
+
deleteRefreshToken(tokenHash: string): Promise<void>;
|
|
196
|
+
deleteAllRefreshTokensForUser(userId: string): Promise<void>;
|
|
197
|
+
listRefreshTokensForUser(userId: string): Promise<RefreshTokenInfo[]>;
|
|
198
|
+
deleteRefreshTokenById(id: string, userId: string): Promise<void>;
|
|
199
|
+
createPasswordResetToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
200
|
+
findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null>;
|
|
201
|
+
markPasswordResetTokenUsed(tokenHash: string): Promise<void>;
|
|
202
|
+
deleteAllPasswordResetTokensForUser(userId: string): Promise<void>;
|
|
203
|
+
deleteExpiredTokens(): Promise<void>;
|
|
204
|
+
createMagicLinkToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
205
|
+
findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
206
|
+
markMagicLinkTokenUsed(tokenHash: string): Promise<void>;
|
|
207
|
+
private _mfaService;
|
|
208
|
+
private getMfaService;
|
|
209
|
+
createMfaFactor(userId: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor>;
|
|
210
|
+
getMfaFactors(userId: string): Promise<MfaFactor[]>;
|
|
211
|
+
getMfaFactorById(factorId: string): Promise<(MfaFactor & {
|
|
212
|
+
secretEncrypted: string;
|
|
213
|
+
}) | null>;
|
|
214
|
+
verifyMfaFactor(factorId: string): Promise<void>;
|
|
215
|
+
deleteMfaFactor(factorId: string, userId: string): Promise<void>;
|
|
216
|
+
createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo>;
|
|
217
|
+
getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null>;
|
|
218
|
+
verifyMfaChallenge(challengeId: string): Promise<void>;
|
|
219
|
+
createRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
|
|
220
|
+
useRecoveryCode(userId: string, codeHash: string): Promise<boolean>;
|
|
221
|
+
getUnusedRecoveryCodeCount(userId: string): Promise<number>;
|
|
222
|
+
deleteAllRecoveryCodes(userId: string): Promise<void>;
|
|
223
|
+
hasVerifiedMfaFactors(userId: string): Promise<boolean>;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* PostgreSQL implementation of MfaRepository.
|
|
227
|
+
* Handles all MFA-related database operations.
|
|
228
|
+
*/
|
|
229
|
+
export declare class MfaService implements MfaRepository {
|
|
230
|
+
private db;
|
|
231
|
+
private schemaName;
|
|
232
|
+
constructor(db: NodePgDatabase, schemaName?: string);
|
|
233
|
+
private qualify;
|
|
234
|
+
createMfaFactor(userId: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor>;
|
|
235
|
+
getMfaFactors(userId: string): Promise<MfaFactor[]>;
|
|
236
|
+
getMfaFactorById(factorId: string): Promise<(MfaFactor & {
|
|
237
|
+
secretEncrypted: string;
|
|
238
|
+
}) | null>;
|
|
239
|
+
verifyMfaFactor(factorId: string): Promise<void>;
|
|
240
|
+
deleteMfaFactor(factorId: string, userId: string): Promise<void>;
|
|
241
|
+
createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo>;
|
|
242
|
+
getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null>;
|
|
243
|
+
verifyMfaChallenge(challengeId: string): Promise<void>;
|
|
244
|
+
createRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
|
|
245
|
+
useRecoveryCode(userId: string, codeHash: string): Promise<boolean>;
|
|
246
|
+
getUnusedRecoveryCodeCount(userId: string): Promise<number>;
|
|
247
|
+
deleteAllRecoveryCodes(userId: string): Promise<void>;
|
|
248
|
+
hasVerifiedMfaFactors(userId: string): Promise<boolean>;
|
|
249
|
+
}
|
|
250
|
+
/** PostgreSQL user repository implementation */
|
|
251
|
+
export type PostgresUserRepository = UserService;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect whether an error (or AggregateError wrapping multiple attempts)
|
|
3
|
+
* represents an ECONNREFUSED — i.e. the database is simply not running.
|
|
4
|
+
*
|
|
5
|
+
* Handles:
|
|
6
|
+
* - Direct `{ code: "ECONNREFUSED" }` errors from Node `net`
|
|
7
|
+
* - `AggregateError` from dual-stack IPv4+IPv6 connection attempts
|
|
8
|
+
* - Drizzle's `cause`-wrapped pg errors
|
|
9
|
+
*/
|
|
10
|
+
export declare function isEconnrefused(err: unknown): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Detect PostgreSQL authentication failures.
|
|
13
|
+
* PG error codes: 28P01 (invalid_password), 28000 (invalid_authorization_specification)
|
|
14
|
+
*/
|
|
15
|
+
export declare function isAuthFailure(err: unknown): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Pre-flight check: verify that the database is reachable before running
|
|
18
|
+
* a heavy subprocess (Atlas, migrations, etc.).
|
|
19
|
+
*
|
|
20
|
+
* Exits with code 1 and a friendly banner on known failure modes.
|
|
21
|
+
* On unknown errors, logs a warning and allows the caller to proceed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function checkDatabaseConnectivity(databaseUrl: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Post-hoc error diagnosis for direct database operations (e.g. applyPolicies).
|
|
26
|
+
* Returns a formatted diagnostic string if the error matches a known pattern,
|
|
27
|
+
* or null if unrecognized.
|
|
28
|
+
*/
|
|
29
|
+
export declare function diagnoseDbError(err: unknown, databaseUrl?: string): string | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EntityCollection } from "@rebasepro/types";
|
|
2
|
+
export declare function resolveLocalBin(binName: string): string | null;
|
|
3
|
+
export declare function getTableIncludesFromCollections(collections: EntityCollection[]): Promise<string[]>;
|
|
4
|
+
export declare function getTableIncludes(collectionsPath: string): Promise<string[]>;
|
|
5
|
+
export declare function getDevDatabaseUrl(databaseUrl: string): string;
|
|
6
|
+
export declare function ensureDevDatabaseExists(databaseUrl: string, devDatabaseUrl: string): Promise<void>;
|
|
7
|
+
export declare function getTableExcludes(databaseUrl: string, collectionsPath: string): Promise<string[]>;
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runPluginCommand(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CollectionRegistry } from "@rebasepro/common";
|
|
2
|
+
import { type EntityCollection } from "@rebasepro/types";
|
|
3
|
+
import { PgEnum, PgTable } from "drizzle-orm/pg-core";
|
|
4
|
+
import { Relations } from "drizzle-orm";
|
|
5
|
+
import { CollectionRegistryInterface } from "../interfaces";
|
|
6
|
+
/**
|
|
7
|
+
* PostgreSQL-specific collection registry.
|
|
8
|
+
* Extends the base CollectionRegistry with support for Drizzle ORM tables, enums, and relations.
|
|
9
|
+
*
|
|
10
|
+
* Satisfies CollectionRegistryInterface through inheritance from CollectionRegistry.
|
|
11
|
+
*/
|
|
12
|
+
export declare class PostgresCollectionRegistry extends CollectionRegistry implements CollectionRegistryInterface {
|
|
13
|
+
private tables;
|
|
14
|
+
private enums;
|
|
15
|
+
private relations;
|
|
16
|
+
registerTable(table: PgTable, tableName: string): void;
|
|
17
|
+
getTable(tableName: string): PgTable | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Checks if a specific collection has a registered table
|
|
20
|
+
*/
|
|
21
|
+
hasTableForCollection(tableName: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Returns all registered table names.
|
|
24
|
+
*/
|
|
25
|
+
getTableNames(): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Finds collections assigned to a specific data source that do not have a registered table.
|
|
28
|
+
*/
|
|
29
|
+
getCollectionsWithoutTables(dataSourceKey?: string): EntityCollection[];
|
|
30
|
+
registerEnums(enums: Record<string, PgEnum<[string, ...string[]]>>): void;
|
|
31
|
+
registerRelations(relations: Record<string, Relations>): void;
|
|
32
|
+
getEnum(name: string): PgEnum<[string, ...string[]]> | undefined;
|
|
33
|
+
getRelation(name: string): Relations | undefined;
|
|
34
|
+
getAllEnums(): Record<string, PgEnum<[string, ...string[]]>>;
|
|
35
|
+
getAllRelations(): Record<string, Relations>;
|
|
36
|
+
/**
|
|
37
|
+
* Get the merged schema object (tables + relations) for use with Drizzle's
|
|
38
|
+
* relational query API (`db.query`).
|
|
39
|
+
*/
|
|
40
|
+
getMergedSchema(): Record<string, unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* Get the available Drizzle relation keys for a given collection path.
|
|
43
|
+
* Maps from the collection's relation property names to the Drizzle relation names
|
|
44
|
+
* defined in the schema.
|
|
45
|
+
*/
|
|
46
|
+
getRelationKeysForCollection(collectionPath: string): string[];
|
|
47
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Pool } from "pg";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for the Postgres connection pool.
|
|
4
|
+
*
|
|
5
|
+
* Sensible defaults are provided for production Cloud Run / single-instance
|
|
6
|
+
* deployments. Override via environment variables or explicit config.
|
|
7
|
+
*/
|
|
8
|
+
export interface PostgresPoolConfig {
|
|
9
|
+
/** Maximum number of connections in the pool (default: 20) */
|
|
10
|
+
max?: number;
|
|
11
|
+
/** Close idle connections after this many ms (default: 30 000) */
|
|
12
|
+
idleTimeoutMillis?: number;
|
|
13
|
+
/** Abort connection attempts after this many ms (default: 10 000) */
|
|
14
|
+
connectionTimeoutMillis?: number;
|
|
15
|
+
/** Per-query timeout in ms (default: 30 000) */
|
|
16
|
+
queryTimeout?: number;
|
|
17
|
+
/** Per-statement timeout in ms (default: 30 000) */
|
|
18
|
+
statementTimeout?: number;
|
|
19
|
+
/** Enable TCP keep-alive (default: true) */
|
|
20
|
+
keepAlive?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a Drizzle-backed Postgres connection with a production-grade
|
|
24
|
+
* connection pool.
|
|
25
|
+
*
|
|
26
|
+
* @param connectionString Postgres connection URL
|
|
27
|
+
* @param schema Optional Drizzle schema for the relational API
|
|
28
|
+
* @param poolConfig Optional pool tuning (merged over defaults)
|
|
29
|
+
*
|
|
30
|
+
* @returns `{ db, pool, connectionString }` — the `pool` is exposed so
|
|
31
|
+
* callers can register shutdown hooks (`pool.end()`) or monitor
|
|
32
|
+
* pool metrics.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createPostgresDatabaseConnection(connectionString: string, schema?: Record<string, unknown>, poolConfig?: PostgresPoolConfig): {
|
|
35
|
+
db: import("drizzle-orm/node-postgres").NodePgDatabase<Record<string, unknown>> & {
|
|
36
|
+
$client: Pool;
|
|
37
|
+
};
|
|
38
|
+
pool: Pool;
|
|
39
|
+
connectionString: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Create a direct (non-pooled) connection for operations that require
|
|
43
|
+
* session-level features incompatible with PgBouncer transaction mode,
|
|
44
|
+
* such as LISTEN/NOTIFY, prepared statements, or advisory locks.
|
|
45
|
+
*
|
|
46
|
+
* Uses a smaller pool since this is only for specific use cases.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createDirectDatabaseConnection(connectionString: string, schema?: Record<string, unknown>, poolConfig?: PostgresPoolConfig): {
|
|
49
|
+
db: import("drizzle-orm/node-postgres").NodePgDatabase<Record<string, unknown>> & {
|
|
50
|
+
$client: Pool;
|
|
51
|
+
};
|
|
52
|
+
pool: Pool;
|
|
53
|
+
connectionString: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Create a read-only connection for routing read queries to replicas.
|
|
57
|
+
* Uses a moderate pool size since reads are distributed across replicas.
|
|
58
|
+
*/
|
|
59
|
+
export declare function createReadReplicaConnection(connectionString: string, schema?: Record<string, unknown>, poolConfig?: PostgresPoolConfig): {
|
|
60
|
+
db: import("drizzle-orm/node-postgres").NodePgDatabase<Record<string, unknown>> & {
|
|
61
|
+
$client: Pool;
|
|
62
|
+
};
|
|
63
|
+
pool: Pool;
|
|
64
|
+
connectionString: string;
|
|
65
|
+
};
|