@rebasepro/client-postgresql 0.0.1-canary.4d4fb3e → 0.0.1-canary.ca2cb6e
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/dist/client/src/admin.d.ts +94 -0
- package/dist/client/src/auth.d.ts +161 -0
- package/dist/client/src/collection.d.ts +19 -0
- package/dist/client/src/cron.d.ts +25 -0
- package/dist/client/src/index.d.ts +42 -0
- package/dist/client/src/query_builder.d.ts +53 -0
- package/dist/client/src/reviver.d.ts +1 -0
- package/dist/client/src/storage.d.ts +3 -0
- package/dist/client/src/transport.d.ts +33 -0
- package/dist/client/src/websocket.d.ts +99 -0
- package/dist/index.es.js +28 -17
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +28 -17
- package/dist/index.umd.js.map +1 -1
- package/dist/types/src/controllers/auth.d.ts +2 -0
- package/dist/types/src/controllers/client.d.ts +119 -7
- package/dist/types/src/controllers/collection_registry.d.ts +4 -3
- package/dist/types/src/controllers/customization_controller.d.ts +7 -1
- package/dist/types/src/controllers/data.d.ts +34 -7
- package/dist/types/src/controllers/data_driver.d.ts +20 -28
- package/dist/types/src/controllers/database_admin.d.ts +2 -2
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +1 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +4 -4
- package/dist/types/src/controllers/navigation.d.ts +5 -5
- package/dist/types/src/controllers/registry.d.ts +6 -3
- package/dist/types/src/controllers/side_entity_controller.d.ts +7 -6
- package/dist/types/src/controllers/storage.d.ts +24 -26
- package/dist/types/src/rebase_context.d.ts +8 -4
- package/dist/types/src/types/backend.d.ts +4 -1
- package/dist/types/src/types/builders.d.ts +5 -4
- package/dist/types/src/types/chips.d.ts +1 -1
- package/dist/types/src/types/collections.d.ts +169 -125
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +1 -1
- package/dist/types/src/types/entity_actions.d.ts +8 -8
- package/dist/types/src/types/entity_callbacks.d.ts +15 -15
- package/dist/types/src/types/entity_link_builder.d.ts +1 -1
- package/dist/types/src/types/entity_overrides.d.ts +2 -1
- package/dist/types/src/types/entity_views.d.ts +8 -8
- package/dist/types/src/types/export_import.d.ts +3 -3
- package/dist/types/src/types/index.d.ts +1 -0
- package/dist/types/src/types/plugins.d.ts +72 -18
- package/dist/types/src/types/properties.d.ts +118 -33
- package/dist/types/src/types/relations.d.ts +1 -1
- package/dist/types/src/types/slots.d.ts +30 -6
- package/dist/types/src/types/translations.d.ts +44 -0
- package/dist/types/src/types/user_management_delegate.d.ts +1 -0
- package/package.json +83 -86
- package/src/usePostgresClientDriver.ts +57 -29
- package/tsconfig.json +1 -0
- package/vite.config.ts +4 -4
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Transport } from "./transport";
|
|
2
|
+
export interface AdminUser {
|
|
3
|
+
uid: string;
|
|
4
|
+
email: string;
|
|
5
|
+
displayName: string | null;
|
|
6
|
+
photoURL: string | null;
|
|
7
|
+
provider: string;
|
|
8
|
+
roles: string[];
|
|
9
|
+
createdAt: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}
|
|
12
|
+
export interface RebaseRole {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
isAdmin: boolean;
|
|
16
|
+
defaultPermissions: Record<string, unknown> | null;
|
|
17
|
+
config: Record<string, unknown> | null;
|
|
18
|
+
}
|
|
19
|
+
export interface CreateAdminOptions {
|
|
20
|
+
adminPath?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function createAdmin(transport: Transport, options?: CreateAdminOptions): {
|
|
23
|
+
listUsers: () => Promise<{
|
|
24
|
+
users: AdminUser[];
|
|
25
|
+
}>;
|
|
26
|
+
listUsersPaginated: (options?: {
|
|
27
|
+
search?: string;
|
|
28
|
+
limit?: number;
|
|
29
|
+
offset?: number;
|
|
30
|
+
orderBy?: string;
|
|
31
|
+
orderDir?: "asc" | "desc";
|
|
32
|
+
}) => Promise<{
|
|
33
|
+
users: AdminUser[];
|
|
34
|
+
total: number;
|
|
35
|
+
limit: number;
|
|
36
|
+
offset: number;
|
|
37
|
+
}>;
|
|
38
|
+
getUser: (userId: string) => Promise<{
|
|
39
|
+
user: AdminUser;
|
|
40
|
+
}>;
|
|
41
|
+
createUser: (data: {
|
|
42
|
+
email: string;
|
|
43
|
+
displayName?: string;
|
|
44
|
+
password?: string;
|
|
45
|
+
roles?: string[];
|
|
46
|
+
}) => Promise<{
|
|
47
|
+
user: AdminUser;
|
|
48
|
+
}>;
|
|
49
|
+
updateUser: (userId: string, data: {
|
|
50
|
+
email?: string;
|
|
51
|
+
displayName?: string;
|
|
52
|
+
password?: string;
|
|
53
|
+
roles?: string[];
|
|
54
|
+
}) => Promise<{
|
|
55
|
+
user: AdminUser;
|
|
56
|
+
}>;
|
|
57
|
+
deleteUser: (userId: string) => Promise<{
|
|
58
|
+
success: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
listRoles: () => Promise<{
|
|
61
|
+
roles: RebaseRole[];
|
|
62
|
+
}>;
|
|
63
|
+
getRole: (roleId: string) => Promise<{
|
|
64
|
+
role: RebaseRole;
|
|
65
|
+
}>;
|
|
66
|
+
createRole: (data: {
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
isAdmin?: boolean;
|
|
70
|
+
defaultPermissions?: Record<string, unknown>;
|
|
71
|
+
config?: Record<string, unknown>;
|
|
72
|
+
}) => Promise<{
|
|
73
|
+
role: RebaseRole;
|
|
74
|
+
}>;
|
|
75
|
+
updateRole: (roleId: string, data: {
|
|
76
|
+
name?: string;
|
|
77
|
+
isAdmin?: boolean;
|
|
78
|
+
defaultPermissions?: Record<string, unknown>;
|
|
79
|
+
config?: Record<string, unknown>;
|
|
80
|
+
}) => Promise<{
|
|
81
|
+
role: RebaseRole;
|
|
82
|
+
}>;
|
|
83
|
+
deleteRole: (roleId: string) => Promise<{
|
|
84
|
+
success: boolean;
|
|
85
|
+
}>;
|
|
86
|
+
bootstrap: () => Promise<{
|
|
87
|
+
success: boolean;
|
|
88
|
+
message: string;
|
|
89
|
+
user: {
|
|
90
|
+
uid: string;
|
|
91
|
+
roles: string[];
|
|
92
|
+
};
|
|
93
|
+
}>;
|
|
94
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Transport } from "./transport";
|
|
2
|
+
export interface RebaseUser {
|
|
3
|
+
uid: string;
|
|
4
|
+
email: string | null;
|
|
5
|
+
displayName: string | null;
|
|
6
|
+
photoURL: string | null;
|
|
7
|
+
emailVerified?: boolean;
|
|
8
|
+
roles?: string[];
|
|
9
|
+
providerId: string;
|
|
10
|
+
isAnonymous: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface RebaseTokens {
|
|
13
|
+
accessToken: string;
|
|
14
|
+
refreshToken: string;
|
|
15
|
+
accessTokenExpiresAt: number;
|
|
16
|
+
}
|
|
17
|
+
export interface RebaseSession {
|
|
18
|
+
accessToken: string;
|
|
19
|
+
refreshToken: string;
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
user: RebaseUser;
|
|
22
|
+
}
|
|
23
|
+
export type AuthChangeEvent = "SIGNED_IN" | "SIGNED_OUT" | "TOKEN_REFRESHED" | "USER_UPDATED";
|
|
24
|
+
export interface AuthConfig {
|
|
25
|
+
needsSetup: boolean;
|
|
26
|
+
registrationEnabled: boolean;
|
|
27
|
+
googleEnabled: boolean;
|
|
28
|
+
emailServiceEnabled: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface AuthStorage {
|
|
31
|
+
getItem: (key: string) => string | null;
|
|
32
|
+
setItem: (key: string, value: string) => void;
|
|
33
|
+
removeItem: (key: string) => void;
|
|
34
|
+
}
|
|
35
|
+
export declare function createMemoryStorage(): AuthStorage;
|
|
36
|
+
export interface CreateAuthOptions {
|
|
37
|
+
storage?: AuthStorage;
|
|
38
|
+
authPath?: string;
|
|
39
|
+
autoRefresh?: boolean;
|
|
40
|
+
persistSession?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export declare function createAuth(transport: Transport, options?: CreateAuthOptions): {
|
|
43
|
+
signInWithEmail: (email: string, password: string) => Promise<{
|
|
44
|
+
user: RebaseUser;
|
|
45
|
+
accessToken: string;
|
|
46
|
+
refreshToken: string;
|
|
47
|
+
}>;
|
|
48
|
+
signUp: (email: string, password: string, displayName?: string) => Promise<{
|
|
49
|
+
user: RebaseUser;
|
|
50
|
+
accessToken: string;
|
|
51
|
+
refreshToken: string;
|
|
52
|
+
}>;
|
|
53
|
+
signInWithGoogle: (idToken: string) => Promise<{
|
|
54
|
+
user: RebaseUser;
|
|
55
|
+
accessToken: string;
|
|
56
|
+
refreshToken: string;
|
|
57
|
+
}>;
|
|
58
|
+
signInWithLinkedin: (code: string, redirectUri: string) => Promise<{
|
|
59
|
+
user: RebaseUser;
|
|
60
|
+
accessToken: string;
|
|
61
|
+
refreshToken: string;
|
|
62
|
+
}>;
|
|
63
|
+
signInWithOAuth: (providerId: string, payload: Record<string, unknown>) => Promise<{
|
|
64
|
+
user: RebaseUser;
|
|
65
|
+
accessToken: string;
|
|
66
|
+
refreshToken: string;
|
|
67
|
+
}>;
|
|
68
|
+
signInWithGitHub: (code: string, redirectUri: string) => Promise<{
|
|
69
|
+
user: RebaseUser;
|
|
70
|
+
accessToken: string;
|
|
71
|
+
refreshToken: string;
|
|
72
|
+
}>;
|
|
73
|
+
signInWithMicrosoft: (code: string, redirectUri: string) => Promise<{
|
|
74
|
+
user: RebaseUser;
|
|
75
|
+
accessToken: string;
|
|
76
|
+
refreshToken: string;
|
|
77
|
+
}>;
|
|
78
|
+
signInWithApple: (code: string, redirectUri: string, user?: {
|
|
79
|
+
name?: {
|
|
80
|
+
firstName?: string;
|
|
81
|
+
lastName?: string;
|
|
82
|
+
};
|
|
83
|
+
email?: string;
|
|
84
|
+
}) => Promise<{
|
|
85
|
+
user: RebaseUser;
|
|
86
|
+
accessToken: string;
|
|
87
|
+
refreshToken: string;
|
|
88
|
+
}>;
|
|
89
|
+
signInWithFacebook: (code: string, redirectUri: string) => Promise<{
|
|
90
|
+
user: RebaseUser;
|
|
91
|
+
accessToken: string;
|
|
92
|
+
refreshToken: string;
|
|
93
|
+
}>;
|
|
94
|
+
signInWithTwitter: (code: string, redirectUri: string, codeVerifier: string) => Promise<{
|
|
95
|
+
user: RebaseUser;
|
|
96
|
+
accessToken: string;
|
|
97
|
+
refreshToken: string;
|
|
98
|
+
}>;
|
|
99
|
+
signInWithDiscord: (code: string, redirectUri: string) => Promise<{
|
|
100
|
+
user: RebaseUser;
|
|
101
|
+
accessToken: string;
|
|
102
|
+
refreshToken: string;
|
|
103
|
+
}>;
|
|
104
|
+
signInWithGitLab: (code: string, redirectUri: string) => Promise<{
|
|
105
|
+
user: RebaseUser;
|
|
106
|
+
accessToken: string;
|
|
107
|
+
refreshToken: string;
|
|
108
|
+
}>;
|
|
109
|
+
signInWithBitbucket: (code: string, redirectUri: string) => Promise<{
|
|
110
|
+
user: RebaseUser;
|
|
111
|
+
accessToken: string;
|
|
112
|
+
refreshToken: string;
|
|
113
|
+
}>;
|
|
114
|
+
signInWithSlack: (code: string, redirectUri: string) => Promise<{
|
|
115
|
+
user: RebaseUser;
|
|
116
|
+
accessToken: string;
|
|
117
|
+
refreshToken: string;
|
|
118
|
+
}>;
|
|
119
|
+
signInWithSpotify: (code: string, redirectUri: string) => Promise<{
|
|
120
|
+
user: RebaseUser;
|
|
121
|
+
accessToken: string;
|
|
122
|
+
refreshToken: string;
|
|
123
|
+
}>;
|
|
124
|
+
signOut: () => Promise<void>;
|
|
125
|
+
refreshSession: () => Promise<RebaseSession>;
|
|
126
|
+
getUser: () => Promise<RebaseUser>;
|
|
127
|
+
updateUser: (updates: {
|
|
128
|
+
displayName?: string;
|
|
129
|
+
photoURL?: string;
|
|
130
|
+
}) => Promise<RebaseUser>;
|
|
131
|
+
resetPasswordForEmail: (email: string) => Promise<{
|
|
132
|
+
success: boolean;
|
|
133
|
+
message: string;
|
|
134
|
+
}>;
|
|
135
|
+
resetPassword: (token: string, password: string) => Promise<{
|
|
136
|
+
success: boolean;
|
|
137
|
+
message: string;
|
|
138
|
+
}>;
|
|
139
|
+
changePassword: (oldPassword: string, newPassword: string) => Promise<{
|
|
140
|
+
success: boolean;
|
|
141
|
+
message: string;
|
|
142
|
+
}>;
|
|
143
|
+
sendVerificationEmail: () => Promise<{
|
|
144
|
+
success: boolean;
|
|
145
|
+
message: string;
|
|
146
|
+
}>;
|
|
147
|
+
verifyEmail: (token: string) => Promise<{
|
|
148
|
+
success: boolean;
|
|
149
|
+
message: string;
|
|
150
|
+
}>;
|
|
151
|
+
getSessions: () => Promise<Record<string, unknown>[]>;
|
|
152
|
+
revokeSession: (sessionId: string) => Promise<{
|
|
153
|
+
success: boolean;
|
|
154
|
+
}>;
|
|
155
|
+
revokeAllSessions: () => Promise<{
|
|
156
|
+
success: boolean;
|
|
157
|
+
}>;
|
|
158
|
+
getAuthConfig: () => Promise<AuthConfig>;
|
|
159
|
+
getSession: () => RebaseSession | null;
|
|
160
|
+
onAuthStateChange: (callback: (event: AuthChangeEvent, session: RebaseSession | null) => void) => () => boolean;
|
|
161
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Transport } from "./transport";
|
|
2
|
+
import { RebaseWebSocketClient } from "./websocket";
|
|
3
|
+
import { CollectionAccessor } from "@rebasepro/types";
|
|
4
|
+
import { FilterOperator, QueryBuilder } from "./query_builder";
|
|
5
|
+
/**
|
|
6
|
+
* CollectionClient extends `CollectionAccessor` from `@rebasepro/types` so that
|
|
7
|
+
* `client.data` can be passed directly to the core Rebase component.
|
|
8
|
+
*
|
|
9
|
+
* Additionally it exposes fluent query builder methods like `.where()`, `.orderBy()`.
|
|
10
|
+
*/
|
|
11
|
+
export interface CollectionClient<M extends Record<string, unknown> = Record<string, unknown>> extends CollectionAccessor<M> {
|
|
12
|
+
where(column: keyof M & string, operator: FilterOperator, value: unknown): QueryBuilder<M>;
|
|
13
|
+
orderBy(column: keyof M & string, ascending?: "asc" | "desc"): QueryBuilder<M>;
|
|
14
|
+
limit(count: number): QueryBuilder<M>;
|
|
15
|
+
offset(count: number): QueryBuilder<M>;
|
|
16
|
+
search(searchString: string): QueryBuilder<M>;
|
|
17
|
+
include(...relations: string[]): QueryBuilder<M>;
|
|
18
|
+
}
|
|
19
|
+
export declare function createCollectionClient<M extends Record<string, unknown> = Record<string, unknown>>(transport: Transport, slug: string, ws?: RebaseWebSocketClient): CollectionClient<M>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Transport } from "./transport";
|
|
2
|
+
import type { CronJobStatus, CronJobLogEntry } from "@rebasepro/types";
|
|
3
|
+
export interface CreateCronOptions {
|
|
4
|
+
cronPath?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function createCron(transport: Transport, options?: CreateCronOptions): {
|
|
7
|
+
listJobs: () => Promise<{
|
|
8
|
+
jobs: CronJobStatus[];
|
|
9
|
+
}>;
|
|
10
|
+
getJob: (jobId: string) => Promise<{
|
|
11
|
+
job: CronJobStatus;
|
|
12
|
+
}>;
|
|
13
|
+
triggerJob: (jobId: string) => Promise<{
|
|
14
|
+
log: CronJobLogEntry;
|
|
15
|
+
job: CronJobStatus;
|
|
16
|
+
}>;
|
|
17
|
+
getJobLogs: (jobId: string, options?: {
|
|
18
|
+
limit?: number;
|
|
19
|
+
}) => Promise<{
|
|
20
|
+
logs: CronJobLogEntry[];
|
|
21
|
+
}>;
|
|
22
|
+
toggleJob: (jobId: string, enabled: boolean) => Promise<{
|
|
23
|
+
job: CronJobStatus;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { RebaseClientConfig } from "./transport";
|
|
2
|
+
import { createAuth, CreateAuthOptions } from "./auth";
|
|
3
|
+
import { createAdmin, CreateAdminOptions } from "./admin";
|
|
4
|
+
import { createCron, CreateCronOptions } from "./cron";
|
|
5
|
+
import { CollectionClient } from "./collection";
|
|
6
|
+
export * from "./transport";
|
|
7
|
+
export * from "./auth";
|
|
8
|
+
export * from "./admin";
|
|
9
|
+
export * from "./cron";
|
|
10
|
+
export * from "./collection";
|
|
11
|
+
export * from "./websocket";
|
|
12
|
+
export * from "./storage";
|
|
13
|
+
export * from "./reviver";
|
|
14
|
+
export interface CreateRebaseClientOptions extends RebaseClientConfig {
|
|
15
|
+
auth?: CreateAuthOptions;
|
|
16
|
+
admin?: CreateAdminOptions;
|
|
17
|
+
cron?: CreateCronOptions;
|
|
18
|
+
}
|
|
19
|
+
import { RebaseWebSocketClient } from "./websocket";
|
|
20
|
+
import { RebaseClient as BaseRebaseClient, RebaseData, StorageSource } from "@rebasepro/types";
|
|
21
|
+
export type RebaseClient<DB = Record<string, unknown>> = BaseRebaseClient<DB> & {
|
|
22
|
+
setToken: (token: string | null) => void;
|
|
23
|
+
setAuthTokenGetter: (getter: () => Promise<string | null>) => void;
|
|
24
|
+
setOnUnauthorized: (handler: () => Promise<boolean>) => void;
|
|
25
|
+
resolveToken: () => Promise<string | null>;
|
|
26
|
+
auth: ReturnType<typeof createAuth>;
|
|
27
|
+
admin: ReturnType<typeof createAdmin>;
|
|
28
|
+
cron: ReturnType<typeof createCron>;
|
|
29
|
+
ws?: RebaseWebSocketClient;
|
|
30
|
+
storage?: StorageSource;
|
|
31
|
+
call: <T = unknown>(endpoint: string, payload?: unknown) => Promise<T>;
|
|
32
|
+
data: RebaseData & {
|
|
33
|
+
collection<K extends keyof DB>(slug: Extract<K, string>): CollectionClient<DB[K] extends {
|
|
34
|
+
Row: infer R extends Record<string, unknown>;
|
|
35
|
+
} ? R : Record<string, unknown>>;
|
|
36
|
+
} & {
|
|
37
|
+
[K in keyof DB]: CollectionClient<DB[K] extends {
|
|
38
|
+
Row: infer R extends Record<string, unknown>;
|
|
39
|
+
} ? R : Record<string, unknown>>;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export declare function createRebaseClient<DB = Record<string, unknown>>(options: CreateRebaseClientOptions): RebaseClient<DB>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { FindResponse } from "@rebasepro/types";
|
|
2
|
+
import { CollectionClient } from "./collection";
|
|
3
|
+
export type FilterOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "cs" | "csa" | "==" | "!=" | ">" | ">=" | "<" | "<=" | "array-contains" | "array-contains-any" | "not-in";
|
|
4
|
+
export declare class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
5
|
+
private collection;
|
|
6
|
+
private params;
|
|
7
|
+
constructor(collection: CollectionClient<M>);
|
|
8
|
+
/**
|
|
9
|
+
* Add a filter condition to your query.
|
|
10
|
+
* @example
|
|
11
|
+
* client.collection('users').where('age', '>=', 18).find()
|
|
12
|
+
*/
|
|
13
|
+
where(column: keyof M & string, operator: FilterOperator, value: unknown): this;
|
|
14
|
+
/**
|
|
15
|
+
* Order the results by a specific column.
|
|
16
|
+
* @example
|
|
17
|
+
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
18
|
+
*/
|
|
19
|
+
orderBy(column: keyof M & string, ascending?: "asc" | "desc"): this;
|
|
20
|
+
/**
|
|
21
|
+
* Limit the number of results returned.
|
|
22
|
+
*/
|
|
23
|
+
limit(count: number): this;
|
|
24
|
+
/**
|
|
25
|
+
* Skip the first N results.
|
|
26
|
+
*/
|
|
27
|
+
offset(count: number): this;
|
|
28
|
+
/**
|
|
29
|
+
* Set a free-text search string if supported by the backend.
|
|
30
|
+
*/
|
|
31
|
+
search(searchString: string): this;
|
|
32
|
+
/**
|
|
33
|
+
* Include related entities in the response.
|
|
34
|
+
* Relations will be populated with full entity data instead of just IDs.
|
|
35
|
+
*
|
|
36
|
+
* @param relations - Relation names to include, or "*" for all.
|
|
37
|
+
* @example
|
|
38
|
+
* // Include specific relations
|
|
39
|
+
* client.data.posts.include("tags", "author").find()
|
|
40
|
+
*
|
|
41
|
+
* // Include all relations
|
|
42
|
+
* client.data.posts.include("*").find()
|
|
43
|
+
*/
|
|
44
|
+
include(...relations: string[]): this;
|
|
45
|
+
/**
|
|
46
|
+
* Execute the find query and return the results.
|
|
47
|
+
*/
|
|
48
|
+
find(): Promise<FindResponse<M>>;
|
|
49
|
+
/**
|
|
50
|
+
* Listen to realtime updates matching this query.
|
|
51
|
+
*/
|
|
52
|
+
listen(onUpdate: (data: FindResponse<M>) => void, onError?: (error: Error) => void): () => void;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function rebaseReviver(_key: string, value: unknown): unknown;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FindParams as TypesFindParams, FindResponse as TypesFindResponse } from "@rebasepro/types";
|
|
2
|
+
export interface RebaseClientConfig {
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
apiPath?: string;
|
|
6
|
+
fetch?: typeof globalThis.fetch;
|
|
7
|
+
onUnauthorized?: () => Promise<boolean>;
|
|
8
|
+
websocketUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Re-export from `@rebasepro/types` for backward compatibility.
|
|
12
|
+
*/
|
|
13
|
+
export type FindParams = TypesFindParams;
|
|
14
|
+
export type FindResponse<T> = TypesFindResponse<T extends Record<string, unknown> ? T : Record<string, unknown>>;
|
|
15
|
+
export declare class RebaseApiError extends Error {
|
|
16
|
+
status: number;
|
|
17
|
+
code?: string;
|
|
18
|
+
details?: unknown;
|
|
19
|
+
constructor(status: number, message: string, code?: string, details?: unknown);
|
|
20
|
+
}
|
|
21
|
+
export declare function buildQueryString(params?: FindParams): string;
|
|
22
|
+
export interface Transport {
|
|
23
|
+
request: <T = unknown>(path: string, init?: RequestInit) => Promise<T>;
|
|
24
|
+
setToken: (newToken: string | null) => void;
|
|
25
|
+
setAuthTokenGetter: (getter: () => Promise<string | null>) => void;
|
|
26
|
+
setOnUnauthorized: (handler: () => Promise<boolean>) => void;
|
|
27
|
+
readonly baseUrl: string;
|
|
28
|
+
readonly apiPath: string;
|
|
29
|
+
readonly fetchFn: typeof globalThis.fetch;
|
|
30
|
+
getHeaders: (init?: RequestInit) => Record<string, string>;
|
|
31
|
+
resolveToken: () => Promise<string | null>;
|
|
32
|
+
}
|
|
33
|
+
export declare function createTransport(config: RebaseClientConfig): Transport;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { DeleteEntityProps, Entity, EntityCollection, FetchCollectionProps, FetchEntityProps, SaveEntityProps, TableMetadata, BranchInfo } from "@rebasepro/types";
|
|
2
|
+
export interface RebaseWebSocketConfig {
|
|
3
|
+
websocketUrl: string;
|
|
4
|
+
/** Optional auth token getter for WebSocket authentication */
|
|
5
|
+
getAuthToken?: () => Promise<string>;
|
|
6
|
+
/** Optional WebSocket constructor to override globalThis.WebSocket (e.g. for Node environments) */
|
|
7
|
+
WebSocket?: typeof WebSocket;
|
|
8
|
+
}
|
|
9
|
+
export declare class ApiError extends Error {
|
|
10
|
+
code?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
constructor(message: string, error?: string, code?: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class RebaseWebSocketClient {
|
|
15
|
+
private websocketUrl;
|
|
16
|
+
private ws;
|
|
17
|
+
getAuthToken?: () => Promise<string>;
|
|
18
|
+
private subscriptions;
|
|
19
|
+
private listeners;
|
|
20
|
+
on(event: "connect" | "disconnect" | "reconnect" | "error", cb: (...args: unknown[]) => void): () => boolean;
|
|
21
|
+
private emit;
|
|
22
|
+
private collectionSubscriptions;
|
|
23
|
+
private entitySubscriptions;
|
|
24
|
+
private backendToCollectionKey;
|
|
25
|
+
private backendToEntityKey;
|
|
26
|
+
private pendingRequests;
|
|
27
|
+
private reconnectAttempts;
|
|
28
|
+
private maxReconnectAttempts;
|
|
29
|
+
private isConnected;
|
|
30
|
+
private messageQueue;
|
|
31
|
+
private reconnectTimeout;
|
|
32
|
+
private isAuthenticated;
|
|
33
|
+
private authPromise;
|
|
34
|
+
private WebSocketConstructor;
|
|
35
|
+
constructor(config: RebaseWebSocketConfig);
|
|
36
|
+
/**
|
|
37
|
+
* Authenticate the WebSocket connection
|
|
38
|
+
*/
|
|
39
|
+
authenticate(token: string): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Set the auth token getter function
|
|
42
|
+
*/
|
|
43
|
+
setAuthTokenGetter(getAuthToken: () => Promise<string>): void;
|
|
44
|
+
disconnect(): void;
|
|
45
|
+
private initWebSocket;
|
|
46
|
+
private processMessageQueue;
|
|
47
|
+
private attemptReconnect;
|
|
48
|
+
private handleWebSocketMessage;
|
|
49
|
+
private ensureAuthenticated;
|
|
50
|
+
/**
|
|
51
|
+
* Force re-authentication (call after token refresh)
|
|
52
|
+
*/
|
|
53
|
+
reauthenticate(): Promise<void>;
|
|
54
|
+
private sendMessage;
|
|
55
|
+
private doSendMessage;
|
|
56
|
+
fetchCollection<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Entity<M>[]>;
|
|
57
|
+
fetchEntity<M extends Record<string, unknown>>(props: FetchEntityProps<M>): Promise<Entity<M> | undefined>;
|
|
58
|
+
saveEntity<M extends Record<string, unknown>>(props: SaveEntityProps<M>): Promise<Entity<M>>;
|
|
59
|
+
deleteEntity<M extends Record<string, unknown>>(props: DeleteEntityProps<M>): Promise<void>;
|
|
60
|
+
executeSql(sql: string, options?: {
|
|
61
|
+
database?: string;
|
|
62
|
+
role?: string;
|
|
63
|
+
}): Promise<Record<string, unknown>[]>;
|
|
64
|
+
fetchAvailableDatabases(): Promise<string[]>;
|
|
65
|
+
fetchAvailableRoles(): Promise<string[]>;
|
|
66
|
+
fetchCurrentDatabase(): Promise<string | undefined>;
|
|
67
|
+
checkUniqueField(path: string, name: string, value: unknown, entityId?: string, collection?: EntityCollection): Promise<boolean>;
|
|
68
|
+
countEntities<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number>;
|
|
69
|
+
fetchUnmappedTables(mappedPaths?: string[]): Promise<string[]>;
|
|
70
|
+
fetchTableMetadata(tableName: string): Promise<TableMetadata>;
|
|
71
|
+
createBranch(name: string, options?: {
|
|
72
|
+
source?: string;
|
|
73
|
+
}): Promise<BranchInfo>;
|
|
74
|
+
deleteBranch(name: string): Promise<void>;
|
|
75
|
+
listBranches(): Promise<BranchInfo[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Recursively compare two values for structural equality.
|
|
78
|
+
* Handles primitives, null, undefined, Date, RegExp, arrays, and plain objects.
|
|
79
|
+
*/
|
|
80
|
+
private deepEqual;
|
|
81
|
+
private normalizeForComparison;
|
|
82
|
+
/**
|
|
83
|
+
* Merge incoming entities with cached data, preserving cached references
|
|
84
|
+
* for entities whose values haven't changed. This avoids unnecessary
|
|
85
|
+
* React re-renders when the server refetches all entities but most
|
|
86
|
+
* haven't actually changed.
|
|
87
|
+
*/
|
|
88
|
+
private mergeEntities;
|
|
89
|
+
listenCollection<M extends Record<string, unknown>>(props: FetchCollectionProps<M>, onUpdate: (entities: Entity[]) => void, onError?: (error: Error) => void): () => void;
|
|
90
|
+
listenEntity<M extends Record<string, unknown>>(props: FetchEntityProps<M>, onUpdate: (entity: Entity | null) => void, onError?: (error: Error) => void): () => void;
|
|
91
|
+
/**
|
|
92
|
+
* Re-send all active subscriptions to the backend after a reconnect.
|
|
93
|
+
* The server wipes subscription state when a client disconnects, so
|
|
94
|
+
* we need to re-register everything to resume receiving updates.
|
|
95
|
+
*/
|
|
96
|
+
private resubscribeAll;
|
|
97
|
+
private createCollectionSubscriptionKey;
|
|
98
|
+
private createEntitySubscriptionKey;
|
|
99
|
+
}
|
package/dist/index.es.js
CHANGED
|
@@ -121,23 +121,34 @@ function usePostgresClientDriver(config) {
|
|
|
121
121
|
isFilterCombinationValid() {
|
|
122
122
|
return true;
|
|
123
123
|
},
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
124
|
+
admin: {
|
|
125
|
+
executeSql(sql, options) {
|
|
126
|
+
return client.executeSql(sql, options);
|
|
127
|
+
},
|
|
128
|
+
fetchAvailableDatabases() {
|
|
129
|
+
return client.fetchAvailableDatabases();
|
|
130
|
+
},
|
|
131
|
+
fetchAvailableRoles() {
|
|
132
|
+
return client.fetchAvailableRoles();
|
|
133
|
+
},
|
|
134
|
+
fetchCurrentDatabase() {
|
|
135
|
+
return client.fetchCurrentDatabase();
|
|
136
|
+
},
|
|
137
|
+
fetchUnmappedTables(mappedPaths) {
|
|
138
|
+
return client.fetchUnmappedTables(mappedPaths);
|
|
139
|
+
},
|
|
140
|
+
fetchTableMetadata(tableName) {
|
|
141
|
+
return client.fetchTableMetadata(tableName);
|
|
142
|
+
},
|
|
143
|
+
createBranch(name_0, options_0) {
|
|
144
|
+
return client.createBranch(name_0, options_0);
|
|
145
|
+
},
|
|
146
|
+
deleteBranch(name_1) {
|
|
147
|
+
return client.deleteBranch(name_1);
|
|
148
|
+
},
|
|
149
|
+
listBranches() {
|
|
150
|
+
return client.listBranches();
|
|
151
|
+
}
|
|
141
152
|
}
|
|
142
153
|
};
|
|
143
154
|
$[0] = client;
|
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/usePostgresClientDriver.ts"],"sourcesContent":["import { useMemo, useEffect } from \"react\";\nimport {\n DataDriver,\n DeleteEntityProps,\n Entity,\n EntityCollection,\n EntityReference, EntityRelation,\n FetchCollectionProps,\n FetchEntityProps,\n ListenCollectionProps,\n ListenEntityProps,\n SaveEntityProps,\n
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/usePostgresClientDriver.ts"],"sourcesContent":["import { useMemo, useEffect } from \"react\";\nimport {\n DataDriver,\n DeleteEntityProps,\n Entity,\n EntityCollection,\n EntityReference, EntityRelation,\n FetchCollectionProps,\n FetchEntityProps,\n ListenCollectionProps,\n ListenEntityProps,\n SaveEntityProps,\n BranchInfo\n} from \"@rebasepro/types\";\nimport { RebaseWebSocketClient } from \"@rebasepro/client\";\n\nexport interface PostgresDataDriverConfig {\n wsClient?: RebaseWebSocketClient;\n}\n\nexport interface PostgresDataDriver extends DataDriver {\n client?: RebaseWebSocketClient;\n}\n\n\nexport function usePostgresClientDriver(config: PostgresDataDriverConfig): PostgresDataDriver {\n const client = config.wsClient;\n\n return useMemo(() => {\n if (!client) throw new Error(\"RebaseWebSocketClient must be provided in config.wsClient\");\n\n return {\n\n key: \"postgres\",\n\n name: \"PostgreSQL\",\n\n client,\n\n async fetchCollection<M extends Record<string, any>>(props: FetchCollectionProps<M>): Promise<Entity<M>[]> {\n // Pick only the fields the client needs, ignoring extra fields from the CMS layer\n const { path, filter, limit, startAfter, orderBy, searchString, order } = props;\n return client.fetchCollection({ path,\nfilter,\nlimit,\nstartAfter,\norderBy,\nsearchString,\norder }) as Promise<Entity<M>[]>;\n },\n\n async fetchEntity<M extends Record<string, any>>(props: FetchEntityProps<M>): Promise<Entity<M> | undefined> {\n const { path, entityId, databaseId } = props;\n return client.fetchEntity({ path,\nentityId,\ndatabaseId }) as Promise<Entity<M> | undefined>;\n },\n\n async saveEntity<M extends Record<string, any>>(props: SaveEntityProps<M>): Promise<Entity<M>> {\n return client.saveEntity({\n path: props.path,\n values: props.values,\n entityId: props.entityId,\n previousValues: props.previousValues,\n status: props.status\n }) as Promise<Entity<M>>;\n },\n\n async deleteEntity<M extends Record<string, any>>(props: DeleteEntityProps<M>): Promise<void> {\n const { entity } = props;\n return client.deleteEntity({ entity });\n },\n\n async checkUniqueField(path: string, name: string, value: any, entityId?: string, collection?: EntityCollection): Promise<boolean> {\n return client.checkUniqueField(path, name, value, entityId, collection);\n },\n\n async countEntities<M extends Record<string, any>>(props: FetchCollectionProps<M>): Promise<number> {\n const { path, filter, limit, startAfter, orderBy, searchString, order } = props;\n return client.countEntities({ path,\nfilter,\nlimit,\nstartAfter,\norderBy,\nsearchString,\norder });\n },\n\n listenCollection<M extends Record<string, any>>(props: ListenCollectionProps<M>): () => void {\n const { path, filter, limit, startAfter, orderBy, searchString, order, onUpdate, onError } = props;\n return client.listenCollection(\n { path,\nfilter,\nlimit,\nstartAfter,\norderBy,\nsearchString,\norder },\n (entities: Entity[]) => props.onUpdate(entities as Entity<M>[]),\n props.onError\n );\n },\n\n listenEntity<M extends Record<string, any>>(props: ListenEntityProps<M>): () => void {\n const { path, entityId, databaseId, onUpdate, onError } = props;\n return client.listenEntity(\n { path,\nentityId,\ndatabaseId },\n (entity: Entity | null) => {\n props.onUpdate(entity as Entity<M> | null);\n },\n props.onError\n );\n },\n\n isFilterCombinationValid(): boolean {\n return true; // PostgreSQL supports complex filter combinations\n },\n\n admin: {\n executeSql(sql: string, options?: { database?: string; role?: string }): Promise<Record<string, unknown>[]> {\n return client.executeSql(sql, options);\n },\n fetchAvailableDatabases(): Promise<string[]> {\n return client.fetchAvailableDatabases();\n },\n fetchAvailableRoles(): Promise<string[]> {\n return client.fetchAvailableRoles();\n },\n fetchCurrentDatabase(): Promise<string | undefined> {\n return client.fetchCurrentDatabase();\n },\n fetchUnmappedTables(mappedPaths?: string[]): Promise<string[]> {\n return client.fetchUnmappedTables(mappedPaths);\n },\n fetchTableMetadata(tableName: string): Promise<unknown> {\n return client.fetchTableMetadata(tableName);\n },\n createBranch(name: string, options?: { source?: string }): Promise<BranchInfo> {\n return client.createBranch(name, options);\n },\n deleteBranch(name: string): Promise<void> {\n return client.deleteBranch(name);\n },\n listBranches(): Promise<BranchInfo[]> {\n return client.listBranches();\n }\n }\n } as PostgresDataDriver;\n }, [client]);\n\n}\n"],"names":["usePostgresClientDriver","config","$","_c","client","wsClient","t0","Error","t1","key","name","fetchCollection","props","path","filter","limit","startAfter","orderBy","searchString","order","fetchEntity","props_0","path_0","entityId","databaseId","saveEntity","props_1","values","previousValues","status","deleteEntity","props_2","entity","checkUniqueField","path_1","value","entityId_0","collection","countEntities","props_3","path_2","filter_0","limit_0","startAfter_0","orderBy_0","searchString_0","order_0","listenCollection","props_4","path_3","filter_1","limit_1","startAfter_1","orderBy_1","searchString_1","order_1","entities","onUpdate","onError","listenEntity","props_5","path_4","entityId_1","databaseId_0","entity_0","isFilterCombinationValid","admin","executeSql","sql","options","fetchAvailableDatabases","fetchAvailableRoles","fetchCurrentDatabase","fetchUnmappedTables","mappedPaths","fetchTableMetadata","tableName","createBranch","name_0","options_0","deleteBranch","name_1","listBranches"],"mappings":"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"}
|