@rebasepro/client 0.0.1-canary.eae7889 → 0.1.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/dist/auth.d.ts CHANGED
@@ -50,7 +50,12 @@ export declare function createAuth(transport: Transport, options?: CreateAuthOpt
50
50
  accessToken: string;
51
51
  refreshToken: string;
52
52
  }>;
53
- signInWithGoogle: (idToken: string) => Promise<{
53
+ signInWithGoogle: (tokenOrPayload: string | {
54
+ idToken?: string;
55
+ accessToken?: string;
56
+ code?: string;
57
+ redirectUri?: string;
58
+ }) => Promise<{
54
59
  user: RebaseUser;
55
60
  accessToken: string;
56
61
  refreshToken: string;
@@ -1,4 +1,4 @@
1
- import { Transport } from "./transport";
1
+ import { Transport, FindParams } from "./transport";
2
2
  import { RebaseWebSocketClient } from "./websocket";
3
3
  import { CollectionAccessor } from "@rebasepro/types";
4
4
  import { FilterOperator, QueryBuilder } from "./query_builder";
@@ -15,5 +15,6 @@ export interface CollectionClient<M extends Record<string, unknown> = Record<str
15
15
  offset(count: number): QueryBuilder<M>;
16
16
  search(searchString: string): QueryBuilder<M>;
17
17
  include(...relations: string[]): QueryBuilder<M>;
18
+ count(params?: FindParams): Promise<number>;
18
19
  }
19
20
  export declare function createCollectionClient<M extends Record<string, unknown> = Record<string, unknown>>(transport: Transport, slug: string, ws?: RebaseWebSocketClient): CollectionClient<M>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ import type { Transport } from "./transport";
2
+ /**
3
+ * Client interface for invoking custom backend functions.
4
+ *
5
+ * Custom functions are Hono route files auto-mounted by the Rebase backend
6
+ * at `/api/functions/{name}`. The `FunctionsClient` wraps the shared
7
+ * transport so callers never need to manually construct URLs or inject
8
+ * auth tokens.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const result = await client.functions.invoke<{ job: Job }>('extract-job', {
13
+ * url: 'https://example.com/posting',
14
+ * html: htmlContent,
15
+ * });
16
+ * ```
17
+ */
18
+ export interface FunctionsClient {
19
+ /**
20
+ * Invoke a custom backend function by name.
21
+ *
22
+ * @typeParam T - Expected shape of the response payload.
23
+ * @param name - Function name (the filename without extension, e.g. `"extract-job"`).
24
+ * @param payload - Optional JSON-serialisable body sent as `POST`.
25
+ * @param options - Optional overrides (HTTP method, sub-path, extra headers).
26
+ * @returns The parsed JSON response from the function.
27
+ */
28
+ invoke<T = unknown>(name: string, payload?: unknown, options?: FunctionInvokeOptions): Promise<T>;
29
+ }
30
+ export interface FunctionInvokeOptions {
31
+ /** HTTP method — defaults to `"POST"`. */
32
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
33
+ /** Sub-path appended after the function name, e.g. `"status/123"`. */
34
+ path?: string;
35
+ /** Extra headers merged into the request (auth is still injected automatically). */
36
+ headers?: Record<string, string>;
37
+ }
38
+ /**
39
+ * Create a `FunctionsClient` backed by the given transport.
40
+ *
41
+ * The transport already handles:
42
+ * - Base URL resolution
43
+ * - JWT injection via `Authorization: Bearer`
44
+ * - 401 retry / `onUnauthorized` flow
45
+ * - Consistent error throwing via `RebaseApiError`
46
+ *
47
+ * @internal
48
+ */
49
+ export declare function createFunctionsClient(transport: Transport): FunctionsClient;
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { createAuth, CreateAuthOptions } from "./auth";
3
3
  import { createAdmin, CreateAdminOptions } from "./admin";
4
4
  import { createCron, CreateCronOptions } from "./cron";
5
5
  import { CollectionClient } from "./collection";
6
+ import type { FunctionsClient } from "./functions";
6
7
  export * from "./transport";
7
8
  export * from "./auth";
8
9
  export * from "./admin";
@@ -11,6 +12,7 @@ export * from "./collection";
11
12
  export * from "./websocket";
12
13
  export * from "./storage";
13
14
  export * from "./reviver";
15
+ export * from "./functions";
14
16
  export interface CreateRebaseClientOptions extends RebaseClientConfig {
15
17
  auth?: CreateAuthOptions;
16
18
  admin?: CreateAdminOptions;
@@ -26,6 +28,7 @@ export type RebaseClient<DB = Record<string, unknown>> = BaseRebaseClient<DB> &
26
28
  auth: ReturnType<typeof createAuth>;
27
29
  admin: ReturnType<typeof createAdmin>;
28
30
  cron: ReturnType<typeof createCron>;
31
+ functions: FunctionsClient;
29
32
  ws?: RebaseWebSocketClient;
30
33
  storage?: StorageSource;
31
34
  call: <T = unknown>(endpoint: string, payload?: unknown) => Promise<T>;
package/dist/index.es.js CHANGED
@@ -381,21 +381,18 @@ function createAuth(transport, options) {
381
381
  refreshToken: session.refreshToken
382
382
  };
383
383
  }
384
- async function signInWithGoogle(idToken) {
384
+ async function signInWithGoogle(tokenOrPayload) {
385
385
  const fetchFn = getFetch();
386
+ const body = typeof tokenOrPayload === "string" ? { idToken: tokenOrPayload } : tokenOrPayload;
386
387
  const res = await fetchFn(authUrl("/google"), {
387
388
  method: "POST",
388
389
  headers: { "Content-Type": "application/json" },
389
- body: JSON.stringify({ idToken })
390
+ body: JSON.stringify(body)
390
391
  });
391
- const body = await res.json().catch(() => ({}));
392
- if (!res.ok) throwApiError(res.status, body, res.statusText);
393
- const session = handleAuthResponse(body, "SIGNED_IN");
394
- return {
395
- user: session.user,
396
- accessToken: session.accessToken,
397
- refreshToken: session.refreshToken
398
- };
392
+ const responseBody = await res.json().catch(() => ({}));
393
+ if (!res.ok) throwApiError(res.status, responseBody, res.statusText);
394
+ const session = handleAuthResponse(responseBody, "SIGNED_IN");
395
+ return { user: session.user, accessToken: session.accessToken, refreshToken: session.refreshToken };
399
396
  }
400
397
  async function signInWithLinkedin(code, redirectUri) {
401
398
  const fetchFn = getFetch();
@@ -1077,6 +1074,12 @@ function createCollectionClient(transport, slug, ws) {
1077
1074
  method: "DELETE"
1078
1075
  });
1079
1076
  },
1077
+ async count(params) {
1078
+ const countParams = { ...params, limit: void 0, offset: void 0 };
1079
+ const qs = buildQueryString(countParams);
1080
+ const raw = await transport.request(basePath + "/count" + qs, { method: "GET" });
1081
+ return raw.count ?? 0;
1082
+ },
1080
1083
  // Fluent builder instantiation
1081
1084
  where(column, operator, value) {
1082
1085
  return new QueryBuilder(client).where(column, operator, value);
@@ -1143,6 +1146,23 @@ function createCollectionClient(transport, slug, ws) {
1143
1146
  }
1144
1147
  return client;
1145
1148
  }
1149
+ function createFunctionsClient(transport) {
1150
+ return {
1151
+ async invoke(name, payload, options) {
1152
+ const method = options?.method ?? "POST";
1153
+ const subPath = options?.path ? `/${options.path.replace(/^\//, "")}` : "";
1154
+ const routePath = `/functions/${encodeURIComponent(name)}${subPath}`;
1155
+ const init = { method };
1156
+ if (payload !== void 0 && method !== "GET") {
1157
+ init.body = JSON.stringify(payload);
1158
+ }
1159
+ if (options?.headers) {
1160
+ init.headers = options.headers;
1161
+ }
1162
+ return transport.request(routePath, init);
1163
+ }
1164
+ };
1165
+ }
1146
1166
  function rehydrateEntity(entity) {
1147
1167
  return entity;
1148
1168
  }
@@ -1249,16 +1269,16 @@ class RebaseWebSocketClient {
1249
1269
  setAuthTokenGetter(getAuthToken) {
1250
1270
  this.getAuthToken = getAuthToken;
1251
1271
  if (this.isConnected && !this.isAuthenticated && !this.authPromise) {
1252
- console.log("WebSocket auto-authenticating after token getter set");
1272
+ console.debug("WebSocket auto-authenticating after token getter set");
1253
1273
  this.getAuthToken().then((token) => {
1254
1274
  if (!this.ws) return;
1255
1275
  if (token) {
1256
1276
  this.authenticate(token).catch((e) => {
1257
- if (this.ws) console.warn("WebSocket auto-auth failed:", e);
1277
+ if (this.ws) console.debug("WebSocket auto-auth skipped:", e?.message || e);
1258
1278
  });
1259
1279
  }
1260
1280
  }).catch((e) => {
1261
- if (this.ws) console.warn("WebSocket auto-auth failed:", e);
1281
+ if (this.ws) console.debug("WebSocket auto-auth skipped:", e?.message || e);
1262
1282
  });
1263
1283
  }
1264
1284
  }
@@ -1285,7 +1305,7 @@ class RebaseWebSocketClient {
1285
1305
  try {
1286
1306
  this.ws = new this.WebSocketConstructor(this.websocketUrl);
1287
1307
  this.ws.onopen = async () => {
1288
- console.log("Connected to PostgreSQL backend");
1308
+ console.debug("Connected to PostgreSQL backend");
1289
1309
  const wasReconnect = this.reconnectAttempts > 0;
1290
1310
  this.isConnected = true;
1291
1311
  this.reconnectAttempts = 0;
@@ -1294,10 +1314,10 @@ class RebaseWebSocketClient {
1294
1314
  const token = await this.getAuthToken();
1295
1315
  if (token) {
1296
1316
  await this.authenticate(token);
1297
- console.log("WebSocket auto-authenticated");
1317
+ console.debug("WebSocket auto-authenticated");
1298
1318
  }
1299
1319
  } catch (error) {
1300
- console.warn("WebSocket auto-auth failed, requests may fail:", error);
1320
+ console.debug("WebSocket connected without auth:", error?.message || error);
1301
1321
  }
1302
1322
  }
1303
1323
  this.emit(wasReconnect ? "reconnect" : "connect");
@@ -1315,7 +1335,7 @@ class RebaseWebSocketClient {
1315
1335
  }
1316
1336
  };
1317
1337
  this.ws.onclose = () => {
1318
- console.log("Disconnected from PostgreSQL backend");
1338
+ console.debug("Disconnected from PostgreSQL backend");
1319
1339
  this.isConnected = false;
1320
1340
  this.isAuthenticated = false;
1321
1341
  this.authPromise = null;
@@ -1357,7 +1377,7 @@ class RebaseWebSocketClient {
1357
1377
  }
1358
1378
  this.reconnectAttempts++;
1359
1379
  const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
1360
- console.log(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);
1380
+ console.debug(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);
1361
1381
  if (this.reconnectTimeout) {
1362
1382
  clearTimeout(this.reconnectTimeout);
1363
1383
  }
@@ -1527,7 +1547,7 @@ class RebaseWebSocketClient {
1527
1547
  this.authPromise = this.authenticate(token);
1528
1548
  await this.authPromise;
1529
1549
  this.authPromise = null;
1530
- console.log("WebSocket authenticated on demand");
1550
+ console.debug("WebSocket authenticated on demand");
1531
1551
  return;
1532
1552
  } catch (error) {
1533
1553
  this.authPromise = null;
@@ -1546,7 +1566,7 @@ class RebaseWebSocketClient {
1546
1566
  }
1547
1567
  if (attempt < retryCount - 1) {
1548
1568
  const delay = Math.min(1e3 * (attempt + 1), 3e3);
1549
- console.log(`WebSocket auth attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
1569
+ console.debug(`WebSocket auth attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
1550
1570
  await new Promise((resolve) => setTimeout(resolve, delay));
1551
1571
  }
1552
1572
  }
@@ -1563,7 +1583,7 @@ class RebaseWebSocketClient {
1563
1583
  try {
1564
1584
  const token = await this.getAuthToken();
1565
1585
  await this.authenticate(token);
1566
- console.log("WebSocket reauthenticated successfully");
1586
+ console.debug("WebSocket reauthenticated successfully");
1567
1587
  } catch (error) {
1568
1588
  console.error("WebSocket reauthentication failed:", error);
1569
1589
  throw error;
@@ -1821,7 +1841,7 @@ class RebaseWebSocketClient {
1821
1841
  };
1822
1842
  }
1823
1843
  }
1824
- console.log(`[RebaseWS] Row ${incomingEntity.id} refetch mismatch:
1844
+ console.debug(`[RebaseWS] Row ${incomingEntity.id} refetch mismatch:
1825
1845
  `, JSON.stringify(mismatches, null, 2));
1826
1846
  }
1827
1847
  }
@@ -1981,7 +2001,7 @@ class RebaseWebSocketClient {
1981
2001
  * we need to re-register everything to resume receiving updates.
1982
2002
  */
1983
2003
  resubscribeAll() {
1984
- console.log(`[WS] Re-subscribing: ${this.collectionSubscriptions.size} collection(s), ${this.entitySubscriptions.size} entity(ies)`);
2004
+ console.debug(`[WS] Re-subscribing: ${this.collectionSubscriptions.size} collection(s), ${this.entitySubscriptions.size} entity(ies)`);
1985
2005
  for (const [key, sub] of this.collectionSubscriptions.entries()) {
1986
2006
  const oldBackendId = sub.backendSubscriptionId;
1987
2007
  const newBackendId = `collection_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -2182,6 +2202,7 @@ function createRebaseClient(options) {
2182
2202
  const admin = createAdmin(transport, options.admin);
2183
2203
  const cron = createCron(transport, options.cron);
2184
2204
  const storage = createStorage(transport);
2205
+ const functions = createFunctionsClient(transport);
2185
2206
  const resolvedWsUrl = options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl);
2186
2207
  let ws;
2187
2208
  if (resolvedWsUrl) {
@@ -2238,6 +2259,7 @@ function createRebaseClient(options) {
2238
2259
  auth,
2239
2260
  admin,
2240
2261
  cron,
2262
+ functions,
2241
2263
  storage,
2242
2264
  ws,
2243
2265
  setToken: transport.setToken,
@@ -2268,6 +2290,7 @@ export {
2268
2290
  createAuth,
2269
2291
  createCollectionClient,
2270
2292
  createCron,
2293
+ createFunctionsClient,
2271
2294
  createMemoryStorage,
2272
2295
  createRebaseClient,
2273
2296
  createStorage,