@rebasepro/client 0.2.5 → 0.4.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/auth.d.ts +8 -0
- package/dist/collection.d.ts +4 -3
- package/dist/index.es.js +315 -70
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +323 -69
- package/dist/index.umd.js.map +1 -1
- package/dist/query_builder.d.ts +1 -1
- package/dist/websocket.d.ts +6 -0
- package/package.json +4 -4
- package/src/auth.ts +64 -0
- package/src/collection.ts +112 -68
- package/src/index.ts +39 -9
- package/src/query_builder.ts +1 -1
- package/src/transport.ts +44 -10
- package/src/websocket.ts +135 -9
package/dist/auth.d.ts
CHANGED
|
@@ -168,3 +168,11 @@ export declare function createAuth(transport: Transport, options?: CreateAuthOpt
|
|
|
168
168
|
getSession: () => RebaseSession | null;
|
|
169
169
|
onAuthStateChange: (callback: (event: AuthChangeEvent, session: RebaseSession | null) => void) => () => boolean;
|
|
170
170
|
};
|
|
171
|
+
export interface CookieStorageOptions {
|
|
172
|
+
path?: string;
|
|
173
|
+
domain?: string;
|
|
174
|
+
secure?: boolean;
|
|
175
|
+
sameSite?: "Lax" | "Strict" | "None";
|
|
176
|
+
maxAge?: number;
|
|
177
|
+
}
|
|
178
|
+
export declare function createCookieStorage(options?: CookieStorageOptions): AuthStorage;
|
package/dist/collection.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FindParams, Transport } from "./transport";
|
|
2
2
|
import { RebaseWebSocketClient } from "./websocket";
|
|
3
|
-
import { CollectionAccessor, FilterOperator } from "@rebasepro/types";
|
|
3
|
+
import { CollectionAccessor, FilterOperator, LogicalCondition, WhereValue } from "@rebasepro/types";
|
|
4
4
|
import { QueryBuilder } from "./query_builder";
|
|
5
5
|
/**
|
|
6
6
|
* CollectionClient extends `CollectionAccessor` from `@rebasepro/types` so that
|
|
@@ -9,7 +9,8 @@ import { QueryBuilder } from "./query_builder";
|
|
|
9
9
|
* Additionally it exposes fluent query builder methods like `.where()`, `.orderBy()`.
|
|
10
10
|
*/
|
|
11
11
|
export interface CollectionClient<M extends Record<string, unknown> = Record<string, unknown>> extends CollectionAccessor<M> {
|
|
12
|
-
where
|
|
12
|
+
where<K extends keyof M & string>(column: K, operator: FilterOperator, value: WhereValue<M[K]>): QueryBuilder<M>;
|
|
13
|
+
where(logicalCondition: LogicalCondition): QueryBuilder<M>;
|
|
13
14
|
orderBy(column: keyof M & string, ascending?: "asc" | "desc"): QueryBuilder<M>;
|
|
14
15
|
limit(count: number): QueryBuilder<M>;
|
|
15
16
|
offset(count: number): QueryBuilder<M>;
|
package/dist/index.es.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Vector, GeoPoint, EntityRelation, EntityReference } from "@rebasepro/types";
|
|
2
2
|
import { QueryBuilder } from "@rebasepro/common";
|
|
3
|
-
import { QueryBuilder as QueryBuilder2 } from "@rebasepro/common";
|
|
3
|
+
import { QueryBuilder as QueryBuilder2, and, cond, or } from "@rebasepro/common";
|
|
4
4
|
import { toSnakeCase } from "@rebasepro/utils";
|
|
5
5
|
function rebaseReviver(_key, value) {
|
|
6
6
|
if (value && typeof value === "object" && "__type" in value) {
|
|
@@ -66,15 +66,33 @@ function normalizeWhereValue(value) {
|
|
|
66
66
|
if (value === null) return "eq.null";
|
|
67
67
|
if (typeof value === "boolean") return `eq.${value}`;
|
|
68
68
|
if (typeof value === "number") return String(value);
|
|
69
|
-
if (Array.isArray(value)
|
|
70
|
-
const [
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
const conditions = Array.isArray(value[0]) ? value : [value];
|
|
71
|
+
const [rawOp, val] = conditions[0] || [];
|
|
72
|
+
if (rawOp) {
|
|
73
|
+
const op = OP_MAP[rawOp] ?? rawOp;
|
|
74
|
+
if (val === null) return `${op}.null`;
|
|
75
|
+
if (Array.isArray(val)) return `${op}.(${val.join(",")})`;
|
|
76
|
+
return `${op}.${val}`;
|
|
77
|
+
}
|
|
75
78
|
}
|
|
76
79
|
return String(value);
|
|
77
80
|
}
|
|
81
|
+
function serializeLogicalCondition(cond2) {
|
|
82
|
+
if ("type" in cond2) {
|
|
83
|
+
const sub = cond2.conditions.map(serializeLogicalCondition).join(",");
|
|
84
|
+
return `${cond2.type}(${sub})`;
|
|
85
|
+
} else {
|
|
86
|
+
const op = OP_MAP[cond2.operator] ?? cond2.operator;
|
|
87
|
+
let formattedValue = cond2.value;
|
|
88
|
+
if (Array.isArray(cond2.value)) {
|
|
89
|
+
formattedValue = `(${cond2.value.join(",")})`;
|
|
90
|
+
} else if (cond2.value === null) {
|
|
91
|
+
formattedValue = "null";
|
|
92
|
+
}
|
|
93
|
+
return `${cond2.column}.${op}.${formattedValue}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
78
96
|
function buildQueryString(params) {
|
|
79
97
|
if (!params) return "";
|
|
80
98
|
const parts = [];
|
|
@@ -90,10 +108,22 @@ function buildQueryString(params) {
|
|
|
90
108
|
if (params.include && params.include.length > 0) {
|
|
91
109
|
parts.push(`include=${encodeURIComponent(params.include.join(","))}`);
|
|
92
110
|
}
|
|
111
|
+
if (params.logical) {
|
|
112
|
+
const root = params.logical;
|
|
113
|
+
const serialized = root.conditions.map(serializeLogicalCondition).join(",");
|
|
114
|
+
parts.push(`${root.type}=${encodeURIComponent(`(${serialized})`)}`);
|
|
115
|
+
}
|
|
93
116
|
if (params.where) {
|
|
94
117
|
for (const [field, value] of Object.entries(params.where)) {
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
if (Array.isArray(value) && value.length > 0 && Array.isArray(value[0])) {
|
|
119
|
+
for (const subVal of value) {
|
|
120
|
+
const normalized = normalizeWhereValue(subVal);
|
|
121
|
+
parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(normalized)}`);
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
const normalized = normalizeWhereValue(value);
|
|
125
|
+
parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(normalized)}`);
|
|
126
|
+
}
|
|
97
127
|
}
|
|
98
128
|
}
|
|
99
129
|
return parts.length > 0 ? "?" + parts.join("&") : "";
|
|
@@ -703,6 +733,58 @@ function createAuth(transport, options) {
|
|
|
703
733
|
onAuthStateChange
|
|
704
734
|
};
|
|
705
735
|
}
|
|
736
|
+
function createCookieStorage(options = {}) {
|
|
737
|
+
const defaultOptions = {
|
|
738
|
+
path: "/",
|
|
739
|
+
sameSite: "Lax",
|
|
740
|
+
...options
|
|
741
|
+
};
|
|
742
|
+
return {
|
|
743
|
+
getItem(key) {
|
|
744
|
+
if (typeof document === "undefined") return null;
|
|
745
|
+
const nameEQ = encodeURIComponent(key) + "=";
|
|
746
|
+
const ca = document.cookie.split(";");
|
|
747
|
+
for (let i = 0; i < ca.length; i++) {
|
|
748
|
+
let c = ca[i];
|
|
749
|
+
while (c.charAt(0) === " ") c = c.substring(1, c.length);
|
|
750
|
+
if (c.indexOf(nameEQ) === 0) {
|
|
751
|
+
return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return null;
|
|
755
|
+
},
|
|
756
|
+
setItem(key, value) {
|
|
757
|
+
if (typeof document === "undefined") return;
|
|
758
|
+
let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
759
|
+
if (defaultOptions.path) {
|
|
760
|
+
cookieStr += `; path=${defaultOptions.path}`;
|
|
761
|
+
}
|
|
762
|
+
if (defaultOptions.domain) {
|
|
763
|
+
cookieStr += `; domain=${defaultOptions.domain}`;
|
|
764
|
+
}
|
|
765
|
+
if (defaultOptions.maxAge !== void 0) {
|
|
766
|
+
cookieStr += `; max-age=${defaultOptions.maxAge}`;
|
|
767
|
+
} else {
|
|
768
|
+
cookieStr += `; max-age=${365 * 24 * 60 * 60}`;
|
|
769
|
+
}
|
|
770
|
+
if (defaultOptions.secure) {
|
|
771
|
+
cookieStr += "; secure";
|
|
772
|
+
}
|
|
773
|
+
if (defaultOptions.sameSite) {
|
|
774
|
+
cookieStr += `; samesite=${defaultOptions.sameSite}`;
|
|
775
|
+
}
|
|
776
|
+
document.cookie = cookieStr;
|
|
777
|
+
},
|
|
778
|
+
removeItem(key) {
|
|
779
|
+
if (typeof document === "undefined") return;
|
|
780
|
+
let cookieStr = `${encodeURIComponent(key)}=; path=${defaultOptions.path || "/"}; max-age=-1`;
|
|
781
|
+
if (defaultOptions.domain) {
|
|
782
|
+
cookieStr += `; domain=${defaultOptions.domain}`;
|
|
783
|
+
}
|
|
784
|
+
document.cookie = cookieStr;
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
}
|
|
706
788
|
function createAdmin(transport, options) {
|
|
707
789
|
const opts = options || {};
|
|
708
790
|
const adminPath = opts.adminPath || "/admin";
|
|
@@ -803,44 +885,34 @@ function createCron(transport, options) {
|
|
|
803
885
|
function parseWhereFilter(where) {
|
|
804
886
|
if (!where) return void 0;
|
|
805
887
|
const filters = {};
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
888
|
+
const OP_TO_FILTER = {
|
|
889
|
+
"eq": "==",
|
|
890
|
+
"neq": "!=",
|
|
891
|
+
"gt": ">",
|
|
892
|
+
"gte": ">=",
|
|
893
|
+
"lt": "<",
|
|
894
|
+
"lte": "<=",
|
|
895
|
+
"==": "==",
|
|
896
|
+
"!=": "!=",
|
|
897
|
+
">": ">",
|
|
898
|
+
">=": ">=",
|
|
899
|
+
"<": "<",
|
|
900
|
+
"<=": "<=",
|
|
901
|
+
"in": "in",
|
|
902
|
+
"nin": "not-in",
|
|
903
|
+
"not-in": "not-in",
|
|
904
|
+
"cs": "array-contains",
|
|
905
|
+
"csa": "array-contains-any",
|
|
906
|
+
"array-contains": "array-contains",
|
|
907
|
+
"array-contains-any": "array-contains-any"
|
|
908
|
+
};
|
|
909
|
+
const parseSingle = (rawValue, fieldKey) => {
|
|
910
|
+
if (rawValue === null) return ["==", null];
|
|
911
|
+
if (typeof rawValue === "boolean") return ["==", rawValue];
|
|
912
|
+
if (typeof rawValue === "number") return ["==", rawValue];
|
|
913
|
+
if (Array.isArray(rawValue) && rawValue.length === 2 && typeof rawValue[0] === "string") {
|
|
820
914
|
const [rawOp, val] = rawValue;
|
|
821
|
-
|
|
822
|
-
"eq": "==",
|
|
823
|
-
"neq": "!=",
|
|
824
|
-
"gt": ">",
|
|
825
|
-
"gte": ">=",
|
|
826
|
-
"lt": "<",
|
|
827
|
-
"lte": "<=",
|
|
828
|
-
"==": "==",
|
|
829
|
-
"!=": "!=",
|
|
830
|
-
">": ">",
|
|
831
|
-
">=": ">=",
|
|
832
|
-
"<": "<",
|
|
833
|
-
"<=": "<=",
|
|
834
|
-
"in": "in",
|
|
835
|
-
"nin": "not-in",
|
|
836
|
-
"not-in": "not-in",
|
|
837
|
-
"cs": "array-contains",
|
|
838
|
-
"csa": "array-contains-any",
|
|
839
|
-
"array-contains": "array-contains",
|
|
840
|
-
"array-contains-any": "array-contains-any"
|
|
841
|
-
};
|
|
842
|
-
filters[key] = [OP_TO_FILTER[rawOp] ?? "==", val];
|
|
843
|
-
continue;
|
|
915
|
+
return [OP_TO_FILTER[rawOp] ?? "==", val];
|
|
844
916
|
}
|
|
845
917
|
const value = String(rawValue);
|
|
846
918
|
const dotIndex = value.indexOf(".");
|
|
@@ -890,10 +962,17 @@ function parseWhereFilter(where) {
|
|
|
890
962
|
if (val === "true") val = true;
|
|
891
963
|
else if (val === "false") val = false;
|
|
892
964
|
else if (val === "null") val = null;
|
|
893
|
-
else if (typeof val === "string" && /^[0-9]+(\.[0-9]+)?$/.test(val) &&
|
|
894
|
-
|
|
965
|
+
else if (typeof val === "string" && /^[0-9]+(\.[0-9]+)?$/.test(val) && fieldKey !== "id" && !fieldKey.endsWith("_id")) val = Number(val);
|
|
966
|
+
return [op, val];
|
|
967
|
+
} else {
|
|
968
|
+
return ["==", value];
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
for (const [key, rawValue] of Object.entries(where)) {
|
|
972
|
+
if (Array.isArray(rawValue) && rawValue.length > 0 && Array.isArray(rawValue[0])) {
|
|
973
|
+
filters[key] = rawValue.map((r) => parseSingle(r, key));
|
|
895
974
|
} else {
|
|
896
|
-
filters[key] =
|
|
975
|
+
filters[key] = parseSingle(rawValue, key);
|
|
897
976
|
}
|
|
898
977
|
}
|
|
899
978
|
return filters;
|
|
@@ -917,9 +996,16 @@ function createCollectionClient(transport, slug, ws) {
|
|
|
917
996
|
};
|
|
918
997
|
},
|
|
919
998
|
async findById(id) {
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
999
|
+
try {
|
|
1000
|
+
const raw = await transport.request(`${basePath}/${encodeURIComponent(String(id))}`, { method: "GET" });
|
|
1001
|
+
if (!raw) return void 0;
|
|
1002
|
+
return rowToEntity(raw, slug);
|
|
1003
|
+
} catch (err) {
|
|
1004
|
+
if (err instanceof RebaseApiError && err.status === 404) {
|
|
1005
|
+
return void 0;
|
|
1006
|
+
}
|
|
1007
|
+
throw err;
|
|
1008
|
+
}
|
|
923
1009
|
},
|
|
924
1010
|
async create(data, id) {
|
|
925
1011
|
const body = { ...data };
|
|
@@ -945,14 +1031,22 @@ function createCollectionClient(transport, slug, ws) {
|
|
|
945
1031
|
});
|
|
946
1032
|
},
|
|
947
1033
|
async count(params) {
|
|
948
|
-
const countParams = {
|
|
1034
|
+
const countParams = {
|
|
1035
|
+
...params,
|
|
1036
|
+
limit: void 0,
|
|
1037
|
+
offset: void 0
|
|
1038
|
+
};
|
|
949
1039
|
const qs = buildQueryString(countParams);
|
|
950
1040
|
const raw = await transport.request(basePath + "/count" + qs, { method: "GET" });
|
|
951
1041
|
return raw.count ?? 0;
|
|
952
1042
|
},
|
|
953
1043
|
// Fluent builder instantiation
|
|
954
|
-
where(
|
|
955
|
-
|
|
1044
|
+
where(columnOrCondition, operator, value) {
|
|
1045
|
+
const builder = new QueryBuilder(client);
|
|
1046
|
+
if (typeof columnOrCondition === "object") {
|
|
1047
|
+
return builder.where(columnOrCondition);
|
|
1048
|
+
}
|
|
1049
|
+
return builder.where(columnOrCondition, operator, value);
|
|
956
1050
|
},
|
|
957
1051
|
orderBy(column, ascending) {
|
|
958
1052
|
return new QueryBuilder(client).orderBy(column, ascending);
|
|
@@ -1089,9 +1183,12 @@ class RebaseWebSocketClient {
|
|
|
1089
1183
|
isAuthenticated = false;
|
|
1090
1184
|
authPromise = null;
|
|
1091
1185
|
WebSocketConstructor;
|
|
1186
|
+
onUnauthorized;
|
|
1187
|
+
refreshInProgress = null;
|
|
1092
1188
|
constructor(config) {
|
|
1093
1189
|
this.websocketUrl = config.websocketUrl;
|
|
1094
1190
|
this.getAuthToken = config.getAuthToken;
|
|
1191
|
+
this.onUnauthorized = config.onUnauthorized;
|
|
1095
1192
|
this.WebSocketConstructor = config.WebSocket || (typeof WebSocket !== "undefined" ? WebSocket : void 0);
|
|
1096
1193
|
if (!this.WebSocketConstructor) {
|
|
1097
1194
|
console.warn("WebSocket is not defined in this environment. Realtime subscriptions will not work unless you provide a WebSocket implementation in the config.");
|
|
@@ -1256,6 +1353,42 @@ class RebaseWebSocketClient {
|
|
|
1256
1353
|
this.initWebSocket();
|
|
1257
1354
|
}, delay);
|
|
1258
1355
|
}
|
|
1356
|
+
isAuthError(message) {
|
|
1357
|
+
if (message.type === "AUTH_ERROR") return true;
|
|
1358
|
+
const { errorMessage, errorCode } = extractMessageError(message);
|
|
1359
|
+
if (errorCode === "UNAUTHORIZED" || errorCode === "JWT_EXPIRED" || errorCode === "AUTH_ERROR") return true;
|
|
1360
|
+
const lowerMessage = errorMessage.toLowerCase();
|
|
1361
|
+
return lowerMessage.includes("unauthorized") || lowerMessage.includes("token expired") || lowerMessage.includes("token is expired") || lowerMessage.includes("invalid token") || lowerMessage.includes("session expired") || lowerMessage.includes("auth error");
|
|
1362
|
+
}
|
|
1363
|
+
async handleAuthFailure() {
|
|
1364
|
+
if (this.refreshInProgress) {
|
|
1365
|
+
return this.refreshInProgress;
|
|
1366
|
+
}
|
|
1367
|
+
this.refreshInProgress = (async () => {
|
|
1368
|
+
this.isAuthenticated = false;
|
|
1369
|
+
this.authPromise = null;
|
|
1370
|
+
if (this.onUnauthorized) {
|
|
1371
|
+
try {
|
|
1372
|
+
const refreshed = await this.onUnauthorized();
|
|
1373
|
+
if (refreshed && this.getAuthToken) {
|
|
1374
|
+
const token = await this.getAuthToken();
|
|
1375
|
+
if (token) {
|
|
1376
|
+
await this.authenticate(token);
|
|
1377
|
+
return true;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
} catch (error) {
|
|
1381
|
+
console.error("WebSocket auth refresh failed:", error);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
return false;
|
|
1385
|
+
})();
|
|
1386
|
+
try {
|
|
1387
|
+
return await this.refreshInProgress;
|
|
1388
|
+
} finally {
|
|
1389
|
+
this.refreshInProgress = null;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1259
1392
|
handleWebSocketMessage(message) {
|
|
1260
1393
|
const {
|
|
1261
1394
|
type,
|
|
@@ -1263,16 +1396,28 @@ class RebaseWebSocketClient {
|
|
|
1263
1396
|
subscriptionId
|
|
1264
1397
|
} = message;
|
|
1265
1398
|
if (requestId && this.pendingRequests.has(requestId)) {
|
|
1266
|
-
const
|
|
1267
|
-
resolve,
|
|
1268
|
-
reject
|
|
1269
|
-
} = this.pendingRequests.get(requestId);
|
|
1270
|
-
this.pendingRequests.delete(requestId);
|
|
1399
|
+
const pendingReq = this.pendingRequests.get(requestId);
|
|
1271
1400
|
if (type === "ERROR" || type === "AUTH_ERROR" || message.error) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1401
|
+
if (this.isAuthError(message)) {
|
|
1402
|
+
this.pendingRequests.delete(requestId);
|
|
1403
|
+
this.handleAuthFailure().then((refreshed) => {
|
|
1404
|
+
if (refreshed && pendingReq.message) {
|
|
1405
|
+
this.doSendMessage(pendingReq.message, pendingReq.resolve, pendingReq.reject).catch(pendingReq.reject);
|
|
1406
|
+
} else {
|
|
1407
|
+
const { errorMessage, errorCode } = extractMessageError(message);
|
|
1408
|
+
pendingReq.reject(new ApiError(errorMessage, errorMessage, errorCode));
|
|
1409
|
+
}
|
|
1410
|
+
}).catch((err) => {
|
|
1411
|
+
pendingReq.reject(err);
|
|
1412
|
+
});
|
|
1413
|
+
} else {
|
|
1414
|
+
this.pendingRequests.delete(requestId);
|
|
1415
|
+
const { errorMessage, errorCode } = extractMessageError(message);
|
|
1416
|
+
pendingReq.reject(new ApiError(errorMessage, errorMessage, errorCode));
|
|
1417
|
+
}
|
|
1274
1418
|
} else {
|
|
1275
|
-
|
|
1419
|
+
this.pendingRequests.delete(requestId);
|
|
1420
|
+
pendingReq.resolve(message.payload || message);
|
|
1276
1421
|
}
|
|
1277
1422
|
return;
|
|
1278
1423
|
}
|
|
@@ -1363,6 +1508,40 @@ class RebaseWebSocketClient {
|
|
|
1363
1508
|
if (collectionKey) {
|
|
1364
1509
|
const collectionSub = this.collectionSubscriptions.get(collectionKey);
|
|
1365
1510
|
if (collectionSub) {
|
|
1511
|
+
if (this.isAuthError(message)) {
|
|
1512
|
+
this.handleAuthFailure().then((refreshed) => {
|
|
1513
|
+
if (refreshed) {
|
|
1514
|
+
const oldBackendId = collectionSub.backendSubscriptionId;
|
|
1515
|
+
const newBackendId = `collection_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1516
|
+
collectionSub.backendSubscriptionId = newBackendId;
|
|
1517
|
+
this.backendToCollectionKey.delete(oldBackendId);
|
|
1518
|
+
this.backendToCollectionKey.set(newBackendId, collectionKey);
|
|
1519
|
+
this.sendMessage({
|
|
1520
|
+
type: "subscribe_collection",
|
|
1521
|
+
payload: {
|
|
1522
|
+
...collectionSub.props,
|
|
1523
|
+
subscriptionId: newBackendId
|
|
1524
|
+
}
|
|
1525
|
+
}).catch((error2) => {
|
|
1526
|
+
console.error("[WS] Failed to re-subscribe collection after auth refresh:", collectionKey, error2);
|
|
1527
|
+
collectionSub.callbacks.forEach((callback) => {
|
|
1528
|
+
if (callback.onError) callback.onError(error2);
|
|
1529
|
+
});
|
|
1530
|
+
});
|
|
1531
|
+
} else {
|
|
1532
|
+
const { errorMessage: errorMessage2, errorCode: errorCode2 } = extractMessageError(message);
|
|
1533
|
+
const error2 = new ApiError(errorMessage2, errorMessage2, errorCode2);
|
|
1534
|
+
collectionSub.callbacks.forEach((callback) => {
|
|
1535
|
+
if (callback.onError) callback.onError(error2);
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
}).catch((err) => {
|
|
1539
|
+
collectionSub.callbacks.forEach((callback) => {
|
|
1540
|
+
if (callback.onError) callback.onError(err);
|
|
1541
|
+
});
|
|
1542
|
+
});
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1366
1545
|
const { errorMessage, errorCode } = extractMessageError(message);
|
|
1367
1546
|
const error = new ApiError(errorMessage, errorMessage, errorCode);
|
|
1368
1547
|
collectionSub.callbacks.forEach((callback) => {
|
|
@@ -1377,6 +1556,40 @@ class RebaseWebSocketClient {
|
|
|
1377
1556
|
if (entityKey) {
|
|
1378
1557
|
const entitySub = this.entitySubscriptions.get(entityKey);
|
|
1379
1558
|
if (entitySub) {
|
|
1559
|
+
if (this.isAuthError(message)) {
|
|
1560
|
+
this.handleAuthFailure().then((refreshed) => {
|
|
1561
|
+
if (refreshed) {
|
|
1562
|
+
const oldBackendId = entitySub.backendSubscriptionId;
|
|
1563
|
+
const newBackendId = `entity_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1564
|
+
entitySub.backendSubscriptionId = newBackendId;
|
|
1565
|
+
this.backendToEntityKey.delete(oldBackendId);
|
|
1566
|
+
this.backendToEntityKey.set(newBackendId, entityKey);
|
|
1567
|
+
this.sendMessage({
|
|
1568
|
+
type: "subscribe_entity",
|
|
1569
|
+
payload: {
|
|
1570
|
+
...entitySub.props,
|
|
1571
|
+
subscriptionId: newBackendId
|
|
1572
|
+
}
|
|
1573
|
+
}).catch((error2) => {
|
|
1574
|
+
console.error("[WS] Failed to re-subscribe entity after auth refresh:", entityKey, error2);
|
|
1575
|
+
entitySub.callbacks.forEach((callback) => {
|
|
1576
|
+
if (callback.onError) callback.onError(error2);
|
|
1577
|
+
});
|
|
1578
|
+
});
|
|
1579
|
+
} else {
|
|
1580
|
+
const { errorMessage: errorMessage2, errorCode: errorCode2 } = extractMessageError(message);
|
|
1581
|
+
const error2 = new ApiError(errorMessage2, errorMessage2, errorCode2);
|
|
1582
|
+
entitySub.callbacks.forEach((callback) => {
|
|
1583
|
+
if (callback.onError) callback.onError(error2);
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
}).catch((err) => {
|
|
1587
|
+
entitySub.callbacks.forEach((callback) => {
|
|
1588
|
+
if (callback.onError) callback.onError(err);
|
|
1589
|
+
});
|
|
1590
|
+
});
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1380
1593
|
const { errorMessage, errorCode } = extractMessageError(message);
|
|
1381
1594
|
const error = new ApiError(errorMessage, errorMessage, errorCode);
|
|
1382
1595
|
entitySub.callbacks.forEach((callback) => {
|
|
@@ -2057,14 +2270,27 @@ function createStorage(transport) {
|
|
|
2057
2270
|
};
|
|
2058
2271
|
}
|
|
2059
2272
|
function deriveWebSocketUrl(baseUrl) {
|
|
2060
|
-
if (
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2273
|
+
if (typeof window !== "undefined") {
|
|
2274
|
+
let absoluteUrl = "";
|
|
2275
|
+
if (!baseUrl) {
|
|
2276
|
+
absoluteUrl = window.location.origin;
|
|
2277
|
+
} else if (/^https?:\/\//i.test(baseUrl) || /^wss?:\/\//i.test(baseUrl)) {
|
|
2278
|
+
absoluteUrl = baseUrl;
|
|
2279
|
+
} else {
|
|
2280
|
+
try {
|
|
2281
|
+
absoluteUrl = new URL(baseUrl, window.location.href).origin;
|
|
2282
|
+
} catch {
|
|
2283
|
+
absoluteUrl = window.location.origin;
|
|
2284
|
+
}
|
|
2064
2285
|
}
|
|
2286
|
+
const protocol = absoluteUrl.startsWith("https:") || absoluteUrl.startsWith("wss:") ? "wss:" : "ws:";
|
|
2287
|
+
return absoluteUrl.replace(/^https?:\/\//i, `${protocol}//`).replace(/^wss?:\/\//i, `${protocol}//`).replace(/\/$/, "");
|
|
2288
|
+
}
|
|
2289
|
+
if (!baseUrl) return "";
|
|
2290
|
+
if (!/^https?:\/\//i.test(baseUrl) && !/^wss?:\/\//i.test(baseUrl)) {
|
|
2065
2291
|
return "";
|
|
2066
2292
|
}
|
|
2067
|
-
return baseUrl.replace(/^https
|
|
2293
|
+
return baseUrl.replace(/^https?:\/\//i, (match) => match.toLowerCase() === "https://" ? "wss://" : "ws://").replace(/\/$/, "");
|
|
2068
2294
|
}
|
|
2069
2295
|
function createRebaseClient(options) {
|
|
2070
2296
|
const transport = createTransport(options);
|
|
@@ -2076,12 +2302,27 @@ function createRebaseClient(options) {
|
|
|
2076
2302
|
const resolvedWsUrl = options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl);
|
|
2077
2303
|
let ws;
|
|
2078
2304
|
if (resolvedWsUrl) {
|
|
2305
|
+
const wsOnUnauthorized = options.onUnauthorized || (async () => {
|
|
2306
|
+
try {
|
|
2307
|
+
await auth.refreshSession();
|
|
2308
|
+
return true;
|
|
2309
|
+
} catch (e) {
|
|
2310
|
+
return false;
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2079
2313
|
ws = new RebaseWebSocketClient({
|
|
2080
2314
|
websocketUrl: resolvedWsUrl,
|
|
2081
2315
|
getAuthToken: async () => {
|
|
2082
|
-
|
|
2316
|
+
let session = auth.getSession();
|
|
2317
|
+
if (session && session.expiresAt <= Date.now() + 1e4) {
|
|
2318
|
+
try {
|
|
2319
|
+
session = await auth.refreshSession();
|
|
2320
|
+
} catch (e) {
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2083
2323
|
return session?.accessToken || options.token || "";
|
|
2084
|
-
}
|
|
2324
|
+
},
|
|
2325
|
+
onUnauthorized: wsOnUnauthorized
|
|
2085
2326
|
});
|
|
2086
2327
|
auth.onAuthStateChange((event, session) => {
|
|
2087
2328
|
if (!ws) return;
|
|
@@ -2156,16 +2397,20 @@ export {
|
|
|
2156
2397
|
QueryBuilder2 as QueryBuilder,
|
|
2157
2398
|
RebaseApiError,
|
|
2158
2399
|
RebaseWebSocketClient,
|
|
2400
|
+
and,
|
|
2159
2401
|
buildQueryString,
|
|
2402
|
+
cond,
|
|
2160
2403
|
createAdmin,
|
|
2161
2404
|
createAuth,
|
|
2162
2405
|
createCollectionClient,
|
|
2406
|
+
createCookieStorage,
|
|
2163
2407
|
createCron,
|
|
2164
2408
|
createFunctionsClient,
|
|
2165
2409
|
createMemoryStorage,
|
|
2166
2410
|
createRebaseClient,
|
|
2167
2411
|
createStorage,
|
|
2168
2412
|
createTransport,
|
|
2413
|
+
or,
|
|
2169
2414
|
rebaseReviver
|
|
2170
2415
|
};
|
|
2171
2416
|
//# sourceMappingURL=index.es.js.map
|