@rebasepro/client 0.11.1-canary.gfd39654 → 0.12.1-canary.gdfba2a1

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.
@@ -1,42 +1,14 @@
1
1
  import type { Transport } from "./transport";
2
- /** A single permission entry scoping an API key to a collection and its allowed operations. */
3
- export interface ApiKeyPermission {
4
- collection: string;
5
- operations: ("read" | "write" | "delete")[];
6
- }
7
- /** An API key with the secret portion masked (returned by list / get / update). */
8
- export interface ApiKeyMasked {
9
- id: string;
10
- name: string;
11
- key_prefix: string;
12
- permissions: ApiKeyPermission[];
13
- admin: boolean;
14
- rate_limit: number | null;
15
- created_by: string;
16
- created_at: string;
17
- updated_at: string;
18
- last_used_at: string | null;
19
- expires_at: string | null;
20
- revoked_at: string | null;
21
- }
22
- /** An API key including the full secret (returned only on creation). */
23
- export interface ApiKeyWithSecret extends ApiKeyMasked {
24
- key: string;
25
- }
26
- /** Payload for creating a new API key. */
27
- export interface CreateApiKeyRequest {
28
- name: string;
29
- permissions: ApiKeyPermission[];
30
- rate_limit?: number | null;
31
- expires_at?: string | null;
32
- }
33
- /** Payload for updating an existing API key. */
34
- export interface UpdateApiKeyRequest {
35
- name?: string;
36
- permissions?: ApiKeyPermission[];
37
- rate_limit?: number | null;
38
- expires_at?: string | null;
39
- }
2
+ /**
3
+ * These were re-declared here, under a comment saying they lived in the server
4
+ * package rather than in `@rebasepro/types`. That stopped being true, and the
5
+ * copy drifted: it never gained `admin`, the flag that grants a key the `admin`
6
+ * role — admin routes plus the RLS `default_admin` policies — so the SDK could
7
+ * describe every kind of key except a privileged one, and
8
+ * `createKey({ …, admin: true })` was an excess-property error.
9
+ */
10
+ export type { ApiKeyPermission, ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequest, UpdateApiKeyRequest } from "@rebasepro/types";
11
+ import type { ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequest, UpdateApiKeyRequest } from "@rebasepro/types";
40
12
  /** Options for the `createApiKeys` factory. */
