@rebasepro/client 0.4.0 → 0.6.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.
@@ -2,7 +2,7 @@ import { DeleteEntityProps, Entity, EntityCollection, FetchCollectionProps, Fetc
2
2
  export interface RebaseWebSocketConfig {
3
3
  websocketUrl: string;
4
4
  /** Optional auth token getter for WebSocket authentication */
5
- getAuthToken?: () => Promise<string>;
5
+ getAuthToken?: () => Promise<string | null>;
6
6
  /** Optional WebSocket constructor to override globalThis.WebSocket (e.g. for Node environments) */
7
7
  WebSocket?: typeof WebSocket;
8
8
  /** Callback to handle unauthorized requests or token expiration (refreshes auth session) */
@@ -16,7 +16,7 @@ export declare class ApiError extends Error {
16
16
  export declare class RebaseWebSocketClient {
17
17
  private websocketUrl;
18
18
  private ws;
19
- getAuthToken?: () => Promise<string>;
19
+ getAuthToken?: () => Promise<string | null>;
20
20
  private subscriptions;
21
21
  private listeners;
22
22
  on(event: "connect" | "disconnect" | "reconnect" | "error", cb: (...args: unknown[]) => void): () => boolean;
@@ -30,6 +30,7 @@ export declare class RebaseWebSocketClient {
30
30
  private maxReconnectAttempts;
31
31
  private isConnected;
32
32
  private messageQueue;
33
+ private requestTimeoutMs;
33
34
  private reconnectTimeout;
34
35
  private isAuthenticated;
35
36
  private authPromise;
@@ -44,18 +45,20 @@ export declare class RebaseWebSocketClient {
44
45
  /**
45
46
  * Set the auth token getter function
46
47
  */
47
- setAuthTokenGetter(getAuthToken: () => Promise<string>): void;
48
+ setAuthTokenGetter(getAuthToken: () => Promise<string | null>): void;
48
49
  disconnect(): void;
49
50
  private initWebSocket;
50
51
  private processMessageQueue;
51
52
  private attemptReconnect;
52
53
  private isAuthError;
53
54
  private handleAuthFailure;
54
- private handleWebSocketMessage;
55
- private ensureAuthenticated;
56
55
  /**
57
- * Force re-authentication (call after token refresh)
56
+ * Shared logic for re-subscribing a collection or entity subscription
57
+ * after an auth error is resolved by refreshing credentials.
58
58
  */
59
+ private resubscribeAfterAuthRefresh;
60
+ private handleWebSocketMessage;
61
+ private ensureAuthenticated;
59
62
  reauthenticate(): Promise<void>;
60
63
  private sendMessage;
61
64
  private doSendMessage;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/client",
3
3
  "type": "module",
4
- "version": "0.4.0",
4
+ "version": "0.6.0",
5
5
  "description": "HTTP SDK client for the Rebase custom backend",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -30,20 +30,20 @@
30
30
  "./package.json": "./package.json"
31
31
  },
32
32
  "dependencies": {
33
- "@rebasepro/common": "0.4.0",
34
- "@rebasepro/types": "0.4.0",
35
- "@rebasepro/utils": "0.4.0"
33
+ "@rebasepro/common": "0.6.0",
34
+ "@rebasepro/utils": "0.6.0",
35
+ "@rebasepro/types": "0.6.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@jest/globals": "^29.7.0",
39
- "@types/jest": "^29.5.14",
40
- "@types/node": "^20.19.41",
41
- "cross-env": "^7.0.3",
42
- "jest": "^29.7.0",
43
- "ts-jest": "^29.4.10",
44
- "tsd": "^0.31.2",
45
- "typescript": "^5.9.3",
46
- "vite": "^5.4.21"
38
+ "@jest/globals": "^30.4.1",
39
+ "@types/jest": "^30.0.0",
40
+ "@types/node": "^25.9.3",
41
+ "cross-env": "^10.1.0",
42
+ "jest": "^30.4.2",
43
+ "ts-jest": "^29.4.11",
44
+ "tsd": "^0.33.0",
45
+ "typescript": "^6.0.3",
46
+ "vite": "^8.0.16"
47
47
  },
48
48
  "files": [
49
49
  "dist",
package/src/admin.ts CHANGED
@@ -1,15 +1,8 @@
1
1
  import type { Transport } from "./transport";
2
+ import { AdminUser } from "@rebasepro/types";
3
+
4
+ export type { AdminUser };
2
5
 
3
- export interface AdminUser {
4
- uid: string;
5
- email: string;
6
- displayName: string | null;
7
- photoURL: string | null;
8
- provider: string;
9
- roles: string[];
10
- createdAt: string;
11
- updatedAt: string;
12
- }
13
6
 
14
7
  export interface CreateAdminOptions {
15
8
  adminPath?: string;
package/src/auth.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { RebaseApiError, Transport } from "./transport";
2
+ import { AuthChangeEvent } from "@rebasepro/types";
3
+
2
4
 
3
5
  export interface RebaseUser {
4
6
  uid: string;
@@ -24,7 +26,8 @@ export interface RebaseSession {
24
26
  user: RebaseUser;
25
27
  }
26
28
 
27
- export type AuthChangeEvent = "SIGNED_IN" | "SIGNED_OUT" | "TOKEN_REFRESHED" | "USER_UPDATED";
29
+ export type { AuthChangeEvent };
30
+
28
31
 
29
32
  export interface AuthConfig {
30
33
  needsSetup: boolean;
@@ -215,7 +218,9 @@ refreshToken: session.refreshToken };
215
218
  const responseBody = await res.json().catch(() => ({}));
216
219
  if (!res.ok) throwApiError(res.status, responseBody, res.statusText);
217
220
  const session = handleAuthResponse(responseBody, "SIGNED_IN");
218
- return { user: session.user, accessToken: session.accessToken, refreshToken: session.refreshToken };
221
+ return { user: session.user,
222
+ accessToken: session.accessToken,
223
+ refreshToken: session.refreshToken };
219
224
  }
220
225
 
221
226
  async function signInWithLinkedin(code: string, redirectUri: string) {
@@ -553,7 +558,7 @@ export function createCookieStorage(options: CookieStorageOptions = {}): AuthSto
553
558
  setItem(key: string, value: string): void {
554
559
  if (typeof document === "undefined") return;
555
560
  let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
556
-
561
+
557
562
  if (defaultOptions.path) {
558
563
  cookieStr += `; path=${defaultOptions.path}`;
559
564
  }
@@ -571,7 +576,7 @@ export function createCookieStorage(options: CookieStorageOptions = {}): AuthSto
571
576
  if (defaultOptions.sameSite) {
572
577
  cookieStr += `; samesite=${defaultOptions.sameSite}`;
573
578
  }
574
-
579
+
575
580
  document.cookie = cookieStr;
576
581
  },
577
582
  removeItem(key: string): void {
@@ -1,4 +1,4 @@
1
- import { jest } from '@jest/globals';
1
+ import { jest } from "@jest/globals";
2
2
  import { createCollectionClient } from "./collection";
3
3
  import type { Transport } from "./transport";
4
4
 
@@ -12,7 +12,7 @@ function createMockTransport(): Transport {
12
12
  apiPath: "/api",
13
13
  fetchFn: globalThis.fetch,
14
14
  getHeaders: () => ({}),
15
- resolveToken: jest.fn<any>().mockResolvedValue(null),
15
+ resolveToken: jest.fn<any>().mockResolvedValue(null)
16
16
  };
17
17
  }
18
18
 
@@ -74,7 +74,8 @@ describe("createCollectionClient", () => {
74
74
 
75
75
  const client = createCollectionClient(transport, "users");
76
76
  // Even if the caller passes limit/offset in params, they should be stripped
77
- await client.count({ limit: 50, offset: 10 });
77
+ await client.count({ limit: 50,
78
+ offset: 10 });
78
79
 
79
80
  const calledUrl = (transport.request as ReturnType<typeof jest.fn>).mock.calls[0][0] as string;
80
81
  expect(calledUrl).not.toContain("limit=");
@@ -111,7 +112,7 @@ describe("createCollectionClient", () => {
111
112
  await client.count({
112
113
  where: {
113
114
  status: "eq.active",
114
- total: [">=", 100],
115
+ total: [">=", 100]
115
116
  }
116
117
  });
117
118
 
@@ -124,8 +125,12 @@ describe("createCollectionClient", () => {
124
125
  describe("find()", () => {
125
126
  it("should call the list endpoint and return entities", async () => {
126
127
  (transport.request as ReturnType<typeof jest.fn>).mockResolvedValue({
127
- data: [{ id: "1", name: "Product A" }],
128
- meta: { total: 1, limit: 20, offset: 0, hasMore: false }
128
+ data: [{ id: "1",
129
+ name: "Product A" }],
130
+ meta: { total: 1,
131
+ limit: 20,
132
+ offset: 0,
133
+ hasMore: false }
129
134
  });
130
135
 
131
136
  const client = createCollectionClient(transport, "products");
package/src/functions.ts CHANGED
@@ -58,7 +58,7 @@ export function createFunctionsClient(transport: Transport): FunctionsClient {
58
58
  async invoke<T = unknown>(
59
59
  name: string,
60
60
  payload?: unknown,
61
- options?: FunctionInvokeOptions,
61
+ options?: FunctionInvokeOptions
62
62
  ): Promise<T> {
63
63
  const method = options?.method ?? "POST";
64
64
  const subPath = options?.path ? `/${options.path.replace(/^\//, "")}` : "";
@@ -75,6 +75,6 @@ export function createFunctionsClient(transport: Transport): FunctionsClient {
75
75
  }
76
76
 
77
77
  return transport.request<T>(routePath, init);
78
- },
78
+ }
79
79
  };
80
80
  }
package/src/index.ts CHANGED
@@ -4,7 +4,10 @@ import { createAdmin, CreateAdminOptions } from "./admin";
4
4
  import { createCron, CreateCronOptions } from "./cron";
5
5
  import { createCollectionClient, CollectionClient } from "./collection";
6
6
  import { createFunctionsClient } from "./functions";
7
- import type { FunctionsClient } from "./functions";
7
+ import { createStorage } from "./storage";
8
+ import { RebaseWebSocketClient } from "./websocket";
9
+ import { RebaseClient, RebaseData, StorageSource } from "@rebasepro/types";
10
+ import { toSnakeCase } from "@rebasepro/utils";
8
11
 
9
12
  export * from "./transport";
10
13
  export * from "./auth";
@@ -16,6 +19,7 @@ export * from "./websocket";
16
19
  export * from "./storage";
17
20
  export * from "./reviver";
18
21
  export * from "./functions";
22
+ export type { Entity, FindResponse } from "@rebasepro/types";
19
23
 
20
24
  export interface CreateRebaseClientOptions extends RebaseClientConfig {
21
25
  auth?: CreateAuthOptions;
@@ -23,17 +27,34 @@ export interface CreateRebaseClientOptions extends RebaseClientConfig {
23
27
  cron?: CreateCronOptions;
24
28
  }
25
29
 
26
- import { RebaseWebSocketClient } from "./websocket";
27
- import { RebaseClient as BaseRebaseClient, RebaseData, CollectionAccessor, StorageSource } from "@rebasepro/types";
28
- export type { Entity, FindResponse } from "@rebasepro/types";
29
- import { toSnakeCase } from "@rebasepro/utils";
30
+ // ─── Typed Data Proxy ────────────────────────────────────────────────────────
31
+ // Adds typed collection accessors when `DB` is provided via the SDK generator.
30
32
 
31
33
  type KebabToCamelCase<S extends string> =
32
34
  S extends `${infer T}-${infer U}`
33
35
  ? `${T}${Capitalize<KebabToCamelCase<U>>}`
34
36
  : S;
35
37
 
36
- export type RebaseClient<DB = Record<string, unknown>> = Omit<BaseRebaseClient<DB>, "data"> & {
38
+ type TypedDataLayer<DB> = {
39
+ collection<S extends string>(slug: S): CollectionClient<
40
+ KebabToCamelCase<S> extends keyof DB
41
+ ? (DB[KebabToCamelCase<S>] extends { Row: infer R extends Record<string, unknown> } ? R : Record<string, unknown>)
42
+ : Record<string, unknown>
43
+ >;
44
+ } & {
45
+ [K in keyof DB]: CollectionClient<
46
+ DB[K] extends { Row: infer R extends Record<string, unknown> } ? R : Record<string, unknown>
47
+ >;
48
+ } & RebaseData;
49
+
50
+ /**
51
+ * The return type of `createRebaseClient<DB>()`.
52
+ *
53
+ * This is `RebaseClient` (from `@rebasepro/types`) with all optional
54
+ * capabilities populated and the `data` layer narrowed to provide
55
+ * typed collection accessors when a `DB` schema generic is supplied.
56
+ */
57
+ export type CreateRebaseClientResult<DB = Record<string, unknown>> = Omit<RebaseClient, "data"> & {
37
58
  setToken: (token: string | null) => void;
38
59
  setAuthTokenGetter: (getter: () => Promise<string | null>) => void;
39
60
  setOnUnauthorized: (handler: () => Promise<boolean>) => void;
@@ -41,24 +62,14 @@ export type RebaseClient<DB = Record<string, unknown>> = Omit<BaseRebaseClient<D
41
62
  auth: ReturnType<typeof createAuth>;
42
63
  admin: ReturnType<typeof createAdmin>;
43
64
  cron: ReturnType<typeof createCron>;
44
- functions: FunctionsClient;
65
+ functions: ReturnType<typeof createFunctionsClient>;
45
66
  ws?: RebaseWebSocketClient;
46
- storage?: StorageSource;
67
+ storage: StorageSource;
47
68
  call: <T = unknown>(endpoint: string, payload?: unknown) => Promise<T>;
48
- data: {
49
- collection<S extends string>(slug: S): CollectionClient<
50
- KebabToCamelCase<S> extends keyof DB
51
- ? (DB[KebabToCamelCase<S>] extends { Row: infer R extends Record<string, unknown> } ? R : Record<string, unknown>)
52
- : Record<string, unknown>
53
- >;
54
- } & {
55
- [K in keyof DB]: CollectionClient<
56
- DB[K] extends { Row: infer R extends Record<string, unknown> } ? R : Record<string, unknown>
57
- >;
58
- } & RebaseData;
69
+ data: TypedDataLayer<DB>;
59
70
  };
60
71
 
61
- import { createStorage } from "./storage";
72
+ // ─── Factory ─────────────────────────────────────────────────────────────────
62
73
 
63
74
  /**
64
75
  * Derive a WebSocket URL from an HTTP base URL.
@@ -94,7 +105,7 @@ function deriveWebSocketUrl(baseUrl?: string): string {
94
105
  .replace(/\/$/, "");
95
106
  }
96
107
 
97
- export function createRebaseClient<DB = Record<string, unknown>>(options: CreateRebaseClientOptions): RebaseClient<DB> {
108
+ export function createRebaseClient<DB = Record<string, unknown>>(options: CreateRebaseClientOptions): CreateRebaseClientResult<DB> {
98
109
  const transport = createTransport(options);
99
110
  const auth = createAuth(transport, options.auth);
100
111
  const admin = createAdmin(transport, options.admin);
@@ -206,7 +217,8 @@ export function createRebaseClient<DB = Record<string, unknown>>(options: Create
206
217
  },
207
218
  data: dataProxy,
208
219
  email: undefined
209
- } as unknown as RebaseClient<DB>;
220
+ } as unknown as CreateRebaseClientResult<DB>;
210
221
 
211
222
  return target;
212
223
  }
224
+