@rebasepro/client 0.1.2 → 0.2.3
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 +21 -6
- package/dist/auth.d.ts +8 -6
- package/dist/collection.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +5 -113
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +14 -121
- package/dist/index.umd.js.map +1 -1
- package/dist/query_builder.d.ts +1 -53
- package/package.json +9 -9
- package/src/auth.ts +4 -8
- package/src/collection.ts +2 -2
- package/src/index.ts +2 -1
- package/src/query_builder.ts +1 -125
- package/src/transport.ts +2 -2
package/dist/query_builder.d.ts
CHANGED
|
@@ -1,53 +1 @@
|
|
|
1
|
-
|
|
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
|
-
}
|
|
1
|
+
export { QueryBuilder } from "@rebasepro/common";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"description": "HTTP SDK client for the Rebase custom backend",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -23,27 +23,27 @@
|
|
|
23
23
|
"exports": {
|
|
24
24
|
".": {
|
|
25
25
|
"types": "./dist/index.d.ts",
|
|
26
|
-
"development": "./
|
|
26
|
+
"development": "./dist/index.es.js",
|
|
27
27
|
"import": "./dist/index.es.js",
|
|
28
28
|
"require": "./dist/index.umd.cjs"
|
|
29
29
|
},
|
|
30
30
|
"./package.json": "./package.json"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@rebasepro/
|
|
34
|
-
"@rebasepro/utils": "0.
|
|
33
|
+
"@rebasepro/common": "0.2.3",
|
|
34
|
+
"@rebasepro/utils": "0.2.3",
|
|
35
|
+
"@rebasepro/types": "0.2.3"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@jest/globals": "^29.7.0",
|
|
38
39
|
"@types/jest": "^29.5.14",
|
|
39
|
-
"@types/node": "^20.
|
|
40
|
+
"@types/node": "^20.19.41",
|
|
40
41
|
"cross-env": "^7.0.3",
|
|
41
42
|
"jest": "^29.7.0",
|
|
42
|
-
"
|
|
43
|
-
"ts-jest": "^29.3.1",
|
|
43
|
+
"ts-jest": "^29.4.10",
|
|
44
44
|
"tsd": "^0.31.2",
|
|
45
|
-
"typescript": "^5.
|
|
46
|
-
"vite": "^5.4.
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vite": "^5.4.21"
|
|
47
47
|
},
|
|
48
48
|
"files": [
|
|
49
49
|
"dist",
|
package/src/auth.ts
CHANGED
|
@@ -29,8 +29,8 @@ export type AuthChangeEvent = "SIGNED_IN" | "SIGNED_OUT" | "TOKEN_REFRESHED" | "
|
|
|
29
29
|
export interface AuthConfig {
|
|
30
30
|
needsSetup: boolean;
|
|
31
31
|
registrationEnabled: boolean;
|
|
32
|
-
googleEnabled: boolean;
|
|
33
32
|
emailServiceEnabled: boolean;
|
|
33
|
+
enabledProviders: string[];
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export interface AuthStorage {
|
|
@@ -196,23 +196,19 @@ refreshToken: session.refreshToken };
|
|
|
196
196
|
/**
|
|
197
197
|
* Sign in with Google.
|
|
198
198
|
*
|
|
199
|
-
* Supports
|
|
199
|
+
* Supports three invocation styles:
|
|
200
200
|
* - `signInWithGoogle({ idToken })` — ID-token flow (One Tap / Sign In button)
|
|
201
201
|
* - `signInWithGoogle({ accessToken })` — Access-token flow (popup)
|
|
202
202
|
* - `signInWithGoogle({ code, redirectUri })` — Authorization code flow (most secure)
|
|
203
|
-
* - `signInWithGoogle(idToken)` — Legacy shorthand for ID-token flow
|
|
204
203
|
*/
|
|
205
204
|
async function signInWithGoogle(
|
|
206
|
-
|
|
205
|
+
payload: { idToken: string } | { accessToken: string } | { code: string; redirectUri: string }
|
|
207
206
|
) {
|
|
208
207
|
const fetchFn = getFetch();
|
|
209
|
-
const body = typeof tokenOrPayload === "string"
|
|
210
|
-
? { idToken: tokenOrPayload }
|
|
211
|
-
: tokenOrPayload;
|
|
212
208
|
const res = await fetchFn(authUrl("/google"), {
|
|
213
209
|
method: "POST",
|
|
214
210
|
headers: { "Content-Type": "application/json" },
|
|
215
|
-
body: JSON.stringify(
|
|
211
|
+
body: JSON.stringify(payload)
|
|
216
212
|
});
|
|
217
213
|
const responseBody = await res.json().catch(() => ({}));
|
|
218
214
|
if (!res.ok) throwApiError(res.status, responseBody, res.statusText);
|
package/src/collection.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Transport, FindParams, buildQueryString } from "./transport";
|
|
2
2
|
import { RebaseWebSocketClient } from "./websocket";
|
|
3
|
-
import { Entity, FilterValues, WhereFilterOp, CollectionAccessor, WhereFieldValue, FindResponse } from "@rebasepro/types";
|
|
3
|
+
import { Entity, FilterValues, WhereFilterOp, CollectionAccessor, WhereFieldValue, FindResponse, FilterOperator } from "@rebasepro/types";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { QueryBuilder } from "./query_builder";
|
|
6
6
|
|
|
7
7
|
function parseWhereFilter(where?: Record<string, WhereFieldValue>): FilterValues<string> | undefined {
|
|
8
8
|
if (!where) return undefined;
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./auth";
|
|
|
11
11
|
export * from "./admin";
|
|
12
12
|
export * from "./cron";
|
|
13
13
|
export * from "./collection";
|
|
14
|
+
export * from "./query_builder";
|
|
14
15
|
export * from "./websocket";
|
|
15
16
|
export * from "./storage";
|
|
16
17
|
export * from "./reviver";
|
|
@@ -163,7 +164,7 @@ export function createRebaseClient<DB = Record<string, unknown>>(options: Create
|
|
|
163
164
|
method: "POST",
|
|
164
165
|
body: payload ? JSON.stringify(payload) : undefined
|
|
165
166
|
});
|
|
166
|
-
return res.data ?? (res as
|
|
167
|
+
return res.data ?? (res as T);
|
|
167
168
|
},
|
|
168
169
|
data: dataProxy,
|
|
169
170
|
email: undefined
|
package/src/query_builder.ts
CHANGED
|
@@ -1,125 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { CollectionClient } from "./collection";
|
|
3
|
-
|
|
4
|
-
export type FilterOperator =
|
|
5
|
-
| "eq" | "neq" | "gt" | "gte" | "lt" | "lte"
|
|
6
|
-
| "in" | "nin" | "cs" | "csa"
|
|
7
|
-
| "==" | "!=" | ">" | ">=" | "<" | "<="
|
|
8
|
-
| "array-contains" | "array-contains-any"
|
|
9
|
-
| "not-in";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Maps standard operators to Rebase backend's string-based operators
|
|
13
|
-
*/
|
|
14
|
-
function mapOperator(op: FilterOperator): string {
|
|
15
|
-
switch (op) {
|
|
16
|
-
case "==": return "eq";
|
|
17
|
-
case "!=": return "neq";
|
|
18
|
-
case ">": return "gt";
|
|
19
|
-
case ">=": return "gte";
|
|
20
|
-
case "<": return "lt";
|
|
21
|
-
case "<=": return "lte";
|
|
22
|
-
case "array-contains": return "cs";
|
|
23
|
-
case "array-contains-any": return "csa";
|
|
24
|
-
case "not-in": return "nin";
|
|
25
|
-
default: return op;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
30
|
-
private params: FindParams = { where: {} };
|
|
31
|
-
|
|
32
|
-
constructor(private collection: CollectionClient<M>) {}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Add a filter condition to your query.
|
|
36
|
-
* @example
|
|
37
|
-
* client.collection('users').where('age', '>=', 18).find()
|
|
38
|
-
*/
|
|
39
|
-
where(column: keyof M & string, operator: FilterOperator, value: unknown): this {
|
|
40
|
-
if (!this.params.where) {
|
|
41
|
-
this.params.where = {};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const mappedOp = mapOperator(operator);
|
|
45
|
-
let formattedValue = value;
|
|
46
|
-
|
|
47
|
-
// Handle arrays for in, nin, cs, csa
|
|
48
|
-
if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
|
|
49
|
-
formattedValue = `(${value.join(",")})`;
|
|
50
|
-
} else if (value === null) {
|
|
51
|
-
formattedValue = "null";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
55
|
-
return this;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Order the results by a specific column.
|
|
60
|
-
* @example
|
|
61
|
-
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
62
|
-
*/
|
|
63
|
-
orderBy(column: keyof M & string, ascending: "asc" | "desc" = "asc"): this {
|
|
64
|
-
this.params.orderBy = `${column}:${ascending}`;
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Limit the number of results returned.
|
|
70
|
-
*/
|
|
71
|
-
limit(count: number): this {
|
|
72
|
-
this.params.limit = count;
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Skip the first N results.
|
|
78
|
-
*/
|
|
79
|
-
offset(count: number): this {
|
|
80
|
-
this.params.offset = count;
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Set a free-text search string if supported by the backend.
|
|
86
|
-
*/
|
|
87
|
-
search(searchString: string): this {
|
|
88
|
-
this.params.searchString = searchString;
|
|
89
|
-
return this;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Include related entities in the response.
|
|
94
|
-
* Relations will be populated with full entity data instead of just IDs.
|
|
95
|
-
*
|
|
96
|
-
* @param relations - Relation names to include, or "*" for all.
|
|
97
|
-
* @example
|
|
98
|
-
* // Include specific relations
|
|
99
|
-
* client.data.posts.include("tags", "author").find()
|
|
100
|
-
*
|
|
101
|
-
* // Include all relations
|
|
102
|
-
* client.data.posts.include("*").find()
|
|
103
|
-
*/
|
|
104
|
-
include(...relations: string[]): this {
|
|
105
|
-
this.params.include = relations;
|
|
106
|
-
return this;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Execute the find query and return the results.
|
|
111
|
-
*/
|
|
112
|
-
async find(): Promise<FindResponse<M>> {
|
|
113
|
-
return this.collection.find(this.params) as Promise<FindResponse<M>>;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Listen to realtime updates matching this query.
|
|
118
|
-
*/
|
|
119
|
-
listen(onUpdate: (data: FindResponse<M>) => void, onError?: (error: Error) => void): () => void {
|
|
120
|
-
if (!this.collection.listen) {
|
|
121
|
-
throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
|
|
122
|
-
}
|
|
123
|
-
return this.collection.listen(this.params, onUpdate, onError);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
1
|
+
export { QueryBuilder } from "@rebasepro/common";
|
package/src/transport.ts
CHANGED
|
@@ -166,7 +166,7 @@ export function createTransport(config: RebaseClientConfig): Transport {
|
|
|
166
166
|
const res = await fetchFn(url, { ...init,
|
|
167
167
|
headers });
|
|
168
168
|
|
|
169
|
-
if (res.status === 204) return undefined as
|
|
169
|
+
if (res.status === 204) return undefined as T; // SAFETY: HTTP 204 No Content has no body
|
|
170
170
|
|
|
171
171
|
const text = await res.text().catch(() => "");
|
|
172
172
|
let body: any = {};
|
|
@@ -193,7 +193,7 @@ headers });
|
|
|
193
193
|
const retryHeaders = getHeaders(retryToken, init) as Record<string, string>;
|
|
194
194
|
const retryRes = await fetchFn(url, { ...init,
|
|
195
195
|
headers: retryHeaders });
|
|
196
|
-
if (retryRes.status === 204) return undefined as
|
|
196
|
+
if (retryRes.status === 204) return undefined as T; // SAFETY: HTTP 204 No Content has no body
|
|
197
197
|
const retryText = await retryRes.text().catch(() => "");
|
|
198
198
|
let retryBody: any = {};
|
|
199
199
|
if (retryText) {
|