41
13
  export interface CreateApiKeysOptions {
42
14
  apiKeysPath?: string;
@@ -27,14 +27,8 @@ export interface FunctionsClient {
27
27
  */
28
28
  invoke<T = unknown>(name: string, payload?: unknown, options?: FunctionInvokeOptions): Promise<T>;
29
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
- }
30
+ export type { FunctionInvokeOptions } from "@rebasepro/types";
31
+ import type { FunctionInvokeOptions } from "@rebasepro/types";
38
32
  /**
39
33
  * Create a `FunctionsClient` backed by the given transport.
40
34
  *
package/dist/index.d.ts CHANGED
@@ -15,6 +15,9 @@ export { RebaseClientError } from "./errors";
15
15
  export type { RebaseClientConfig, FindParams, FindResponse } from "./transport";
16
16
  export type { CollectionClient } from "./collection";
17
17
  export type { FindResult, SDKCollectionClient, SDKQueryBuilderInterface, PaginationMeta } from "@rebasepro/types";
18
+ export type { IterateParams, FindAllParams, PageWalkOptions, CursorSpec } from "@rebasepro/types";
19
+ export { RebasePaginationError } from "@rebasepro/common";
20
+ export type { PaginationErrorCode } from "@rebasepro/common";
18
21
  export { QueryBuilder, or, and, cond } from "@rebasepro/common";
19
22
  export { createCookieStorage, createMemoryStorage } from "./auth";
20
23
  export type { AuthConfig, AuthStorage, CookieStorageOptions, CreateAuthOptions } from "./auth";
package/dist/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, GeoPoint, PUBLIC_STORAGE_PREFIX, RebaseApiError, RebaseApiError as RebaseApiError$1, RebaseClientError, Vector, isPublicStoragePath, toCanonicalOp } from "@rebasepro/types";
2
- import { COMPOSITE_ID_SEPARATOR, QueryBuilder, and, buildCompositeId, cond, or, serializeFilter, serializeLogicalCondition, serializeOrderBy } from "@rebasepro/common";
2
+ import { COMPOSITE_ID_SEPARATOR, QueryBuilder, RebasePaginationError, and, buildCompositeId, collectAllPages, cond, or, paginateFind, serializeFilter, serializeLogicalCondition, serializeOrderBy } from "@rebasepro/common";
3
3
  import { toSnakeCase } from "@rebasepro/utils";
4
4
  //#region src/reviver.ts
5
5
  function rebaseReviver(_key, value) {
@@ -30,6 +30,25 @@ function rebaseReviver(_key, value) {
30
30
  }
31
31
  //#endregion
32
32
  //#region src/transport.ts
33
+ /**
34
+ * True when there is no browser to have signed a user in — a Node script, a
35
+ * cron job, an edge worker.
36
+ *
37
+ * Anonymous is an ordinary, correct state in a browser: before sign-in, on a
38
+ * marketing page, for public reads. Warning there would be noise that teaches
39
+ * people to ignore warnings, so the guard is off entirely. This uses the same
40
+ * `typeof window` test as {@link resolveBaseUrl}, and additionally treats a
41
+ * defined `document` as a browser so an SSR shim or test harness that installs
42
+ * only one of the two is still excluded.
43
+ */
44
+ function isServerLikeEnvironment() {
45
+ return typeof window === "undefined" && typeof document === "undefined";
46
+ }
47
+ /**
48
+ * Emitted once per client. Kept as a constant so the wording is testable and
49
+ * greppable — this is the string a user will paste into a search.
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.";
33
52
  function buildQueryString(params) {
34
53
  if (!params) return "";
35
54
  const parts = [];
@@ -76,12 +95,31 @@ function resolveBaseUrl(configured) {
76
95
  if (typeof window !== "undefined" && window.location?.origin) return window.location.origin;
77
96
  return "";
78
97
  }
79
- function createTransport(config) {
98
+ function createTransport(config, environment) {
80
99
  const fetchFn = config.fetch || globalThis.fetch;
81
100
  const apiPath = config.apiPath || "/api";
82
101
  let token = config.token;
83
102
  let tokenGetter;
84
103
  let onUnauthorizedHandler = config.onUnauthorized;
104
+ /** Once per client, never per request — log spam is its own bug. */
105
+ let anonymousWarningIssued = false;
106
+ /**
107
+ * Warn a server-side caller that it built a client that can only ever be
108
+ * anonymous. Deliberately checked at the *first request* rather than at
109
+ * construction: `setToken()` / `setAuthTokenGetter()` and a server-side
110
+ * `auth.signIn…()` (which calls `transport.setToken`) all land after the
111
+ * constructor, and warning at construction would fire on every one of them.
112
+ */
113
+ function warnIfAnonymousServerClient(activeToken) {
114
+ if (anonymousWarningIssued) return;
115
+ if (activeToken) return;
116
+ if (tokenGetter) return;
117
+ if (config.anonymous) return;
118
+ if (environment?.credentialOutOfBand) return;
119
+ if (!isServerLikeEnvironment()) return;
120
+ anonymousWarningIssued = true;
121
+ console.warn(ANONYMOUS_SERVER_CLIENT_WARNING);
122
+ }
85
123
  function getHeaders(activeToken, init) {
86
124
  return {
87
125
  "Content-Type": "application/json",
@@ -96,6 +134,7 @@ function createTransport(config) {
96
134
  const fetched = await tokenGetter();
97
135
  if (fetched !== null && fetched !== void 0) activeToken = fetched;
98
136
  } catch (e) {}
137
+ warnIfAnonymousServerClient(activeToken);
99
138
  const headers = getHeaders(activeToken, init);
100
139
  if (init?.body instanceof FormData) delete headers["Content-Type"];
101
140
  const res = await fetchFn(url, {
@@ -1184,6 +1223,12 @@ function createCollectionClient(transport, slug, ws) {
1184
1223
  meta: raw.meta
1185
1224
  };
1186
1225
  },
1226
+ iterate(params) {
1227
+ return paginateFind((p) => client.find(p), params, slug);
1228
+ },
1229
+ findAll(params) {
1230
+ return collectAllPages((p) => client.find(p), params, slug);
1231
+ },
1187
1232
  async findById(id) {
1188
1233
  try {
1189
1234
  const raw = await transport.request(`${basePath}/${encodeURIComponent(String(id))}`, { method: "GET" });
@@ -1373,7 +1418,8 @@ function createCollectionClient(transport, slug, ws) {
1373
1418
  function createFunctionsClient(transport) {
1374
1419
  return { async invoke(name, payload, options) {
1375
1420
  const method = options?.method ?? "POST";
1376
- const subPath = options?.path ? `/${options.path.replace(/^\//, "")}` : "";
1421
+ const rawPath = options?.path;
1422
+ const subPath = rawPath ? /^[?#]/.test(rawPath) ? rawPath : `/${rawPath.replace(/^\//, "")}` : "";
1377
1423
  const routePath = `/functions/${encodeURIComponent(name)}${subPath}`;
1378
1424
  const init = { method };
1379
1425
  if (payload !== void 0 && method !== "GET") init.body = JSON.stringify(payload);
@@ -1587,7 +1633,7 @@ function extractMessageError(message) {
1587
1633
  * Fire-and-forget (the server sends no response envelope), and exempt from the
1588
1634
  * client-side auth gate — a public channel is usable without an account.
1589
1635
  */
1590
- var CHANNEL_MESSAGE_TYPES = new Set([
1636
+ var CHANNEL_MESSAGE_TYPES = /* @__PURE__ */ new Set([
1591
1637
  "join_channel",
1592
1638
  "leave_channel",
1593
1639
  "broadcast",
@@ -2380,7 +2426,7 @@ var RebaseWebSocketClient = class {
2380
2426
  if (this.deepEqual(normCached, normIncoming)) return cachedRow;
2381
2427
  else {
2382
2428
  const mismatches = {};
2383
- const allKeys = new Set([...Object.keys(normCached), ...Object.keys(normIncoming)]);
2429
+ const allKeys = /* @__PURE__ */ new Set([...Object.keys(normCached), ...Object.keys(normIncoming)]);
2384
2430
  for (const key of allKeys) if (!this.deepEqual(normCached[key], normIncoming[key])) mismatches[key] = {
2385
2431
  cached: normCached[key],
2386
2432
  incoming: normIncoming[key]
@@ -3183,7 +3229,7 @@ function isNetworkError(error) {
3183
3229
  * bug the same payload will hit again than a blip, and retrying it forever
3184
3230
  * jams every write behind it.
3185
3231
  */
3186
- var RETRYABLE_STATUSES = new Set([
3232
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([
3187
3233
  408,
3188
3234
  425,
3189
3235
  429,
@@ -4008,6 +4054,8 @@ var OfflineManager = class {
4008
4054
  meta: answer.meta
4009
4055
  };
4010
4056
  },
4057
+ iterate: (params) => paginateFind((p) => wrapped.find(p), params, slug),
4058
+ findAll: (params) => collectAllPages((p) => wrapped.find(p), params, slug),
4011
4059
  findById: async (id) => {
4012
4060
  await this.ensureCollection(slug);
4013
4061
  if (this.connectivity.shouldAttempt()) try {
@@ -5077,7 +5125,7 @@ function deriveWebSocketUrl(baseUrl) {
5077
5125
  return baseUrl.replace(/^https?:\/\//i, (match) => match.toLowerCase() === "https://" ? "wss://" : "ws://").replace(/\/$/, "");
5078
5126
  }
5079
5127
  function createRebaseClient(options) {
5080
- const transport = createTransport(options);
5128
+ const transport = createTransport(options, { credentialOutOfBand: options.auth?.authFlowMode === "cookie" });
5081
5129
  const auth = createAuth(transport, options.auth);
5082
5130
  const admin = createAdmin(transport, options.admin);
5083
5131
  const cron = createCron(transport, options.cron);
@@ -5262,6 +5310,6 @@ channel: (name, options) => {
5262
5310
  };
5263
5311
  }
5264
5312
  //#endregion
5265
- export { MemoryOfflineStore, QueryBuilder, RebaseApiError, RebaseClientError, RebaseRealtimeChannel, RebaseWebSocketClient, and, cond, createBackups, createCookieStorage, createMemoryStorage, createRebaseClient, isOfflineError, or };
5313
+ export { MemoryOfflineStore, QueryBuilder, RebaseApiError, RebaseClientError, RebasePaginationError, RebaseRealtimeChannel, RebaseWebSocketClient, and, cond, createBackups, createCookieStorage, createMemoryStorage, createRebaseClient, isOfflineError, or };
5266
5314
 
5267
5315
  //# sourceMappingURL=index.es.js.map