@basictech/react 0.11.0 → 0.12.0-beta.1
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/CHANGELOG.md +585 -0
- package/README.md +32 -25
- package/dist/index.d.mts +14 -8
- package/dist/index.d.ts +14 -8
- package/dist/index.js +95 -110
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +92 -109
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -59,10 +59,21 @@ __export(index_exports, {
|
|
|
59
59
|
});
|
|
60
60
|
module.exports = __toCommonJS(index_exports);
|
|
61
61
|
|
|
62
|
+
// src/create-basic.tsx
|
|
63
|
+
var import_react12 = require("react");
|
|
64
|
+
|
|
65
|
+
// src/context.ts
|
|
66
|
+
var import_react = require("react");
|
|
67
|
+
var BasicClientContext = (0, import_react.createContext)(null);
|
|
68
|
+
|
|
62
69
|
// src/create-client.ts
|
|
63
70
|
var import_core4 = require("@basictech/core");
|
|
64
71
|
|
|
65
72
|
// src/adapters/broadcast-channel.ts
|
|
73
|
+
function supportsAccessTokenAdoption(store) {
|
|
74
|
+
const candidate = store;
|
|
75
|
+
return typeof candidate?.accessToken === "function" && typeof candidate?.adoptAccessToken === "function";
|
|
76
|
+
}
|
|
66
77
|
function createBrowserMessageChannel(name) {
|
|
67
78
|
const channel = new BroadcastChannel(name);
|
|
68
79
|
const adapter = {
|
|
@@ -73,12 +84,11 @@ function createBrowserMessageChannel(name) {
|
|
|
73
84
|
channel.onmessage = (event) => adapter.onmessage?.({ data: event.data });
|
|
74
85
|
return adapter;
|
|
75
86
|
}
|
|
76
|
-
function createBrowserAuthChannelFactory(
|
|
77
|
-
|
|
78
|
-
return (name) => {
|
|
87
|
+
function createBrowserAuthChannelFactory(tokenStore) {
|
|
88
|
+
return (name, context) => {
|
|
79
89
|
const channel = createBrowserMessageChannel(name);
|
|
80
|
-
|
|
81
|
-
const tokenKey =
|
|
90
|
+
if (!context) return channel;
|
|
91
|
+
const { tokenKey } = context;
|
|
82
92
|
const adapter = {
|
|
83
93
|
postMessage: (message) => {
|
|
84
94
|
const outgoing = message;
|
|
@@ -620,32 +630,27 @@ function browserReconcileTrigger(listener) {
|
|
|
620
630
|
function createBasicClient(config) {
|
|
621
631
|
const local = browserStorage("localStorage") ?? new import_core4.MemoryKeyValueStorage();
|
|
622
632
|
const session = browserStorage("sessionStorage") ?? new import_core4.MemoryKeyValueStorage();
|
|
623
|
-
const
|
|
633
|
+
const tokenStore = config.tokenStore ?? new BrowserTokenStore(local);
|
|
624
634
|
const canBroadcast = typeof BroadcastChannel !== "undefined";
|
|
625
635
|
const canPersistReplica = typeof indexedDB !== "undefined";
|
|
636
|
+
const messageChannel = !canBroadcast ? void 0 : supportsAccessTokenAdoption(tokenStore) ? createBrowserAuthChannelFactory(tokenStore) : createBrowserMessageChannel;
|
|
626
637
|
return (0, import_core4.createBasicClient)({
|
|
627
638
|
...config,
|
|
628
639
|
kv: config.kv ?? local,
|
|
629
|
-
tokenStore
|
|
640
|
+
tokenStore,
|
|
630
641
|
sessionStorage: config.sessionStorage ?? session,
|
|
631
642
|
replicaStore: config.replicaStore ?? (canPersistReplica ? new PersistenceStore(config.clientId) : new import_core4.MemoryReplicaStoreFactory()),
|
|
632
643
|
navigate: config.navigate ?? browserNavigate,
|
|
633
644
|
currentUrl: config.currentUrl ?? browserCurrentUrl,
|
|
634
645
|
replaceUrl: config.replaceUrl ?? browserReplaceUrl,
|
|
635
646
|
reconcileTrigger: config.reconcileTrigger ?? browserReconcileTrigger,
|
|
636
|
-
createMessageChannel: config.createMessageChannel ??
|
|
647
|
+
createMessageChannel: config.createMessageChannel ?? messageChannel,
|
|
637
648
|
uploadTransport: config.uploadTransport ?? (typeof XMLHttpRequest === "undefined" ? void 0 : browserUploadTransport)
|
|
638
649
|
});
|
|
639
650
|
}
|
|
640
651
|
|
|
641
652
|
// src/provider.tsx
|
|
642
653
|
var import_react2 = require("react");
|
|
643
|
-
|
|
644
|
-
// src/context.ts
|
|
645
|
-
var import_react = require("react");
|
|
646
|
-
var BasicClientContext = (0, import_react.createContext)(null);
|
|
647
|
-
|
|
648
|
-
// src/provider.tsx
|
|
649
654
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
650
655
|
var clientLifecycles = /* @__PURE__ */ new WeakMap();
|
|
651
656
|
function retainClient(client) {
|
|
@@ -727,6 +732,10 @@ function useRequiredClient() {
|
|
|
727
732
|
function useClientSnapshot(client) {
|
|
728
733
|
return (0, import_react3.useSyncExternalStore)(client.subscribe, client.getSnapshot, client.getSnapshot);
|
|
729
734
|
}
|
|
735
|
+
function useSignInGate(snapshot) {
|
|
736
|
+
const code = snapshot.isReady && !snapshot.isSignedIn ? snapshot.readOnlyReason ?? "AUTHORIZATION_REQUIRED" : null;
|
|
737
|
+
return (0, import_react3.useMemo)(() => code === null ? null : new import_core5.BasicError(code, code === "AUTH_EXPIRED" ? "Session expired. Sign in again to load this list." : "Sign in to load this list."), [code]);
|
|
738
|
+
}
|
|
730
739
|
function toBasicError(error, fallback = "UNKNOWN_ERROR") {
|
|
731
740
|
if (error instanceof import_core5.BasicError) return error;
|
|
732
741
|
return new import_core5.BasicError(fallback, error instanceof Error ? error.message : String(error));
|
|
@@ -779,11 +788,11 @@ function useAccountsFor(client) {
|
|
|
779
788
|
const removeAccount = (0, import_react4.useCallback)((id) => client.removeAccount(id), [client]);
|
|
780
789
|
return (0, import_react4.useMemo)(() => ({
|
|
781
790
|
accounts: snapshot.accounts,
|
|
782
|
-
|
|
791
|
+
activeAccount: snapshot.activeAccount,
|
|
783
792
|
switchAccount,
|
|
784
793
|
addAccount,
|
|
785
794
|
removeAccount
|
|
786
|
-
}), [snapshot.accounts, snapshot.
|
|
795
|
+
}), [snapshot.accounts, snapshot.activeAccount, switchAccount, addAccount, removeAccount]);
|
|
787
796
|
}
|
|
788
797
|
function useAccounts() {
|
|
789
798
|
return useAccountsFor(useRequiredClient());
|
|
@@ -791,7 +800,6 @@ function useAccounts() {
|
|
|
791
800
|
|
|
792
801
|
// src/hooks/auth.ts
|
|
793
802
|
var import_react5 = require("react");
|
|
794
|
-
var import_core6 = require("@basictech/core");
|
|
795
803
|
function useAuthFor(client) {
|
|
796
804
|
const snapshot = useClientSnapshot(client);
|
|
797
805
|
const signIn = (0, import_react5.useCallback)((input) => client.signIn(input), [client]);
|
|
@@ -804,7 +812,7 @@ function useAuthFor(client) {
|
|
|
804
812
|
canWrite: snapshot.canWrite,
|
|
805
813
|
readOnlyReason: snapshot.readOnlyReason,
|
|
806
814
|
status: snapshot.authStatus,
|
|
807
|
-
error: snapshot.
|
|
815
|
+
error: snapshot.authError,
|
|
808
816
|
user: snapshot.user,
|
|
809
817
|
did: snapshot.did,
|
|
810
818
|
handle: snapshot.handle,
|
|
@@ -817,6 +825,9 @@ function useAuth() {
|
|
|
817
825
|
return useAuthFor(useRequiredClient());
|
|
818
826
|
}
|
|
819
827
|
|
|
828
|
+
// src/hooks/basic.ts
|
|
829
|
+
var import_react8 = require("react");
|
|
830
|
+
|
|
820
831
|
// src/hooks/db.ts
|
|
821
832
|
function useDbFor(client, source = "default") {
|
|
822
833
|
return client.db(source);
|
|
@@ -893,51 +904,41 @@ function useBasicFor(client) {
|
|
|
893
904
|
const auth = useAuthFor(client);
|
|
894
905
|
const accounts = useAccountsFor(client);
|
|
895
906
|
const sync = useSyncStatusFor(client);
|
|
896
|
-
const repos = useReposFor(client);
|
|
897
|
-
|
|
907
|
+
const repos = useReposFor(client).repos;
|
|
908
|
+
const db = useDbFor(client);
|
|
909
|
+
return (0, import_react8.useMemo)(() => ({
|
|
898
910
|
...auth,
|
|
899
911
|
client,
|
|
900
|
-
db
|
|
912
|
+
db,
|
|
901
913
|
accounts,
|
|
902
914
|
sync,
|
|
903
|
-
repos
|
|
904
|
-
};
|
|
915
|
+
repos
|
|
916
|
+
}), [auth, client, db, accounts, sync, repos]);
|
|
905
917
|
}
|
|
906
918
|
function useBasic() {
|
|
907
919
|
return useBasicFor(useRequiredClient());
|
|
908
920
|
}
|
|
909
921
|
|
|
910
922
|
// src/hooks/files.ts
|
|
911
|
-
var
|
|
923
|
+
var import_react9 = require("react");
|
|
912
924
|
var zero = () => 0;
|
|
913
925
|
function useFilesFor(client, query = {}, options = {}) {
|
|
914
926
|
const source = options.source ?? "default";
|
|
915
927
|
const sourceKey = JSON.stringify(source);
|
|
916
928
|
const queryKey = JSON.stringify(query);
|
|
917
929
|
const snapshot = useClientSnapshot(client);
|
|
918
|
-
const subscription = (0,
|
|
930
|
+
const subscription = (0, import_react9.useMemo)(
|
|
919
931
|
() => client.queryStore.collection(source, "_files"),
|
|
920
932
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
921
933
|
[client, sourceKey]
|
|
922
934
|
);
|
|
923
|
-
const version = (0,
|
|
935
|
+
const version = (0, import_react9.useSyncExternalStore)(subscription.subscribe, subscription.getSnapshot, zero);
|
|
924
936
|
return useAsyncResult(
|
|
925
|
-
async () =>
|
|
926
|
-
const files = client.db(source).files;
|
|
927
|
-
if (source !== "default" && "mountId" in source) {
|
|
928
|
-
const page = await files.list({
|
|
929
|
-
...query.prefix !== void 0 ? { prefix: query.prefix } : {},
|
|
930
|
-
...query.limit !== void 0 ? { limit: query.limit } : {},
|
|
931
|
-
...query.cursor !== void 0 ? { cursor: query.cursor } : {}
|
|
932
|
-
});
|
|
933
|
-
return page.data;
|
|
934
|
-
}
|
|
935
|
-
return (await files.list(query)).data;
|
|
936
|
-
},
|
|
937
|
+
async () => (await client.db(source).files.list(query)).data,
|
|
937
938
|
[],
|
|
938
939
|
snapshot.isReady,
|
|
939
940
|
snapshot.isReady,
|
|
940
|
-
[sourceKey, queryKey, version, snapshot.
|
|
941
|
+
[sourceKey, queryKey, version, snapshot.activeAccount?.id]
|
|
941
942
|
);
|
|
942
943
|
}
|
|
943
944
|
function useFiles(query = {}, options = {}) {
|
|
@@ -945,61 +946,62 @@ function useFiles(query = {}, options = {}) {
|
|
|
945
946
|
}
|
|
946
947
|
function useStorageInfoFor(client) {
|
|
947
948
|
const snapshot = useClientSnapshot(client);
|
|
948
|
-
|
|
949
|
+
const gate = useSignInGate(snapshot);
|
|
950
|
+
const result = useAsyncResult(
|
|
949
951
|
() => client.storageInfo(client.resolveRepoId()),
|
|
950
952
|
null,
|
|
951
953
|
snapshot.isReady && snapshot.isSignedIn,
|
|
952
954
|
snapshot.isReady,
|
|
953
|
-
[snapshot.
|
|
955
|
+
[snapshot.activeAccount?.id]
|
|
954
956
|
);
|
|
957
|
+
return { ...result, error: result.error ?? gate };
|
|
955
958
|
}
|
|
956
959
|
function useStorageInfo() {
|
|
957
960
|
return useStorageInfoFor(useRequiredClient());
|
|
958
961
|
}
|
|
959
962
|
|
|
960
963
|
// src/hooks/mounts.ts
|
|
961
|
-
var
|
|
964
|
+
var import_react10 = require("react");
|
|
962
965
|
function useMountsFor(client, query = {}) {
|
|
963
966
|
const snapshot = useClientSnapshot(client);
|
|
967
|
+
const gate = useSignInGate(snapshot);
|
|
964
968
|
const queryKey = JSON.stringify(query);
|
|
965
969
|
const result = useAsyncResult(
|
|
966
970
|
() => client.shares.mounts(query),
|
|
967
971
|
[],
|
|
968
972
|
snapshot.isReady && snapshot.isSignedIn,
|
|
969
973
|
snapshot.isReady,
|
|
970
|
-
[queryKey, snapshot.
|
|
974
|
+
[queryKey, snapshot.activeAccount?.id]
|
|
971
975
|
);
|
|
972
|
-
const open = (0,
|
|
976
|
+
const open = (0, import_react10.useCallback)(
|
|
973
977
|
(mountId) => client.shares.mount(mountId),
|
|
974
978
|
[client]
|
|
975
979
|
);
|
|
976
|
-
const manageUrl = (0,
|
|
977
|
-
|
|
978
|
-
return { data: mounts, mounts, ...state, open, manageUrl };
|
|
980
|
+
const manageUrl = (0, import_react10.useCallback)(() => client.shares.manageUrl(), [client]);
|
|
981
|
+
return { ...result, error: result.error ?? gate, open, manageUrl };
|
|
979
982
|
}
|
|
980
983
|
function useMounts(query = {}) {
|
|
981
984
|
return useMountsFor(useRequiredClient(), query);
|
|
982
985
|
}
|
|
983
986
|
function useOutgoingSharesFor(client) {
|
|
984
987
|
const snapshot = useClientSnapshot(client);
|
|
988
|
+
const gate = useSignInGate(snapshot);
|
|
985
989
|
const result = useAsyncResult(
|
|
986
990
|
() => client.shares.listOutgoing(),
|
|
987
991
|
[],
|
|
988
992
|
snapshot.isReady && snapshot.isSignedIn,
|
|
989
993
|
snapshot.isReady,
|
|
990
|
-
[snapshot.
|
|
994
|
+
[snapshot.activeAccount?.id]
|
|
991
995
|
);
|
|
992
|
-
const create = (0,
|
|
993
|
-
const get = (0,
|
|
994
|
-
const cancel = (0,
|
|
995
|
-
const revoke = (0,
|
|
996
|
-
const getContactHandle = (0,
|
|
997
|
-
const manageUrl = (0,
|
|
998
|
-
const { data: outgoingShares, ...state } = result;
|
|
996
|
+
const create = (0, import_react10.useCallback)((input) => client.shares.createOutgoing(input), [client]);
|
|
997
|
+
const get = (0, import_react10.useCallback)((id) => client.shares.getOutgoing(id), [client]);
|
|
998
|
+
const cancel = (0, import_react10.useCallback)((id) => client.shares.cancel(id), [client]);
|
|
999
|
+
const revoke = (0, import_react10.useCallback)((id) => client.shares.revoke(id), [client]);
|
|
1000
|
+
const getContactHandle = (0, import_react10.useCallback)((did) => client.shares.getContactHandle(did), [client]);
|
|
1001
|
+
const manageUrl = (0, import_react10.useCallback)(() => client.shares.manageUrl(), [client]);
|
|
999
1002
|
return {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
...state,
|
|
1003
|
+
...result,
|
|
1004
|
+
error: result.error ?? gate,
|
|
1003
1005
|
create,
|
|
1004
1006
|
get,
|
|
1005
1007
|
cancel,
|
|
@@ -1013,58 +1015,35 @@ function useOutgoingShares() {
|
|
|
1013
1015
|
}
|
|
1014
1016
|
|
|
1015
1017
|
// src/hooks/query.ts
|
|
1016
|
-
var
|
|
1017
|
-
var subscribeNever = () => () => {
|
|
1018
|
-
};
|
|
1019
|
-
var zero2 = () => 0;
|
|
1018
|
+
var import_react11 = require("react");
|
|
1020
1019
|
function useQueryValueFor(client, collection, query = {}, options = {}) {
|
|
1021
1020
|
const source = options.source ?? "default";
|
|
1022
1021
|
const sourceKey = JSON.stringify(source);
|
|
1023
1022
|
const queryKey = JSON.stringify(query);
|
|
1024
1023
|
const snapshot = useClientSnapshot(client);
|
|
1025
|
-
const
|
|
1026
|
-
() => client.queryStore.collection(source, collection),
|
|
1027
|
-
// sourceKey captures semantic source identity rather than object identity.
|
|
1028
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1029
|
-
[client, sourceKey, collection]
|
|
1030
|
-
);
|
|
1031
|
-
const version = (0, import_react10.useSyncExternalStore)(
|
|
1032
|
-
subscription?.subscribe ?? subscribeNever,
|
|
1033
|
-
subscription?.getSnapshot ?? zero2,
|
|
1034
|
-
zero2
|
|
1035
|
-
);
|
|
1036
|
-
const [result, setResult] = (0, import_react10.useState)({
|
|
1024
|
+
const [result, setResult] = (0, import_react11.useState)({
|
|
1037
1025
|
data: [],
|
|
1038
1026
|
isLoading: true,
|
|
1039
1027
|
error: null
|
|
1040
1028
|
});
|
|
1041
|
-
(0,
|
|
1042
|
-
let active = true;
|
|
1043
|
-
if (!snapshot.isReady) {
|
|
1044
|
-
setResult((current) => ({ ...current, isLoading: true, error: null }));
|
|
1045
|
-
return () => {
|
|
1046
|
-
active = false;
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1029
|
+
(0, import_react11.useEffect)(() => {
|
|
1049
1030
|
setResult((current) => ({ ...current, isLoading: true, error: null }));
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
(error) => {
|
|
1057
|
-
if (active) setResult((current) => ({
|
|
1031
|
+
if (!snapshot.isReady) return;
|
|
1032
|
+
try {
|
|
1033
|
+
return client.db(source).collection(collection).watch(
|
|
1034
|
+
query,
|
|
1035
|
+
(page) => setResult({ data: page.data, isLoading: false, error: null }),
|
|
1036
|
+
(error) => setResult((current) => ({
|
|
1058
1037
|
...current,
|
|
1059
1038
|
isLoading: false,
|
|
1060
1039
|
error: toBasicError(error, "QUERY_FAILED")
|
|
1061
|
-
}))
|
|
1062
|
-
|
|
1063
|
-
)
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
}
|
|
1067
|
-
}, [client, collection, sourceKey, queryKey,
|
|
1040
|
+
}))
|
|
1041
|
+
);
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
setResult((current) => ({ ...current, isLoading: false, error: toBasicError(error, "QUERY_FAILED") }));
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
}, [client, collection, sourceKey, queryKey, snapshot.isReady]);
|
|
1068
1047
|
return result;
|
|
1069
1048
|
}
|
|
1070
1049
|
function useQueryFor(client, collection, query = {}, options = {}) {
|
|
@@ -1081,25 +1060,31 @@ function createBasic(config) {
|
|
|
1081
1060
|
function Provider({ children, renderWhileLoading }) {
|
|
1082
1061
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BasicProvider, { client, renderWhileLoading, children });
|
|
1083
1062
|
}
|
|
1063
|
+
function useBound() {
|
|
1064
|
+
if ((0, import_react12.useContext)(BasicClientContext) !== client) {
|
|
1065
|
+
throw new Error("Bound Basic hooks must be used within their own <basic.Provider>");
|
|
1066
|
+
}
|
|
1067
|
+
return client;
|
|
1068
|
+
}
|
|
1084
1069
|
function useBoundDb(source = "default") {
|
|
1085
|
-
return
|
|
1070
|
+
return useBound().db(source);
|
|
1086
1071
|
}
|
|
1087
1072
|
return {
|
|
1088
1073
|
client,
|
|
1089
1074
|
Provider,
|
|
1090
|
-
useBasic: () => useBasicFor(
|
|
1091
|
-
useAuth: () => useAuthFor(
|
|
1092
|
-
useAccounts: () => useAccountsFor(
|
|
1075
|
+
useBasic: () => useBasicFor(useBound()),
|
|
1076
|
+
useAuth: () => useAuthFor(useBound()),
|
|
1077
|
+
useAccounts: () => useAccountsFor(useBound()),
|
|
1093
1078
|
useDb: useBoundDb,
|
|
1094
|
-
useCollection: (name, options = {}) => useCollectionFor(
|
|
1095
|
-
useQuery: (collection, query = {}, options = {}) => useQueryFor(
|
|
1096
|
-
useSyncStatus: (source) => useSyncStatusFor(
|
|
1097
|
-
useSchemaStatus: (source = "default") => useSchemaStatusFor(
|
|
1098
|
-
useRepos: () => useReposFor(
|
|
1099
|
-
useFiles: (query = {}, options = {}) => useFilesFor(
|
|
1100
|
-
useStorageInfo: () => useStorageInfoFor(
|
|
1101
|
-
useMounts: (query = {}) => useMountsFor(
|
|
1102
|
-
useOutgoingShares: () => useOutgoingSharesFor(
|
|
1079
|
+
useCollection: (name, options = {}) => useCollectionFor(useBound(), name, options),
|
|
1080
|
+
useQuery: (collection, query = {}, options = {}) => useQueryFor(useBound(), collection, query, options),
|
|
1081
|
+
useSyncStatus: (source) => useSyncStatusFor(useBound(), source),
|
|
1082
|
+
useSchemaStatus: (source = "default") => useSchemaStatusFor(useBound(), source),
|
|
1083
|
+
useRepos: () => useReposFor(useBound()),
|
|
1084
|
+
useFiles: (query = {}, options = {}) => useFilesFor(useBound(), query, options),
|
|
1085
|
+
useStorageInfo: () => useStorageInfoFor(useBound()),
|
|
1086
|
+
useMounts: (query = {}) => useMountsFor(useBound(), query),
|
|
1087
|
+
useOutgoingShares: () => useOutgoingSharesFor(useBound())
|
|
1103
1088
|
};
|
|
1104
1089
|
}
|
|
1105
1090
|
// Annotate the CommonJS export names for ESM import in node:
|