@rebasepro/client 0.16.0 → 0.16.1-canary.g0d7af95

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/admin.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Transport } from "./transport";
1
+ import type { Transport } from "./transport.js";
2
2
  import { AdminUser } from "@rebasepro/types";
3
3
  export type { AdminUser };
4
4
  export interface CreateAdminOptions {
@@ -1,4 +1,4 @@
1
- import type { Transport } from "./transport";
1
+ import type { Transport } from "./transport.js";
2
2
  /**
3
3
  * These were re-declared here, under a comment saying they lived in the server
4
4
  * package rather than in `@rebasepro/types`. That stopped being true, and the
package/dist/auth.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Transport } from "./transport";
2
- import type { AuthChangeEvent, RebaseSession, DeviceSession, User } from "@rebasepro/types";
1
+ import { Transport } from "./transport.js";
2
+ import type { AuthAdapterCapabilities, AuthChangeEvent, RebaseSession, DeviceSession, User } from "@rebasepro/types";
3
3
  export type { RebaseSession, AuthTokens, AuthChangeEvent, DeviceSession } from "@rebasepro/types";
4
4
  /** Minimal, non-sensitive user profile returned by {@link findUserByEmail}. */
5
5
  export interface PublicUserProfile {
@@ -7,15 +7,15 @@ export interface PublicUserProfile {
7
7
  displayName: string | null;
8
8
  photoURL: string | null;
9
9
  }
10
- export interface AuthConfig {
11
- needsSetup: boolean;
12
- registrationEnabled: boolean;
13
- emailServiceEnabled?: boolean;
14
- passwordReset?: boolean;
15
- emailVerification?: boolean;
16
- magicLink?: boolean;
17
- enabledProviders: string[];
18
- }
10
+ /**
11
+ * What `GET /auth/config` answers: the backend's auth capability document.
12
+ *
13
+ * Declared once, in `@rebasepro/types`, and re-exported here under the name the
14
+ * endpoint uses. The SDK used to carry its own near-copy of it, which listed an
15
+ * `emailServiceEnabled` flag no backend has ever sent and omitted half of what
16
+ * every backend does send.
17
+ */
18
+ export type AuthConfig = AuthAdapterCapabilities;
19
19
  export interface AuthStorage {
20
20
  getItem: (key: string) => string | null;
21
21
  setItem: (key: string, value: string) => void;
@@ -167,6 +167,16 @@ export declare function createAuth(transport: Transport, options?: CreateAuthOpt
167
167
  accessToken: string;
168
168
  refreshToken: string;
169
169
  }>;
170
+ sendEmailOtp: (email: string) => Promise<{
171
+ success: boolean;
172
+ message: string;
173
+ expiresInSeconds: number;
174
+ }>;
175
+ verifyEmailOtp: (email: string, code: string) => Promise<{
176
+ user: User;
177
+ accessToken: string;
178
+ refreshToken: string;
179
+ }>;
170
180
  getSessions: () => Promise<DeviceSession[]>;
171
181
  revokeSession: (sessionId: string) => Promise<{
172
182
  success: boolean;
@@ -174,7 +184,7 @@ export declare function createAuth(transport: Transport, options?: CreateAuthOpt
174
184
  revokeAllSessions: () => Promise<{
175
185
  success: boolean;
176
186
  }>;
177
- getAuthConfig: () => Promise<AuthConfig>;
187
+ getAuthConfig: () => Promise<AuthAdapterCapabilities>;
178
188
  getSession: () => RebaseSession | null;
179
189
  onAuthStateChange: (callback: (event: AuthChangeEvent, session: RebaseSession | null) => void) => () => boolean;
180
190
  canRestoreSession: () => boolean;
package/dist/backups.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Transport } from "./transport";
1
+ import { Transport } from "./transport.js";
2
2
  import type { BackupInfo, BackupDestinationKind } from "@rebasepro/types";
3
3
  export interface CreateBackupsOptions {
4
4
  backupsPath?: string;
@@ -1,5 +1,5 @@
1
- import { FindParams, Transport } from "./transport";
2
- import { RebaseWebSocketClient } from "./websocket";
1
+ import { FindParams, Transport } from "./transport.js";
2
+ import { RebaseWebSocketClient } from "./websocket.js";
3
3
  import { FindResult, SDKCollectionClient } from "@rebasepro/types";
4
4
  /**
5
5
  * A live query result: a normal {@link FindResult} plus what an interface
package/dist/cron.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Transport } from "./transport";
1
+ import { Transport } from "./transport.js";
2
2
  import type { CronJobStatus, CronJobLogEntry } from "@rebasepro/types";
3
3
  export interface CreateCronOptions {
4
4
  cronPath?: string;
@@ -1,4 +1,4 @@
1
- import type { Transport } from "./transport";
1
+ import type { Transport } from "./transport.js";
2
2
  /**
3
3
  * Client interface for invoking custom backend functions.
4
4
  *
package/dist/index.d.ts CHANGED
@@ -1,43 +1,43 @@
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 { createBackups } from "./backups";
6
- import { createApiKeys, CreateApiKeysOptions } from "./api-keys";
7
- import { CollectionClient } from "./collection";
8
- import { createFunctionsClient } from "./functions";
9
- import { RebaseWebSocketClient } from "./websocket";
10
- import { RebaseRealtimeChannel, type ChannelOptions } from "./realtime-channel";
11
- import { type OfflineApi, type OfflineConfig } from "./offline";
1
+ import { RebaseClientConfig } from "./transport.js";
2
+ import { createAuth, CreateAuthOptions } from "./auth.js";
3
+ import { createAdmin, CreateAdminOptions } from "./admin.js";
4
+ import { createCron, CreateCronOptions } from "./cron.js";
5
+ import { createBackups } from "./backups.js";
6
+ import { createApiKeys, CreateApiKeysOptions } from "./api-keys.js";
7
+ import { CollectionClient } from "./collection.js";
8
+ import { createFunctionsClient } from "./functions.js";
9
+ import { RebaseWebSocketClient } from "./websocket.js";
10
+ import { RebaseRealtimeChannel, type ChannelOptions } from "./realtime-channel.js";
11
+ import { type OfflineApi, type OfflineConfig } from "./offline.js";
12
12
  import { InsertOf, RebaseClient, RebaseSdkData, RowOf, StorageSource, StorageSourceDefinition, StorageSourceRegistry, UpdateOf } from "@rebasepro/types";
13
- export { RebaseApiError } from "./transport";
14
- export { RebaseClientError } from "./errors";
13
+ export { RebaseApiError } from "./transport.js";
14
+ export { RebaseClientError } from "./errors.js";
15
15
  export type { RebaseErrorCode } from "@rebasepro/types";
16
- export type { RebaseClientConfig, FindParams, FindResponse } from "./transport";
17
- export type { CollectionClient } from "./collection";
16
+ export type { RebaseClientConfig, FindParams, FindResponse } from "./transport.js";
17
+ export type { CollectionClient } from "./collection.js";
18
18
  export type { FindResult, SDKCollectionClient, SDKQueryBuilderInterface, PaginationMeta } from "@rebasepro/types";
19
19
  export type { IterateParams, FindAllParams, PageWalkOptions, CursorSpec } from "@rebasepro/types";
20
20
  export { RebasePaginationError } from "@rebasepro/common";
21
21
  export type { PaginationErrorCode } from "@rebasepro/common";
22
22
  export { QueryBuilder, or, and, cond } from "@rebasepro/common";
23
- export { createCookieStorage, createMemoryStorage } from "./auth";
24
- export type { AuthConfig, AuthStorage, CookieStorageOptions, CreateAuthOptions } from "./auth";
23
+ export { createCookieStorage, createMemoryStorage } from "./auth.js";
24
+ export type { AuthConfig, AuthStorage, CookieStorageOptions, CreateAuthOptions } from "./auth.js";
25
25
  export type { User, RebaseSession, AuthTokens, AuthChangeEvent, DeviceSession } from "@rebasepro/types";
26
- export type { CreateAdminOptions } from "./admin";
27
- export type { AdminUser } from "./admin";
28
- export type { CreateCronOptions } from "./cron";
29
- export { createBackups } from "./backups";
30
- export type { CreateBackupsOptions } from "./backups";
31
- export type { ApiKeyMasked, ApiKeyPermission, ApiKeyWithSecret, CreateApiKeyRequest, CreateApiKeysOptions, UpdateApiKeyRequest } from "./api-keys";
32
- export type { FunctionInvokeOptions, FunctionsClient } from "./functions";
33
- export { RebaseWebSocketClient } from "./websocket";
34
- export { RebaseRealtimeChannel } from "./realtime-channel";
35
- export type { PresenceState, PresenceDiff, BroadcastEvent, ChannelTransport, ChannelOptions, ChannelHistoryEntry, ChannelHistoryResult } from "./realtime-channel";
36
- export type { OfflineApi, OfflineConfig, OfflineStatus } from "./offline";
37
- export { isOfflineError } from "./offline";
38
- export type { LiveResult, ObserveOptions, RowSnapshotMeta } from "./collection";
39
- export type { OfflineStore, OfflineCacheEntry, OfflineCacheRecord, PendingMutation, MutationRollback } from "./offline-store";
40
- export { MemoryOfflineStore } from "./offline-store";
26
+ export type { CreateAdminOptions } from "./admin.js";
27
+ export type { AdminUser } from "./admin.js";
28
+ export type { CreateCronOptions } from "./cron.js";
29
+ export { createBackups } from "./backups.js";
30
+ export type { CreateBackupsOptions } from "./backups.js";
31
+ export type { ApiKeyMasked, ApiKeyPermission, ApiKeyWithSecret, CreateApiKeyRequest, CreateApiKeysOptions, UpdateApiKeyRequest } from "./api-keys.js";
32
+ export type { FunctionInvokeOptions, FunctionsClient } from "./functions.js";
33
+ export { RebaseWebSocketClient } from "./websocket.js";
34
+ export { RebaseRealtimeChannel } from "./realtime-channel.js";
35
+ export type { PresenceState, PresenceDiff, BroadcastEvent, ChannelTransport, ChannelOptions, ChannelHistoryEntry, ChannelHistoryResult } from "./realtime-channel.js";
36
+ export type { OfflineApi, OfflineConfig, OfflineStatus } from "./offline.js";
37
+ export { isOfflineError } from "./offline.js";
38
+ export type { LiveResult, ObserveOptions, RowSnapshotMeta } from "./collection.js";
39
+ export type { OfflineStore, OfflineCacheEntry, OfflineCacheRecord, PendingMutation, MutationRollback } from "./offline-store.js";
40
+ export { MemoryOfflineStore } from "./offline-store.js";
41
41
  export interface CreateRebaseClientOptions extends RebaseClientConfig {
42
42
  auth?: CreateAuthOptions;
43
43
  admin?: CreateAdminOptions;
package/dist/index.es.js CHANGED
@@ -48,7 +48,7 @@ function isServerLikeEnvironment() {
48
48
  * Emitted once per client. Kept as a constant so the wording is testable and
49
49
  * greppable — this is the string a user will paste into a search.
50
50
  */
51
- var ANONYMOUS_SERVER_CLIENT_WARNING = "[rebase] This client was created outside a browser with no credential — no `token`, no auth token getter, and no cookie auth flow — so every request runs as an anonymous caller. Row-level security will return only publicly readable rows, which is usually nothing and occasionally the wrong thing. Inside a cron or function handler, use the `client` you were handed instead of building a new one: its data plane is already admin-scoped. In a standalone script or job, pass the service key as `token`. If you really do want anonymous access, pass `anonymous: true` to silence this.";
51
+ var ANONYMOUS_SERVER_CLIENT_WARNING = "[rebase] This client was created outside a browser with no credential — no `token`, no auth token getter, and no cookie auth flow — so every request runs as an anonymous caller. Row-level security will return only publicly readable rows, which is usually nothing and occasionally the wrong thing. Inside a cron or function handler, use the `rebase` you were handed instead of building a new one: its data plane is already admin-scoped. In a standalone script or job, pass the service key as `token`. If you really do want anonymous access, pass `anonymous: true` to silence this.";
52
52
  /**
53
53
  * Refuse a filter whose *value* is missing.
54
54
  *
@@ -936,6 +936,49 @@ function createAuth(transport, options) {
936
936
  refreshToken: session.refreshToken
937
937
  };
938
938
  }
939
+ /**
940
+ * Ask for a six-digit sign-in code by email.
941
+ *
942
+ * Answers the same thing whether or not the address has an account — do not
943
+ * use the result to tell a person whether they are registered, because it
944
+ * does not know.
945
+ */
946
+ async function sendEmailOtp(email) {
947
+ const res = await getFetch()(authUrl("/otp"), {
948
+ method: "POST",
949
+ headers: { "Content-Type": "application/json" },
950
+ body: JSON.stringify({ email })
951
+ });
952
+ const body = await res.json().catch(() => ({}));
953
+ if (!res.ok) throwApiError(res.status, body, res.statusText);
954
+ return body;
955
+ }
956
+ /**
957
+ * Trade a code for a session.
958
+ *
959
+ * The address is sent with the code because the code is only valid for it:
960
+ * that is what keeps a six-digit guess a guess against one account rather
961
+ * than against every account at once.
962
+ */
963
+ async function verifyEmailOtp(email, code) {
964
+ const res = await getFetch()(authUrl("/otp/verify"), {
965
+ method: "POST",
966
+ headers: { "Content-Type": "application/json" },
967
+ body: JSON.stringify({
968
+ email,
969
+ code
970
+ }),
971
+ credentials: authFlowMode === "cookie" ? "include" : void 0
972
+ });
973
+ const body = await res.json().catch(() => ({}));
974
+ if (!res.ok) throwApiError(res.status, body, res.statusText);
975
+ const session = handleAuthResponse(body, "SIGNED_IN");
976
+ return {
977
+ user: session.user,
978
+ accessToken: session.accessToken,
979
+ refreshToken: session.refreshToken
980
+ };
981
+ }
939
982
  async function getSessions() {
940
983
  return (await transport.request(authPath + "/sessions", { method: "GET" })).sessions;
941
984
  }
@@ -1026,6 +1069,8 @@ function createAuth(transport, options) {
1026
1069
  verifyEmail,
1027
1070
  sendMagicLink,
1028
1071
  verifyMagicLink,
1072
+ sendEmailOtp,
1073
+ verifyEmailOtp,
1029
1074
  getSessions,
1030
1075
  revokeSession,
1031
1076
  revokeAllSessions,
@@ -1444,23 +1489,17 @@ function createCollectionClient(transport, slug, ws) {
1444
1489
  })).data || [];
1445
1490
  },
1446
1491
  /**
1447
- * Still `PUT`, deliberately, even though the server now serves `PATCH`
1448
- * on the same handler and `PATCH` is the honest verb for a merge.
1492
+ * `PATCH`, the verb for a merge `update(id, data: Partial<M>)` sends
1493
+ * only the keys the caller named and the server merges the rest.
1449
1494
  *
1450
- * The two are interchangeable server-side, so switching buys nothing at
1451
- * runtime and it costs compatibility in the direction that fails
1452
- * quietly. A 0.14 client talking to a 0.13 server would send `PATCH` to
1453
- * a route that does not exist and get a **404**, which is
1454
- * indistinguishable from "that row is gone". Every write would look like
1455
- * a missing record.
1456
- *
1457
- * `PATCH` is what the OpenAPI spec advertises, so anyone generating a
1458
- * client gets the correct verb; this stays on `PUT` until the oldest
1459
- * supported server is one that serves both.
1495
+ * It was `PUT` for a while, on a route that served both so older
1496
+ * clients kept working. The alias is gone: the OpenAPI spec, the SDK
1497
+ * and the route now name one verb, and a spec-validating gateway in
1498
+ * front of the API sees the operation the server actually implements.
1460
1499
  */
1461
1500
  async update(id, data) {
1462
1501
  return await transport.request(`${basePath}/${encodeURIComponent(String(id))}`, {
1463
- method: "PUT",
1502
+ method: "PATCH",
1464
1503
  body: JSON.stringify(data)
1465
1504
  });
1466
1505
  },
@@ -1604,7 +1643,8 @@ function createCollectionClient(transport, slug, ws) {
1604
1643
  offset: window.driverOffset,
1605
1644
  orderBy: normalizeOrderBy(params?.orderBy),
1606
1645
  searchString: params?.searchString,
1607
- searchExplain: params?.searchExplain
1646
+ searchExplain: params?.searchExplain,
1647
+ vectorSearch: params?.vectorSearch
1608
1648
  }, (incomingRows) => {
1609
1649
  const currentUpdateId = ++lastUpdateId;
1610
1650
  const requestedLimit = window.limit;
@@ -1684,6 +1724,27 @@ function createFunctionsClient(transport) {
1684
1724
  * When set, it is forwarded to the server so the correct
1685
1725
  * `StorageController` is resolved from the registry.
1686
1726
  */
1727
+ /**
1728
+ * A storage key, encoded for a URL path.
1729
+ *
1730
+ * Every call below interpolated the key raw, and the server decodes what it
1731
+ * receives — so three ordinary filenames did three different wrong things:
1732
+ *
1733
+ * `Invoice #12.pdf` the `#` began the fragment, the server saw
1734
+ * `default/Invoice ` and looked up a key that is not the
1735
+ * file — and a scoped `?token=` after it was swallowed too
1736
+ * `100% done.png` `decodeURIComponent` threw URIError → 500
1737
+ * `a%2Fb.png` decoded to `a/b.png`, silently resolving a DIFFERENT
1738
+ * object
1739
+ *
1740
+ * Per SEGMENT, because `/` is the key's own separator and must survive; every
1741
+ * other character is encoded, including a literal `%`. Verified to round-trip
1742
+ * through the server's decode for all of the above, plus `+`, accented
1743
+ * characters and a trailing `%`.
1744
+ */
1745
+ function encodeStorageKey(key) {
1746
+ return key.split("/").map(encodeURIComponent).join("/");
1747
+ }
1687
1748
  function createStorage(transport, storageId) {
1688
1749
  const urlsCache = /* @__PURE__ */ new Map();
1689
1750
  /**
@@ -1729,15 +1790,15 @@ function createStorage(transport, storageId) {
1729
1790
  fileNotFound: true
1730
1791
  };
1731
1792
  if (isPublicStoragePath(filePath)) {
1732
- const publicConfig = { url: withStorageId(`${fileUrlBase()}/storage/file/${filePath}`) };
1793
+ const publicConfig = { url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}`) };
1733
1794
  urlsCache.set(cacheKey, { config: publicConfig });
1734
1795
  return publicConfig;
1735
1796
  }
1736
1797
  try {
1737
- const result = await transport.request(withStorageId(`/storage/metadata/${filePath}`));
1798
+ const result = await transport.request(withStorageId(`/storage/metadata/${encodeStorageKey(filePath)}`));
1738
1799
  if (result.data.public) {
1739
1800
  const publicConfig = {
1740
- url: withStorageId(`${fileUrlBase()}/storage/file/${filePath}`),
1801
+ url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}`),
1741
1802
  metadata: result.data
1742
1803
  };
1743
1804
  urlsCache.set(cacheKey, { config: publicConfig });
@@ -1746,7 +1807,7 @@ function createStorage(transport, storageId) {
1746
1807
  const scopedToken = result.data.token;
1747
1808
  const tokenQuery = scopedToken ? `?token=${scopedToken}` : "";
1748
1809
  const downloadConfig = {
1749
- url: withStorageId(`${fileUrlBase()}/storage/file/${filePath}${tokenQuery}`),
1810
+ url: withStorageId(`${fileUrlBase()}/storage/file/${encodeStorageKey(filePath)}${tokenQuery}`),
1750
1811
  metadata: result.data
1751
1812
  };
1752
1813
  const expiresAt = result.data.tokenExpiresIn ? Date.now() + (result.data.tokenExpiresIn - 10) * 1e3 : void 0;
@@ -1779,7 +1840,7 @@ function createStorage(transport, storageId) {
1779
1840
  if (bucket && filePath && !filePath.startsWith(bucket)) filePath = `${bucket}/${filePath}`;
1780
1841
  if (!filePath || filePath.trim() === "" || filePath === "/") return;
1781
1842
  try {
1782
- await transport.request(withStorageId(`/storage/file/${filePath}`), { method: "DELETE" });
1843
+ await transport.request(withStorageId(`/storage/file/${encodeStorageKey(filePath)}`), { method: "DELETE" });
1783
1844
  } catch (e) {
1784
1845
  if (!(e instanceof Error && "status" in e && e.status === 404)) throw e;
1785
1846
  }
@@ -1904,7 +1965,6 @@ var RebaseWebSocketClient = class {
1904
1965
  websocketUrl;
1905
1966
  ws = null;
1906
1967
  getAuthToken;
1907
- subscriptions = /* @__PURE__ */ new Map();
1908
1968
  listeners = /* @__PURE__ */ new Map();
1909
1969
  /** Channel-name → handlers, for broadcast and presence frames. */
1910
1970
  channelHandlers = /* @__PURE__ */ new Map();
@@ -2399,17 +2459,6 @@ var RebaseWebSocketClient = class {
2399
2459
  }
2400
2460
  }
2401
2461
  }
2402
- if (subscriptionId && this.subscriptions.has(subscriptionId)) {
2403
- const callback = this.subscriptions.get(subscriptionId);
2404
- if (!callback) throw new Error(`Subscription callback not found for subscriptionId: ${subscriptionId}`);
2405
- if (message.type === "ERROR" || message.error) {
2406
- if (callback.onError) {
2407
- const { errorMessage, errorCode } = extractMessageError(message);
2408
- callback.onError(new RebaseApiError$1(errorMessage, { code: errorCode }));
2409
- }
2410
- } else callback.onUpdate(message);
2411
- return;
2412
- }
2413
2462
  if (type === "ERROR" || type === "error" || message.error) {
2414
2463
  const { errorMessage, errorCode } = extractMessageError(message);
2415
2464
  console.warn(`[Rebase] Realtime error from the server${errorCode ? ` (${errorCode})` : ""}: ${errorMessage}`);