@rebasepro/client 0.11.1-canary.gfd39654 → 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.
- package/dist/api-keys.d.ts +10 -38
- package/dist/functions.d.ts +2 -8
- package/dist/index.d.ts +3 -0
- package/dist/index.es.js +51 -4
- package/dist/index.es.js.map +1 -1
- package/dist/realtime-channel.d.ts +8 -8
- package/dist/transport.d.ts +35 -1
- package/package.json +4 -4
- package/src/anonymous-client-guard.test.ts +190 -0
- package/src/api-keys.ts +21 -44
- package/src/collection.test.ts +1 -1
- package/src/collection.ts +14 -0
- package/src/functions.ts +2 -8
- package/src/index.ts +10 -1
- package/src/offline-idb-store.test.ts +35 -0
- package/src/offline-integration.test.ts +5 -0
- package/src/offline.ts +9 -1
- package/src/realtime-channel.ts +8 -8
- package/src/transport.ts +80 -1
package/dist/api-keys.d.ts
CHANGED
|
@@ -1,42 +1,14 @@
|
|
|
1
1
|
import type { Transport } from "./transport";
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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;
|
package/dist/functions.d.ts
CHANGED
|
@@ -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
|
|
31
|
-
|
|
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" });
|
|
@@ -4008,6 +4053,8 @@ var OfflineManager = class {
|
|
|
4008
4053
|
meta: answer.meta
|
|
4009
4054
|
};
|
|
4010
4055
|
},
|
|
4056
|
+
iterate: (params) => paginateFind((p) => wrapped.find(p), params, slug),
|
|
4057
|
+
findAll: (params) => collectAllPages((p) => wrapped.find(p), params, slug),
|
|
4011
4058
|
findById: async (id) => {
|
|
4012
4059
|
await this.ensureCollection(slug);
|
|
4013
4060
|
if (this.connectivity.shouldAttempt()) try {
|
|
@@ -5077,7 +5124,7 @@ function deriveWebSocketUrl(baseUrl) {
|
|
|
5077
5124
|
return baseUrl.replace(/^https?:\/\//i, (match) => match.toLowerCase() === "https://" ? "wss://" : "ws://").replace(/\/$/, "");
|
|
5078
5125
|
}
|
|
5079
5126
|
function createRebaseClient(options) {
|
|
5080
|
-
const transport = createTransport(options);
|
|
5127
|
+
const transport = createTransport(options, { credentialOutOfBand: options.auth?.authFlowMode === "cookie" });
|
|
5081
5128
|
const auth = createAuth(transport, options.auth);
|
|
5082
5129
|
const admin = createAdmin(transport, options.admin);
|
|
5083
5130
|
const cron = createCron(transport, options.cron);
|
|
@@ -5262,6 +5309,6 @@ channel: (name, options) => {
|
|
|
5262
5309
|
};
|
|
5263
5310
|
}
|
|
5264
5311
|
//#endregion
|
|
5265
|
-
export { MemoryOfflineStore, QueryBuilder, RebaseApiError, RebaseClientError, RebaseRealtimeChannel, RebaseWebSocketClient, and, cond, createBackups, createCookieStorage, createMemoryStorage, createRebaseClient, isOfflineError, or };
|
|
5312
|
+
export { MemoryOfflineStore, QueryBuilder, RebaseApiError, RebaseClientError, RebasePaginationError, RebaseRealtimeChannel, RebaseWebSocketClient, and, cond, createBackups, createCookieStorage, createMemoryStorage, createRebaseClient, isOfflineError, or };
|
|
5266
5313
|
|
|
5267
5314
|
//# sourceMappingURL=index.es.js.map
|