@rebasepro/client 0.11.1-canary.gfadf355 → 0.12.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.
@@ -49,14 +49,14 @@ export interface BroadcastEvent {
49
49
  */
50
50
  replayed?: boolean;
51
51
  }
52
- /** One retained message, as returned by {@link RebaseRealtimeChannel.history}. */
53
- export interface ChannelHistoryEntry {
54
- seq: number;
55
- event: string;
56
- payload: unknown;
57
- senderId?: string;
58
- at?: string;
59
- }
52
+ /**
53
+ * One retained message, as returned by {@link RebaseRealtimeChannel.history}.
54
+ *
55
+ * Re-exported rather than re-declared: the copy that used to live here had
56
+ * drifted `at` to optional, while the server always sends it.
57
+ */
58
+ export type { ChannelHistoryEntry } from "@rebasepro/types";
59
+ import type { ChannelHistoryEntry } from "@rebasepro/types";
60
60
  /** The answer to a catch-up request. */
61
61
  export interface ChannelHistoryResult {
62
62
  messages: ChannelHistoryEntry[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/client",
3
3
  "type": "module",
4
- "version": "0.11.1-canary.gfadf355",
4
+ "version": "0.12.0",
5
5
  "description": "HTTP SDK client for the Rebase custom backend",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -29,9 +29,9 @@
29
29
  "./package.json": "./package.json"
30
30
  },
31
31
  "dependencies": {
32
- "@rebasepro/common": "0.11.1-canary.gfadf355",
33
- "@rebasepro/types": "0.11.1-canary.gfadf355",
34
- "@rebasepro/utils": "0.11.1-canary.gfadf355"
32
+ "@rebasepro/common": "0.12.0",
33
+ "@rebasepro/types": "0.12.0",
34
+ "@rebasepro/utils": "0.12.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@jest/globals": "^30.4.1",
package/src/api-keys.ts CHANGED
@@ -1,50 +1,27 @@
1
1
  import type { Transport } from "./transport";
2
2
 
3
- // Re-define the types locally since they live in server, not in @rebasepro/types.
4
- // These match the server-side types exactly.
5
-
6
- /** A single permission entry scoping an API key to a collection and its allowed operations. */
7
- export interface ApiKeyPermission {
8
- collection: string;
9
- operations: ("read" | "write" | "delete")[];
10
- }
11
-
12
- /** An API key with the secret portion masked (returned by list / get / update). */
13
- export interface ApiKeyMasked {
14
- id: string;
15
- name: string;
16
- key_prefix: string;
17
- permissions: ApiKeyPermission[];
18
- admin: boolean;
19
- rate_limit: number | null;
20
- created_by: string;
21
- created_at: string;
22
- updated_at: string;
23
- last_used_at: string | null;
24
- expires_at: string | null;
25
- revoked_at: string | null;
26
- }
27
-
28
- /** An API key including the full secret (returned only on creation). */
29
- export interface ApiKeyWithSecret extends ApiKeyMasked {
30
- key: string;
31
- }
32
-
33
- /** Payload for creating a new API key. */
34
- export interface CreateApiKeyRequest {
35
- name: string;
36
- permissions: ApiKeyPermission[];
37
- rate_limit?: number | null;
38
- expires_at?: string | null;
39
- }
3
+ /**
4
+ * These were re-declared here, under a comment saying they lived in the server
5
+ * package rather than in `@rebasepro/types`. That stopped being true, and the
6
+ * copy drifted: it never gained `admin`, the flag that grants a key the `admin`
7
+ * role — admin routes plus the RLS `default_admin` policies — so the SDK could
8
+ * describe every kind of key except a privileged one, and
9
+ * `createKey({ …, admin: true })` was an excess-property error.
10
+ */
11
+ export type {
12
+ ApiKeyPermission,
13
+ ApiKeyMasked,
14
+ ApiKeyWithSecret,
15
+ CreateApiKeyRequest,
16
+ UpdateApiKeyRequest
17
+ } from "@rebasepro/types";
40
18
 
41
- /** Payload for updating an existing API key. */
42
- export interface UpdateApiKeyRequest {
43
- name?: string;
44
- permissions?: ApiKeyPermission[];
45
- rate_limit?: number | null;
46
- expires_at?: string | null;
47
- }
19
+ import type {
20
+ ApiKeyMasked,
21
+ ApiKeyWithSecret,
22
+ CreateApiKeyRequest,
23
+ UpdateApiKeyRequest
24
+ } from "@rebasepro/types";
48
25
 
49
26
  /** Options for the `createApiKeys` factory. */
50
27
  export interface CreateApiKeysOptions {
package/src/functions.ts CHANGED
@@ -33,14 +33,8 @@ export interface FunctionsClient {
33
33
  ): Promise<T>;
34
34
  }
35
35
 
36
- export interface FunctionInvokeOptions {
37
- /** HTTP method — defaults to `"POST"`. */
38
- method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
39
- /** Sub-path appended after the function name, e.g. `"status/123"`. */
40
- path?: string;
41
- /** Extra headers merged into the request (auth is still injected automatically). */
42
- headers?: Record<string, string>;
43
- }
36
+ export type { FunctionInvokeOptions } from "@rebasepro/types";
37
+ import type { FunctionInvokeOptions } from "@rebasepro/types";
44
38
 
45
39
  /**
46
40
  * Create a `FunctionsClient` backed by the given transport.
@@ -155,7 +155,40 @@ describe("row codec round-trip", () => {
155
155
  });
156
156
  });
157
157
 
158
+ /**
159
+ * Empty the shared queue between the manager tests.
160
+ *
161
+ * `IDB_NAME` is a hardcoded `"rebase-offline"`, so every
162
+ * `new IndexedDBOfflineStore()` in this file opens the *same* database. The
163
+ * `IndexedDBOfflineStore` block above shares it deliberately — each of its
164
+ * tests uses its own key prefix, and one of them exists to prove that
165
+ * isolation holds. The two `OfflineManager` blocks below do not: a manager
166
+ * queues under no prefix, so without this they inherit each other's pending
167
+ * writes.
168
+ *
169
+ * CI failed both manager tests on 2026-07-28 with exactly the symptoms this
170
+ * coupling would produce — "replays them from a fresh one" asserted
171
+ * `flushed: 1` and got `2`, and the cross-tab read came back empty. It has not
172
+ * been reproduced locally (14 runs, including randomized order), so treat this
173
+ * as removing a real coupling rather than as a confirmed diagnosis: if CI fails
174
+ * here again, the shared queue is no longer the explanation and the next place
175
+ * to look is timing inside the manager itself.
176
+ */
177
+ async function clearOfflineQueue(): Promise<void> {
178
+ await new Promise<void>((resolve, reject) => {
179
+ const request = indexedDB.deleteDatabase("rebase-offline");
180
+ request.onsuccess = () => resolve();
181
+ request.onerror = () => reject(request.error);
182
+ // A still-open connection blocks the delete; the manager tests each
183
+ // build their own stores, so resolving here keeps a stray one from
184
+ // hanging the suite rather than failing it.
185
+ request.onblocked = () => resolve();
186
+ });
187
+ }
188
+
158
189
  describe("two tabs over one database", () => {
190
+ beforeEach(clearOfflineQueue);
191
+
159
192
  /** BroadcastChannel delivery is asynchronous; give it a macrotask or two. */
160
193
  const settle = async () => {
161
194
  for (let i = 0; i < 3; i++) await new Promise((resolve) => setTimeout(resolve, 0));
@@ -240,6 +273,8 @@ describe("two tabs over one database", () => {
240
273
  });
241
274
 
242
275
  describe("OfflineManager over IndexedDB", () => {
276
+ beforeEach(clearOfflineQueue);
277
+
243
278
  it("queues offline writes in one manager and replays them from a fresh one", async () => {
244
279
  const table = new Map<string, Record<string, unknown>>();
245
280
  const state = { online: true };
@@ -53,14 +53,14 @@ export interface BroadcastEvent {
53
53
  replayed?: boolean;
54
54
  }
55
55
 
56
- /** One retained message, as returned by {@link RebaseRealtimeChannel.history}. */
57
- export interface ChannelHistoryEntry {
58
- seq: number;
59
- event: string;
60
- payload: unknown;
61
- senderId?: string;
62
- at?: string;
63
- }
56
+ /**
57
+ * One retained message, as returned by {@link RebaseRealtimeChannel.history}.
58
+ *
59
+ * Re-exported rather than re-declared: the copy that used to live here had
60
+ * drifted `at` to optional, while the server always sends it.
61
+ */
62
+ export type { ChannelHistoryEntry } from "@rebasepro/types";
63
+ import type { ChannelHistoryEntry } from "@rebasepro/types";
64
64
 
65
65
  /** The answer to a catch-up request. */
66
66
  export interface ChannelHistoryResult {