@rebasepro/client 0.6.1 → 0.8.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 +13 -0
- package/dist/api-keys.d.ts +66 -0
- package/dist/auth.d.ts +10 -0
- package/dist/collection.d.ts +3 -3
- package/dist/index.d.ts +24 -1
- package/dist/index.es.js +218 -167
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +222 -169
- package/dist/index.umd.js.map +1 -1
- package/dist/storage-registry.d.ts +42 -0
- package/dist/storage.d.ts +9 -1
- package/package.json +5 -4
- package/src/admin.ts +19 -0
- package/src/api-keys.ts +110 -0
- package/src/auth.ts +30 -0
- package/src/collection.test.ts +2 -2
- package/src/collection.ts +41 -119
- package/src/index.ts +83 -2
- package/src/storage-registry.ts +102 -0
- package/src/storage.ts +30 -20
- package/src/transport.ts +8 -82
package/dist/index.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { QueryBuilder, and, cond, or, serializeFilter, serializeLogicalCondition } from "@rebasepro/common";
|
|
2
|
+
import { DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, GeoPoint, Vector } from "@rebasepro/types";
|
|
3
3
|
import { toSnakeCase } from "@rebasepro/utils";
|
|
4
4
|
//#region src/reviver.ts
|
|
5
5
|
function rebaseReviver(_key, value) {
|
|
@@ -42,61 +42,6 @@ var RebaseApiError = class extends Error {
|
|
|
42
42
|
this.details = details;
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
|
-
/**
|
|
46
|
-
* Maps a short operator alias to the PostgREST-style short code.
|
|
47
|
-
*/
|
|
48
|
-
var OP_MAP = {
|
|
49
|
-
"==": "eq",
|
|
50
|
-
"!=": "neq",
|
|
51
|
-
">": "gt",
|
|
52
|
-
">=": "gte",
|
|
53
|
-
"<": "lt",
|
|
54
|
-
"<=": "lte",
|
|
55
|
-
"not-in": "nin",
|
|
56
|
-
"array-contains": "cs",
|
|
57
|
-
"array-contains-any": "csa"
|
|
58
|
-
};
|
|
59
|
-
/**
|
|
60
|
-
* Normalise a single `WhereFieldValue` into the PostgREST query-string
|
|
61
|
-
* representation the backend expects.
|
|
62
|
-
*
|
|
63
|
-
* Supports:
|
|
64
|
-
* - `null` → `"eq.null"`
|
|
65
|
-
* - `true`/`false` → `"eq.true"` / `"eq.false"`
|
|
66
|
-
* - `42` → `"42"` (plain equality)
|
|
67
|
-
* - `"active"` → `"active"` (plain equality, backward-compat)
|
|
68
|
-
* - `"gte.18"` → `"gte.18"` (pass-through PostgREST string)
|
|
69
|
-
* - `[">=", 18]` → `"gte.18"` (tuple syntax)
|
|
70
|
-
* - `["in", [1,2]]` → `"in.(1,2)"` (tuple with array value)
|
|
71
|
-
* - `["!=", null]` → `"neq.null"`
|
|
72
|
-
*/
|
|
73
|
-
function normalizeWhereValue(value) {
|
|
74
|
-
if (value === null) return "eq.null";
|
|
75
|
-
if (typeof value === "boolean") return `eq.${value}`;
|
|
76
|
-
if (typeof value === "number") return String(value);
|
|
77
|
-
if (Array.isArray(value)) {
|
|
78
|
-
const [rawOp, val] = (Array.isArray(value[0]) ? value : [value])[0] || [];
|
|
79
|
-
if (rawOp) {
|
|
80
|
-
const op = OP_MAP[rawOp] ?? rawOp;
|
|
81
|
-
if (val === null) return `${op}.null`;
|
|
82
|
-
if (Array.isArray(val)) return `${op}.(${val.join(",")})`;
|
|
83
|
-
return `${op}.${val}`;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return String(value);
|
|
87
|
-
}
|
|
88
|
-
function serializeLogicalCondition(cond) {
|
|
89
|
-
if ("type" in cond) {
|
|
90
|
-
const sub = (cond.conditions ?? []).map(serializeLogicalCondition).join(",");
|
|
91
|
-
return `${cond.type}(${sub})`;
|
|
92
|
-
} else {
|
|
93
|
-
const op = OP_MAP[cond.operator] ?? cond.operator;
|
|
94
|
-
let formattedValue = cond.value;
|
|
95
|
-
if (Array.isArray(cond.value)) formattedValue = `(${cond.value.join(",")})`;
|
|
96
|
-
else if (cond.value === null) formattedValue = "null";
|
|
97
|
-
return `${cond.column}.${op}.${formattedValue}`;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
45
|
function buildQueryString(params) {
|
|
101
46
|
if (!params) return "";
|
|
102
47
|
const parts = [];
|
|
@@ -111,13 +56,10 @@ function buildQueryString(params) {
|
|
|
111
56
|
const serialized = (root.conditions ?? []).map(serializeLogicalCondition).join(",");
|
|
112
57
|
parts.push(`${root.type}=${encodeURIComponent(`(${serialized})`)}`);
|
|
113
58
|
}
|
|
114
|
-
if (params.where)
|
|
115
|
-
const
|
|
116
|
-
parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(
|
|
117
|
-
|
|
118
|
-
else {
|
|
119
|
-
const normalized = normalizeWhereValue(value);
|
|
120
|
-
parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(normalized)}`);
|
|
59
|
+
if (params.where) {
|
|
60
|
+
const serialized = serializeFilter(params.where);
|
|
61
|
+
for (const [field, value] of Object.entries(serialized)) if (Array.isArray(value)) for (const v of value) parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(v)}`);
|
|
62
|
+
else parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(value)}`);
|
|
121
63
|
}
|
|
122
64
|
return parts.length > 0 ? "?" + parts.join("&") : "";
|
|
123
65
|
}
|
|
@@ -583,6 +525,31 @@ function createAuth(transport, options) {
|
|
|
583
525
|
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
584
526
|
return body;
|
|
585
527
|
}
|
|
528
|
+
async function sendMagicLink(email) {
|
|
529
|
+
const res = await getFetch()(authUrl("/magic-link"), {
|
|
530
|
+
method: "POST",
|
|
531
|
+
headers: { "Content-Type": "application/json" },
|
|
532
|
+
body: JSON.stringify({ email })
|
|
533
|
+
});
|
|
534
|
+
const body = await res.json().catch(() => ({}));
|
|
535
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
536
|
+
return body;
|
|
537
|
+
}
|
|
538
|
+
async function verifyMagicLink(token) {
|
|
539
|
+
const res = await getFetch()(authUrl("/magic-link/verify"), {
|
|
540
|
+
method: "POST",
|
|
541
|
+
headers: { "Content-Type": "application/json" },
|
|
542
|
+
body: JSON.stringify({ token })
|
|
543
|
+
});
|
|
544
|
+
const body = await res.json().catch(() => ({}));
|
|
545
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
546
|
+
const session = handleAuthResponse(body, "SIGNED_IN");
|
|
547
|
+
return {
|
|
548
|
+
user: session.user,
|
|
549
|
+
accessToken: session.accessToken,
|
|
550
|
+
refreshToken: session.refreshToken
|
|
551
|
+
};
|
|
552
|
+
}
|
|
586
553
|
async function getSessions() {
|
|
587
554
|
return (await transport.request(authPath + "/sessions", { method: "GET" })).sessions;
|
|
588
555
|
}
|
|
@@ -659,6 +626,8 @@ function createAuth(transport, options) {
|
|
|
659
626
|
changePassword,
|
|
660
627
|
sendVerificationEmail,
|
|
661
628
|
verifyEmail,
|
|
629
|
+
sendMagicLink,
|
|
630
|
+
verifyMagicLink,
|
|
662
631
|
getSessions,
|
|
663
632
|
revokeSession,
|
|
664
633
|
revokeAllSessions,
|
|
@@ -739,6 +708,15 @@ function createAdmin(transport, options) {
|
|
|
739
708
|
async function deleteUser(userId) {
|
|
740
709
|
return transport.request(adminPath + "/users/" + encodeURIComponent(userId), { method: "DELETE" });
|
|
741
710
|
}
|
|
711
|
+
async function resetPassword(userId, options) {
|
|
712
|
+
return transport.request(adminPath + "/users/" + encodeURIComponent(userId) + "/reset-password", {
|
|
713
|
+
method: "POST",
|
|
714
|
+
...options?.password ? { body: JSON.stringify({ password: options.password }) } : {}
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
async function listRoles() {
|
|
718
|
+
return transport.request(adminPath + "/roles", { method: "GET" });
|
|
719
|
+
}
|
|
742
720
|
async function bootstrap() {
|
|
743
721
|
return transport.request(adminPath + "/bootstrap", { method: "POST" });
|
|
744
722
|
}
|
|
@@ -749,6 +727,8 @@ function createAdmin(transport, options) {
|
|
|
749
727
|
createUser,
|
|
750
728
|
updateUser,
|
|
751
729
|
deleteUser,
|
|
730
|
+
resetPassword,
|
|
731
|
+
listRoles,
|
|
752
732
|
bootstrap
|
|
753
733
|
};
|
|
754
734
|
}
|
|
@@ -786,95 +766,51 @@ function createCron(transport, options) {
|
|
|
786
766
|
};
|
|
787
767
|
}
|
|
788
768
|
//#endregion
|
|
789
|
-
//#region src/
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
"
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
case "eq":
|
|
831
|
-
op = "==";
|
|
832
|
-
break;
|
|
833
|
-
case "neq":
|
|
834
|
-
op = "!=";
|
|
835
|
-
break;
|
|
836
|
-
case "gt":
|
|
837
|
-
op = ">";
|
|
838
|
-
break;
|
|
839
|
-
case "gte":
|
|
840
|
-
op = ">=";
|
|
841
|
-
break;
|
|
842
|
-
case "lt":
|
|
843
|
-
op = "<";
|
|
844
|
-
break;
|
|
845
|
-
case "lte":
|
|
846
|
-
op = "<=";
|
|
847
|
-
break;
|
|
848
|
-
case "in":
|
|
849
|
-
op = "in";
|
|
850
|
-
val = valStr.startsWith("(") && valStr.endsWith(")") ? valStr.slice(1, -1).split(",").map((v) => v.trim()) : valStr.split(",");
|
|
851
|
-
break;
|
|
852
|
-
case "nin":
|
|
853
|
-
op = "not-in";
|
|
854
|
-
val = valStr.startsWith("(") && valStr.endsWith(")") ? valStr.slice(1, -1).split(",").map((v) => v.trim()) : valStr.split(",");
|
|
855
|
-
break;
|
|
856
|
-
case "cs":
|
|
857
|
-
op = "array-contains";
|
|
858
|
-
break;
|
|
859
|
-
case "csa":
|
|
860
|
-
op = "array-contains-any";
|
|
861
|
-
val = valStr.startsWith("(") && valStr.endsWith(")") ? valStr.slice(1, -1).split(",").map((v) => v.trim()) : valStr.split(",");
|
|
862
|
-
break;
|
|
863
|
-
default:
|
|
864
|
-
op = "==";
|
|
865
|
-
val = value;
|
|
866
|
-
}
|
|
867
|
-
if (val === "true") val = true;
|
|
868
|
-
else if (val === "false") val = false;
|
|
869
|
-
else if (val === "null") val = null;
|
|
870
|
-
else if (typeof val === "string" && /^[0-9]+(\.[0-9]+)?$/.test(val) && fieldKey !== "id" && !fieldKey.endsWith("_id")) val = Number(val);
|
|
871
|
-
return [op, val];
|
|
872
|
-
} else return ["==", value];
|
|
769
|
+
//#region src/api-keys.ts
|
|
770
|
+
/**
|
|
771
|
+
* Creates a client for managing API keys via the admin routes.
|
|
772
|
+
*
|
|
773
|
+
* @param transport - The shared HTTP transport created by `createTransport`.
|
|
774
|
+
* @param options - Optional overrides (e.g. a custom base path).
|
|
775
|
+
*/
|
|
776
|
+
function createApiKeys(transport, options) {
|
|
777
|
+
const apiKeysPath = options?.apiKeysPath || "/admin/api-keys";
|
|
778
|
+
/** List all API keys (masked). */
|
|
779
|
+
async function listKeys() {
|
|
780
|
+
return transport.request(apiKeysPath, { method: "GET" });
|
|
781
|
+
}
|
|
782
|
+
/** Get a single API key by ID (masked). */
|
|
783
|
+
async function getKey(id) {
|
|
784
|
+
return transport.request(apiKeysPath + "/" + encodeURIComponent(id), { method: "GET" });
|
|
785
|
+
}
|
|
786
|
+
/** Create a new API key. The full secret is included in the response. */
|
|
787
|
+
async function createKey(data) {
|
|
788
|
+
return transport.request(apiKeysPath, {
|
|
789
|
+
method: "POST",
|
|
790
|
+
body: JSON.stringify(data)
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
/** Update an existing API key. */
|
|
794
|
+
async function updateKey(id, data) {
|
|
795
|
+
return transport.request(apiKeysPath + "/" + encodeURIComponent(id), {
|
|
796
|
+
method: "PUT",
|
|
797
|
+
body: JSON.stringify(data)
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
/** Revoke (soft-delete) an API key. */
|
|
801
|
+
async function revokeKey(id) {
|
|
802
|
+
return transport.request(apiKeysPath + "/" + encodeURIComponent(id), { method: "DELETE" });
|
|
803
|
+
}
|
|
804
|
+
return {
|
|
805
|
+
listKeys,
|
|
806
|
+
getKey,
|
|
807
|
+
createKey,
|
|
808
|
+
updateKey,
|
|
809
|
+
revokeKey
|
|
873
810
|
};
|
|
874
|
-
for (const [key, rawValue] of Object.entries(where)) if (Array.isArray(rawValue) && rawValue.length > 0 && Array.isArray(rawValue[0])) filters[key] = rawValue.map((r) => parseSingle(r, key));
|
|
875
|
-
else filters[key] = parseSingle(rawValue, key);
|
|
876
|
-
return filters;
|
|
877
811
|
}
|
|
812
|
+
//#endregion
|
|
813
|
+
//#region src/collection.ts
|
|
878
814
|
/**
|
|
879
815
|
* Wrap a flat row (returned by the REST API as `{ id, ...fields }`) into
|
|
880
816
|
* a proper `Entity<M>` structure expected by the core framework.
|
|
@@ -939,8 +875,8 @@ function createCollectionClient(transport, slug, ws) {
|
|
|
939
875
|
if (typeof columnOrCondition === "object") return builder.where(columnOrCondition);
|
|
940
876
|
return builder.where(columnOrCondition, operator, value);
|
|
941
877
|
},
|
|
942
|
-
orderBy(column,
|
|
943
|
-
return new QueryBuilder(client).orderBy(column,
|
|
878
|
+
orderBy(column, direction) {
|
|
879
|
+
return new QueryBuilder(client).orderBy(column, direction);
|
|
944
880
|
},
|
|
945
881
|
limit(count) {
|
|
946
882
|
return new QueryBuilder(client).limit(count);
|
|
@@ -957,26 +893,45 @@ function createCollectionClient(transport, slug, ws) {
|
|
|
957
893
|
};
|
|
958
894
|
if (ws) {
|
|
959
895
|
client.listen = (params, onUpdate, onError) => {
|
|
960
|
-
|
|
896
|
+
let active = true;
|
|
897
|
+
let lastUpdateId = 0;
|
|
898
|
+
const unsub = ws.listenCollection({
|
|
961
899
|
path: slug,
|
|
962
|
-
filter:
|
|
900
|
+
filter: params?.where,
|
|
963
901
|
limit: params?.limit,
|
|
964
902
|
startAfter: params?.offset ? String(params.offset) : void 0,
|
|
965
903
|
orderBy: params?.orderBy?.split(":")[0],
|
|
966
904
|
order: params?.orderBy?.split(":")[1],
|
|
967
905
|
searchString: params?.searchString
|
|
968
906
|
}, (entities) => {
|
|
907
|
+
const currentUpdateId = ++lastUpdateId;
|
|
969
908
|
const requestedLimit = params?.limit || 20;
|
|
909
|
+
const offset = params?.offset || 0;
|
|
970
910
|
onUpdate({
|
|
971
911
|
data: entities,
|
|
972
912
|
meta: {
|
|
973
913
|
total: entities.length,
|
|
974
914
|
limit: requestedLimit,
|
|
975
|
-
offset
|
|
915
|
+
offset,
|
|
976
916
|
hasMore: entities.length >= requestedLimit
|
|
977
917
|
}
|
|
978
918
|
});
|
|
919
|
+
if (client.count) client.count(params).then((total) => {
|
|
920
|
+
if (active && currentUpdateId === lastUpdateId) onUpdate({
|
|
921
|
+
data: entities,
|
|
922
|
+
meta: {
|
|
923
|
+
total,
|
|
924
|
+
limit: requestedLimit,
|
|
925
|
+
offset,
|
|
926
|
+
hasMore: offset + entities.length < total
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
}).catch(() => {});
|
|
979
930
|
}, onError);
|
|
931
|
+
return () => {
|
|
932
|
+
active = false;
|
|
933
|
+
unsub();
|
|
934
|
+
};
|
|
980
935
|
};
|
|
981
936
|
client.listenById = (id, onUpdate, onError) => {
|
|
982
937
|
return ws.listenEntity({
|
|
@@ -1016,17 +971,31 @@ function createFunctionsClient(transport) {
|
|
|
1016
971
|
}
|
|
1017
972
|
//#endregion
|
|
1018
973
|
//#region src/storage.ts
|
|
1019
|
-
|
|
974
|
+
/**
|
|
975
|
+
* Create a StorageSource that talks to the Rebase backend REST API.
|
|
976
|
+
*
|
|
977
|
+
* @param transport - HTTP transport instance
|
|
978
|
+
* @param storageId - Optional storage-source key for multi-backend routing.
|
|
979
|
+
* When set, it is forwarded to the server so the correct
|
|
980
|
+
* `StorageController` is resolved from the registry.
|
|
981
|
+
*/
|
|
982
|
+
function createStorage(transport, storageId) {
|
|
1020
983
|
const urlsCache = /* @__PURE__ */ new Map();
|
|
984
|
+
/** Append ?storageId=... to a path when multi-backend routing is active. */
|
|
985
|
+
const withStorageId = (path) => {
|
|
986
|
+
if (!storageId) return path;
|
|
987
|
+
return `${path}${path.includes("?") ? "&" : "?"}storageId=${encodeURIComponent(storageId)}`;
|
|
988
|
+
};
|
|
1021
989
|
async function putObject({ file, key, metadata, bucket }) {
|
|
1022
990
|
const formData = new FormData();
|
|
1023
991
|
formData.append("file", file);
|
|
1024
992
|
if (key) formData.append("key", key);
|
|
1025
993
|
if (bucket) formData.append("bucket", bucket);
|
|
994
|
+
if (storageId) formData.append("storageId", storageId);
|
|
1026
995
|
if (metadata) {
|
|
1027
996
|
for (const [key, value] of Object.entries(metadata)) if (value !== void 0 && value !== null) formData.append(`metadata_${key}`, typeof value === "string" ? value : JSON.stringify(value));
|
|
1028
997
|
}
|
|
1029
|
-
return (await transport.request("/storage/upload", {
|
|
998
|
+
return (await transport.request(withStorageId("/storage/upload"), {
|
|
1030
999
|
method: "POST",
|
|
1031
1000
|
body: formData,
|
|
1032
1001
|
headers: {}
|
|
@@ -1037,18 +1006,18 @@ function createStorage(transport) {
|
|
|
1037
1006
|
const cached = urlsCache.get(cacheKey);
|
|
1038
1007
|
if (cached) return cached;
|
|
1039
1008
|
let filePath = keyOrUrl;
|
|
1040
|
-
if (filePath && (filePath.startsWith("local://") || filePath.startsWith("s3://"))) filePath = filePath.substring(filePath.indexOf("://") + 3);
|
|
1009
|
+
if (filePath && (filePath.startsWith("local://") || filePath.startsWith("s3://") || filePath.startsWith("gs://"))) filePath = filePath.substring(filePath.indexOf("://") + 3);
|
|
1041
1010
|
if (bucket && filePath && !filePath.startsWith(bucket)) filePath = `${bucket}/${filePath}`;
|
|
1042
1011
|
if (!filePath || filePath.trim() === "" || filePath === "/") return {
|
|
1043
1012
|
url: null,
|
|
1044
1013
|
fileNotFound: true
|
|
1045
1014
|
};
|
|
1046
1015
|
try {
|
|
1047
|
-
const result = await transport.request(`/storage/metadata/${filePath}`);
|
|
1016
|
+
const result = await transport.request(withStorageId(`/storage/metadata/${filePath}`));
|
|
1048
1017
|
const activeToken = await transport.resolveToken();
|
|
1049
1018
|
const tokenQuery = activeToken ? `?token=${activeToken}` : "";
|
|
1050
1019
|
const downloadConfig = {
|
|
1051
|
-
url: `${transport.baseUrl}${transport.apiPath}/storage/file/${filePath}${tokenQuery}
|
|
1020
|
+
url: withStorageId(`${transport.baseUrl}${transport.apiPath}/storage/file/${filePath}${tokenQuery}`),
|
|
1052
1021
|
metadata: result.data
|
|
1053
1022
|
};
|
|
1054
1023
|
urlsCache.set(cacheKey, downloadConfig);
|
|
@@ -1063,10 +1032,10 @@ function createStorage(transport) {
|
|
|
1063
1032
|
}
|
|
1064
1033
|
async function getObject(key, bucket) {
|
|
1065
1034
|
let filePath = key;
|
|
1066
|
-
if (filePath && (filePath.startsWith("local://") || filePath.startsWith("s3://"))) filePath = filePath.substring(filePath.indexOf("://") + 3);
|
|
1035
|
+
if (filePath && (filePath.startsWith("local://") || filePath.startsWith("s3://") || filePath.startsWith("gs://"))) filePath = filePath.substring(filePath.indexOf("://") + 3);
|
|
1067
1036
|
if (bucket && filePath && !filePath.startsWith(bucket)) filePath = `${bucket}/${filePath}`;
|
|
1068
1037
|
if (!filePath || filePath.trim() === "" || filePath === "/") return null;
|
|
1069
|
-
const url = `${transport.baseUrl}${transport.apiPath}/storage/file/${filePath}
|
|
1038
|
+
const url = withStorageId(`${transport.baseUrl}${transport.apiPath}/storage/file/${filePath}`);
|
|
1070
1039
|
const response = await transport.fetchFn(url, { headers: transport.getHeaders ? transport.getHeaders() : {} });
|
|
1071
1040
|
if (response.status === 404) return null;
|
|
1072
1041
|
if (!response.ok) throw new Error("Failed to get file");
|
|
@@ -1076,11 +1045,11 @@ function createStorage(transport) {
|
|
|
1076
1045
|
}
|
|
1077
1046
|
async function deleteObject(key, bucket) {
|
|
1078
1047
|
let filePath = key;
|
|
1079
|
-
if (filePath && (filePath.startsWith("local://") || filePath.startsWith("s3://"))) filePath = filePath.substring(filePath.indexOf("://") + 3);
|
|
1048
|
+
if (filePath && (filePath.startsWith("local://") || filePath.startsWith("s3://") || filePath.startsWith("gs://"))) filePath = filePath.substring(filePath.indexOf("://") + 3);
|
|
1080
1049
|
if (bucket && filePath && !filePath.startsWith(bucket)) filePath = `${bucket}/${filePath}`;
|
|
1081
1050
|
if (!filePath || filePath.trim() === "" || filePath === "/") return;
|
|
1082
1051
|
try {
|
|
1083
|
-
await transport.request(`/storage/file/${filePath}
|
|
1052
|
+
await transport.request(withStorageId(`/storage/file/${filePath}`), { method: "DELETE" });
|
|
1084
1053
|
} catch (e) {
|
|
1085
1054
|
if (!(e instanceof Error && "status" in e && e.status === 404)) throw e;
|
|
1086
1055
|
}
|
|
@@ -1092,6 +1061,7 @@ function createStorage(transport) {
|
|
|
1092
1061
|
if (options?.bucket) params.set("bucket", options.bucket);
|
|
1093
1062
|
if (options?.maxResults) params.set("maxResults", String(options.maxResults));
|
|
1094
1063
|
if (options?.pageToken) params.set("pageToken", options.pageToken);
|
|
1064
|
+
if (storageId) params.set("storageId", storageId);
|
|
1095
1065
|
return (await transport.request(`/storage/list?${params.toString()}`)).data;
|
|
1096
1066
|
}
|
|
1097
1067
|
return {
|
|
@@ -1103,6 +1073,62 @@ function createStorage(transport) {
|
|
|
1103
1073
|
};
|
|
1104
1074
|
}
|
|
1105
1075
|
//#endregion
|
|
1076
|
+
//#region src/storage-registry.ts
|
|
1077
|
+
/**
|
|
1078
|
+
* Default implementation of the client-side `StorageSourceRegistry`.
|
|
1079
|
+
*/
|
|
1080
|
+
var ClientStorageSourceRegistry = class ClientStorageSourceRegistry {
|
|
1081
|
+
sources = /* @__PURE__ */ new Map();
|
|
1082
|
+
/**
|
|
1083
|
+
* Register a storage source.
|
|
1084
|
+
* @param key - Unique key matching a `StorageSourceDefinition.key`
|
|
1085
|
+
* @param source - The `StorageSource` instance
|
|
1086
|
+
*/
|
|
1087
|
+
register(key, source) {
|
|
1088
|
+
this.sources.set(key, source);
|
|
1089
|
+
}
|
|
1090
|
+
getDefault() {
|
|
1091
|
+
const source = this.sources.get(DEFAULT_STORAGE_SOURCE_KEY);
|
|
1092
|
+
if (!source) throw new Error(`[StorageSourceRegistry] No default storage source registered. Register one with key "${DEFAULT_STORAGE_SOURCE_KEY}".`);
|
|
1093
|
+
return source;
|
|
1094
|
+
}
|
|
1095
|
+
get(key) {
|
|
1096
|
+
if (key === void 0 || key === null) return this.sources.get(DEFAULT_STORAGE_SOURCE_KEY);
|
|
1097
|
+
return this.sources.get(key);
|
|
1098
|
+
}
|
|
1099
|
+
getOrDefault(key) {
|
|
1100
|
+
if (key === void 0 || key === null) return this.getDefault();
|
|
1101
|
+
const source = this.sources.get(key);
|
|
1102
|
+
if (source) return source;
|
|
1103
|
+
console.warn(`[StorageSourceRegistry] Storage source "${key}" not found, falling back to "${DEFAULT_STORAGE_SOURCE_KEY}".`);
|
|
1104
|
+
return this.getDefault();
|
|
1105
|
+
}
|
|
1106
|
+
has(key) {
|
|
1107
|
+
return this.sources.has(key);
|
|
1108
|
+
}
|
|
1109
|
+
list() {
|
|
1110
|
+
return Array.from(this.sources.keys());
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Build a registry from `StorageSourceDefinition[]` and an HTTP transport.
|
|
1114
|
+
*
|
|
1115
|
+
* - Sources with `transport: "server"` are auto-wired via `createStorage(transport, key)`.
|
|
1116
|
+
* - Sources with `transport: "direct"` are **not** auto-wired — they must
|
|
1117
|
+
* be registered manually after this call (e.g. via a Firebase hook).
|
|
1118
|
+
*
|
|
1119
|
+
* @param definitions - Array of storage source definitions
|
|
1120
|
+
* @param transport - HTTP transport for server-backed sources
|
|
1121
|
+
*/
|
|
1122
|
+
static fromDefinitions(definitions, transport) {
|
|
1123
|
+
const registry = new ClientStorageSourceRegistry();
|
|
1124
|
+
for (const def of definitions) if (def.transport === "server") {
|
|
1125
|
+
const source = createStorage(transport, def.key === DEFAULT_STORAGE_SOURCE_KEY ? void 0 : def.key);
|
|
1126
|
+
registry.register(def.key, source);
|
|
1127
|
+
}
|
|
1128
|
+
return registry;
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
//#endregion
|
|
1106
1132
|
//#region src/websocket.ts
|
|
1107
1133
|
/**
|
|
1108
1134
|
* Extract error message and code from a WebSocket message payload.
|
|
@@ -2037,8 +2063,26 @@ function createRebaseClient(options) {
|
|
|
2037
2063
|
const auth = createAuth(transport, options.auth);
|
|
2038
2064
|
const admin = createAdmin(transport, options.admin);
|
|
2039
2065
|
const cron = createCron(transport, options.cron);
|
|
2066
|
+
const apiKeys = createApiKeys(transport, options.apiKeys);
|
|
2040
2067
|
const storage = createStorage(transport);
|
|
2041
2068
|
const functions = createFunctionsClient(transport);
|
|
2069
|
+
const createStorageSource = (storageId) => storageId === DEFAULT_STORAGE_SOURCE_KEY ? storage : createStorage(transport, storageId);
|
|
2070
|
+
const storageRegistry = new ClientStorageSourceRegistry();
|
|
2071
|
+
storageRegistry.register(DEFAULT_STORAGE_SOURCE_KEY, storage);
|
|
2072
|
+
for (const def of options.storageSources ?? []) if (def.transport === "server" && def.key !== DEFAULT_STORAGE_SOURCE_KEY) storageRegistry.register(def.key, createStorageSource(def.key));
|
|
2073
|
+
let storageSourcesPromise;
|
|
2074
|
+
const fetchStorageSources = () => {
|
|
2075
|
+
if (storageSourcesPromise) return storageSourcesPromise;
|
|
2076
|
+
storageSourcesPromise = transport.request("/storage/sources").then((res) => {
|
|
2077
|
+
const defs = res.data ?? [];
|
|
2078
|
+
for (const def of defs) if (def.transport === "server" && def.key !== DEFAULT_STORAGE_SOURCE_KEY && !storageRegistry.has(def.key)) storageRegistry.register(def.key, createStorageSource(def.key));
|
|
2079
|
+
return defs;
|
|
2080
|
+
}).catch((e) => {
|
|
2081
|
+
storageSourcesPromise = void 0;
|
|
2082
|
+
throw e;
|
|
2083
|
+
});
|
|
2084
|
+
return storageSourcesPromise;
|
|
2085
|
+
};
|
|
2042
2086
|
const resolvedWsUrl = options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl);
|
|
2043
2087
|
let ws;
|
|
2044
2088
|
if (resolvedWsUrl) {
|
|
@@ -2084,14 +2128,21 @@ function createRebaseClient(options) {
|
|
|
2084
2128
|
const dataProxy = new Proxy({ collection }, { get(_target, prop) {
|
|
2085
2129
|
if (prop === "collection") return collection;
|
|
2086
2130
|
if (typeof prop === "symbol") return void 0;
|
|
2087
|
-
if (typeof prop === "string" && prop !== "then" && prop !== "toJSON" && prop !== "$$typeof")
|
|
2131
|
+
if (typeof prop === "string" && prop !== "then" && prop !== "toJSON" && prop !== "$$typeof") {
|
|
2132
|
+
if (options.collections && prop in options.collections) return collection(options.collections[prop]);
|
|
2133
|
+
return collection(toSnakeCase(prop));
|
|
2134
|
+
}
|
|
2088
2135
|
} });
|
|
2089
2136
|
return {
|
|
2090
2137
|
auth,
|
|
2091
2138
|
admin,
|
|
2092
2139
|
cron,
|
|
2140
|
+
apiKeys,
|
|
2093
2141
|
functions,
|
|
2094
2142
|
storage,
|
|
2143
|
+
storageRegistry,
|
|
2144
|
+
createStorageSource,
|
|
2145
|
+
fetchStorageSources,
|
|
2095
2146
|
ws,
|
|
2096
2147
|
setToken: transport.setToken,
|
|
2097
2148
|
setAuthTokenGetter: transport.setAuthTokenGetter,
|
|
@@ -2112,6 +2163,6 @@ function createRebaseClient(options) {
|
|
|
2112
2163
|
};
|
|
2113
2164
|
}
|
|
2114
2165
|
//#endregion
|
|
2115
|
-
export { ApiError, QueryBuilder, RebaseApiError, RebaseWebSocketClient, and, buildQueryString, cond, createAdmin, createAuth, createCollectionClient, createCookieStorage, createCron, createFunctionsClient, createMemoryStorage, createRebaseClient, createStorage, createTransport, or, rebaseReviver };
|
|
2166
|
+
export { ApiError, ClientStorageSourceRegistry, QueryBuilder, RebaseApiError, RebaseWebSocketClient, and, buildQueryString, cond, createAdmin, createApiKeys, createAuth, createCollectionClient, createCookieStorage, createCron, createFunctionsClient, createMemoryStorage, createRebaseClient, createStorage, createTransport, or, rebaseReviver };
|
|
2116
2167
|
|
|
2117
2168
|
//# sourceMappingURL=index.es.js.map
|