@rebasepro/client 0.3.0 → 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/admin.d.ts +1 -31
- package/dist/auth.d.ts +8 -0
- package/dist/collection.d.ts +4 -3
- package/dist/index.es.js +315 -98
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +323 -97
- 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/admin.ts +1 -41
- 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/index.umd.js
CHANGED
|
@@ -66,15 +66,33 @@
|
|
|
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(cond) {
|
|
82
|
+
if ("type" in cond) {
|
|
83
|
+
const sub = cond.conditions.map(serializeLogicalCondition).join(",");
|
|
84
|
+
return `${cond.type}(${sub})`;
|
|
85
|
+
} else {
|
|
86
|
+
const op = OP_MAP[cond.operator] ?? cond.operator;
|
|
87
|
+
let formattedValue = cond.value;
|
|
88
|
+
if (Array.isArray(cond.value)) {
|
|
89
|
+
formattedValue = `(${cond.value.join(",")})`;
|
|
90
|
+
} else if (cond.value === null) {
|
|
91
|
+
formattedValue = "null";
|
|
92
|
+
}
|
|
93
|
+
return `${cond.column}.${op}.${formattedValue}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
78
96
|
function buildQueryString(params) {
|
|
79
97
|
if (!params) return "";
|
|
80
98
|
const parts = [];
|
|
@@ -90,10 +108,22 @@
|
|
|
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 @@
|
|
|
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";
|
|
@@ -742,29 +824,6 @@
|
|
|
742
824
|
method: "DELETE"
|
|
743
825
|
});
|
|
744
826
|
}
|
|
745
|
-
async function listRoles() {
|
|
746
|
-
return transport.request(adminPath + "/roles", { method: "GET" });
|
|
747
|
-
}
|
|
748
|
-
async function getRole(roleId) {
|
|
749
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), { method: "GET" });
|
|
750
|
-
}
|
|
751
|
-
async function createRole(data) {
|
|
752
|
-
return transport.request(adminPath + "/roles", {
|
|
753
|
-
method: "POST",
|
|
754
|
-
body: JSON.stringify(data)
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
async function updateRole(roleId, data) {
|
|
758
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
759
|
-
method: "PUT",
|
|
760
|
-
body: JSON.stringify(data)
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
async function deleteRole(roleId) {
|
|
764
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
765
|
-
method: "DELETE"
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
827
|
async function bootstrap() {
|
|
769
828
|
return transport.request(adminPath + "/bootstrap", {
|
|
770
829
|
method: "POST"
|
|
@@ -777,11 +836,6 @@
|
|
|
777
836
|
createUser,
|
|
778
837
|
updateUser,
|
|
779
838
|
deleteUser,
|
|
780
|
-
listRoles,
|
|
781
|
-
getRole,
|
|
782
|
-
createRole,
|
|
783
|
-
updateRole,
|
|
784
|
-
deleteRole,
|
|
785
839
|
bootstrap
|
|
786
840
|
};
|
|
787
841
|
}
|
|
@@ -831,44 +885,34 @@
|
|
|
831
885
|
function parseWhereFilter(where) {
|
|
832
886
|
if (!where) return void 0;
|
|
833
887
|
const filters = {};
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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") {
|
|
848
914
|
const [rawOp, val] = rawValue;
|
|
849
|
-
|
|
850
|
-
"eq": "==",
|
|
851
|
-
"neq": "!=",
|
|
852
|
-
"gt": ">",
|
|
853
|
-
"gte": ">=",
|
|
854
|
-
"lt": "<",
|
|
855
|
-
"lte": "<=",
|
|
856
|
-
"==": "==",
|
|
857
|
-
"!=": "!=",
|
|
858
|
-
">": ">",
|
|
859
|
-
">=": ">=",
|
|
860
|
-
"<": "<",
|
|
861
|
-
"<=": "<=",
|
|
862
|
-
"in": "in",
|
|
863
|
-
"nin": "not-in",
|
|
864
|
-
"not-in": "not-in",
|
|
865
|
-
"cs": "array-contains",
|
|
866
|
-
"csa": "array-contains-any",
|
|
867
|
-
"array-contains": "array-contains",
|
|
868
|
-
"array-contains-any": "array-contains-any"
|
|
869
|
-
};
|
|
870
|
-
filters[key] = [OP_TO_FILTER[rawOp] ?? "==", val];
|
|
871
|
-
continue;
|
|
915
|
+
return [OP_TO_FILTER[rawOp] ?? "==", val];
|
|
872
916
|
}
|
|
873
917
|
const value = String(rawValue);
|
|
874
918
|
const dotIndex = value.indexOf(".");
|
|
@@ -918,10 +962,17 @@
|
|
|
918
962
|
if (val === "true") val = true;
|
|
919
963
|
else if (val === "false") val = false;
|
|
920
964
|
else if (val === "null") val = null;
|
|
921
|
-
else if (typeof val === "string" && /^[0-9]+(\.[0-9]+)?$/.test(val) &&
|
|
922
|
-
|
|
965
|
+
else if (typeof val === "string" && /^[0-9]+(\.[0-9]+)?$/.test(val) && fieldKey !== "id" && !fieldKey.endsWith("_id")) val = Number(val);
|
|
966
|
+
return [op, val];
|
|
923
967
|
} else {
|
|
924
|
-
|
|
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));
|
|
974
|
+
} else {
|
|
975
|
+
filters[key] = parseSingle(rawValue, key);
|
|
925
976
|
}
|
|
926
977
|
}
|
|
927
978
|
return filters;
|
|
@@ -945,9 +996,16 @@
|
|
|
945
996
|
};
|
|
946
997
|
},
|
|
947
998
|
async findById(id) {
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
+
}
|
|
951
1009
|
},
|
|
952
1010
|
async create(data, id) {
|
|
953
1011
|
const body = { ...data };
|
|
@@ -973,14 +1031,22 @@
|
|
|
973
1031
|
});
|
|
974
1032
|
},
|
|
975
1033
|
async count(params) {
|
|
976
|
-
const countParams = {
|
|
1034
|
+
const countParams = {
|
|
1035
|
+
...params,
|
|
1036
|
+
limit: void 0,
|
|
1037
|
+
offset: void 0
|
|
1038
|
+
};
|
|
977
1039
|
const qs = buildQueryString(countParams);
|
|
978
1040
|
const raw = await transport.request(basePath + "/count" + qs, { method: "GET" });
|
|
979
1041
|
return raw.count ?? 0;
|
|
980
1042
|
},
|
|
981
1043
|
// Fluent builder instantiation
|
|
982
|
-
where(
|
|
983
|
-
|
|
1044
|
+
where(columnOrCondition, operator, value) {
|
|
1045
|
+
const builder = new common.QueryBuilder(client);
|
|
1046
|
+
if (typeof columnOrCondition === "object") {
|
|
1047
|
+
return builder.where(columnOrCondition);
|
|
1048
|
+
}
|
|
1049
|
+
return builder.where(columnOrCondition, operator, value);
|
|
984
1050
|
},
|
|
985
1051
|
orderBy(column, ascending) {
|
|
986
1052
|
return new common.QueryBuilder(client).orderBy(column, ascending);
|
|
@@ -1117,9 +1183,12 @@
|
|
|
1117
1183
|
isAuthenticated = false;
|
|
1118
1184
|
authPromise = null;
|
|
1119
1185
|
WebSocketConstructor;
|
|
1186
|
+
onUnauthorized;
|
|
1187
|
+
refreshInProgress = null;
|
|
1120
1188
|
constructor(config) {
|
|
1121
1189
|
this.websocketUrl = config.websocketUrl;
|
|
1122
1190
|
this.getAuthToken = config.getAuthToken;
|
|
1191
|
+
this.onUnauthorized = config.onUnauthorized;
|
|
1123
1192
|
this.WebSocketConstructor = config.WebSocket || (typeof WebSocket !== "undefined" ? WebSocket : void 0);
|
|
1124
1193
|
if (!this.WebSocketConstructor) {
|
|
1125
1194
|
console.warn("WebSocket is not defined in this environment. Realtime subscriptions will not work unless you provide a WebSocket implementation in the config.");
|
|
@@ -1284,6 +1353,42 @@
|
|
|
1284
1353
|
this.initWebSocket();
|
|
1285
1354
|
}, delay);
|
|
1286
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
|
+
}
|
|
1287
1392
|
handleWebSocketMessage(message) {
|
|
1288
1393
|
const {
|
|
1289
1394
|
type,
|
|
@@ -1291,16 +1396,28 @@
|
|
|
1291
1396
|
subscriptionId
|
|
1292
1397
|
} = message;
|
|
1293
1398
|
if (requestId && this.pendingRequests.has(requestId)) {
|
|
1294
|
-
const
|
|
1295
|
-
resolve,
|
|
1296
|
-
reject
|
|
1297
|
-
} = this.pendingRequests.get(requestId);
|
|
1298
|
-
this.pendingRequests.delete(requestId);
|
|
1399
|
+
const pendingReq = this.pendingRequests.get(requestId);
|
|
1299
1400
|
if (type === "ERROR" || type === "AUTH_ERROR" || message.error) {
|
|
1300
|
-
|
|
1301
|
-
|
|
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
|
+
}
|
|
1302
1418
|
} else {
|
|
1303
|
-
|
|
1419
|
+
this.pendingRequests.delete(requestId);
|
|
1420
|
+
pendingReq.resolve(message.payload || message);
|
|
1304
1421
|
}
|
|
1305
1422
|
return;
|
|
1306
1423
|
}
|
|
@@ -1391,6 +1508,40 @@
|
|
|
1391
1508
|
if (collectionKey) {
|
|
1392
1509
|
const collectionSub = this.collectionSubscriptions.get(collectionKey);
|
|
1393
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
|
+
}
|
|
1394
1545
|
const { errorMessage, errorCode } = extractMessageError(message);
|
|
1395
1546
|
const error = new ApiError(errorMessage, errorMessage, errorCode);
|
|
1396
1547
|
collectionSub.callbacks.forEach((callback) => {
|
|
@@ -1405,6 +1556,40 @@
|
|
|
1405
1556
|
if (entityKey) {
|
|
1406
1557
|
const entitySub = this.entitySubscriptions.get(entityKey);
|
|
1407
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
|
+
}
|
|
1408
1593
|
const { errorMessage, errorCode } = extractMessageError(message);
|
|
1409
1594
|
const error = new ApiError(errorMessage, errorMessage, errorCode);
|
|
1410
1595
|
entitySub.callbacks.forEach((callback) => {
|
|
@@ -2085,14 +2270,27 @@
|
|
|
2085
2270
|
};
|
|
2086
2271
|
}
|
|
2087
2272
|
function deriveWebSocketUrl(baseUrl) {
|
|
2088
|
-
if (
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
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
|
+
}
|
|
2092
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)) {
|
|
2093
2291
|
return "";
|
|
2094
2292
|
}
|
|
2095
|
-
return baseUrl.replace(/^https
|
|
2293
|
+
return baseUrl.replace(/^https?:\/\//i, (match) => match.toLowerCase() === "https://" ? "wss://" : "ws://").replace(/\/$/, "");
|
|
2096
2294
|
}
|
|
2097
2295
|
function createRebaseClient(options) {
|
|
2098
2296
|
const transport = createTransport(options);
|
|
@@ -2104,12 +2302,27 @@
|
|
|
2104
2302
|
const resolvedWsUrl = options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl);
|
|
2105
2303
|
let ws;
|
|
2106
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
|
+
});
|
|
2107
2313
|
ws = new RebaseWebSocketClient({
|
|
2108
2314
|
websocketUrl: resolvedWsUrl,
|
|
2109
2315
|
getAuthToken: async () => {
|
|
2110
|
-
|
|
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
|
+
}
|
|
2111
2323
|
return session?.accessToken || options.token || "";
|
|
2112
|
-
}
|
|
2324
|
+
},
|
|
2325
|
+
onUnauthorized: wsOnUnauthorized
|
|
2113
2326
|
});
|
|
2114
2327
|
auth.onAuthStateChange((event, session) => {
|
|
2115
2328
|
if (!ws) return;
|
|
@@ -2183,6 +2396,18 @@
|
|
|
2183
2396
|
enumerable: true,
|
|
2184
2397
|
get: () => common.QueryBuilder
|
|
2185
2398
|
});
|
|
2399
|
+
Object.defineProperty(exports2, "and", {
|
|
2400
|
+
enumerable: true,
|
|
2401
|
+
get: () => common.and
|
|
2402
|
+
});
|
|
2403
|
+
Object.defineProperty(exports2, "cond", {
|
|
2404
|
+
enumerable: true,
|
|
2405
|
+
get: () => common.cond
|
|
2406
|
+
});
|
|
2407
|
+
Object.defineProperty(exports2, "or", {
|
|
2408
|
+
enumerable: true,
|
|
2409
|
+
get: () => common.or
|
|
2410
|
+
});
|
|
2186
2411
|
exports2.ApiError = ApiError;
|
|
2187
2412
|
exports2.RebaseApiError = RebaseApiError;
|
|
2188
2413
|
exports2.RebaseWebSocketClient = RebaseWebSocketClient;
|
|
@@ -2190,6 +2415,7 @@
|
|
|
2190
2415
|
exports2.createAdmin = createAdmin;
|
|
2191
2416
|
exports2.createAuth = createAuth;
|
|
2192
2417
|
exports2.createCollectionClient = createCollectionClient;
|
|
2418
|
+
exports2.createCookieStorage = createCookieStorage;
|
|
2193
2419
|
exports2.createCron = createCron;
|
|
2194
2420
|
exports2.createFunctionsClient = createFunctionsClient;
|
|
2195
2421
|
exports2.createMemoryStorage = createMemoryStorage;
|