@calimero-network/mero-react 8.0.0 → 9.1.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/README.md +37 -0
- package/dist/index.cjs +89 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -8
- package/dist/index.d.ts +108 -8
- package/dist/index.js +87 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -208,6 +208,41 @@ const { contexts, loading, error, refetch } = useContexts(applicationId);
|
|
|
208
208
|
// contexts: Array<{ contextId: string; applicationId: string }>
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
+
### Blob hooks
|
|
212
|
+
|
|
213
|
+
`useBlobInfo` / `useBlobUrl` / `useUploadBlob` read and write the node's blob
|
|
214
|
+
store. The reads take an optional `contextId`, and it is what picks the mode:
|
|
215
|
+
|
|
216
|
+
- **without `contextId`** — local-only. The node answers from its own blob
|
|
217
|
+
store immediately, or 404s.
|
|
218
|
+
- **with `contextId`** — network discovery. The node probes that context's
|
|
219
|
+
peers (availability nodes first) for a holder.
|
|
220
|
+
|
|
221
|
+
Discovery is slow: core bounds the search by a **~30s deadline**, and for
|
|
222
|
+
`useBlobUrl` the byte transfer is on top of that. `loading` is real UI state on
|
|
223
|
+
these hooks, not decoration.
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
// Presence and size, no download — ask this before pulling something large.
|
|
227
|
+
const { info, notFound, loading, error } = useBlobInfo(blobId, { contextId });
|
|
228
|
+
// info: { blobId, size, hash?, mimeType?, source?: 'local' | 'peer' } | null
|
|
229
|
+
|
|
230
|
+
// Bytes as an object URL, revoked for you on unmount and on id/context change.
|
|
231
|
+
const { url } = useBlobUrl(blobId, { contextId, type: 'image/png' });
|
|
232
|
+
return url ? <img src={url} /> : null;
|
|
233
|
+
|
|
234
|
+
// Upload. Pass contextId to announce the blob so the context's peers can
|
|
235
|
+
// discover it later.
|
|
236
|
+
const { uploadBlob, loading, error } = useUploadBlob();
|
|
237
|
+
const result = await uploadBlob({ data: bytes, contextId });
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
A null/undefined `blobId` fetches nothing, like the other `string | null` read
|
|
241
|
+
hooks. `hash` and `mimeType` are genuinely optional — a peer probe carries only
|
|
242
|
+
presence and size, so a discovery-sourced hit has neither; read `info.source`
|
|
243
|
+
to tell a local answer from a peer one. `notFound` marks the node's legitimate
|
|
244
|
+
"no holder" 404 apart from a transport failure; `error` is set for both.
|
|
245
|
+
|
|
211
246
|
### Storage helpers
|
|
212
247
|
|
|
213
248
|
Persist/read node URL, application ID, context ID, and context identity in localStorage.
|
|
@@ -360,11 +395,13 @@ useDetachContextFromGroup
|
|
|
360
395
|
useNamespaces, useNamespace, useNamespaceGroups, useNamespaceIdentity
|
|
361
396
|
useNamespacesForApplication, useCreateNamespace, useDeleteNamespace
|
|
362
397
|
useJoinNamespace, useCreateNamespaceInvitation, useCreateGroupInNamespace
|
|
398
|
+
useBlobInfo, useBlobUrl, useUploadBlob
|
|
363
399
|
|
|
364
400
|
// Types (mero-react)
|
|
365
401
|
MeroContextValue, MeroProviderConfig, MeroProviderProps
|
|
366
402
|
CustomConnectionConfig, AppContext, ExecutionResult
|
|
367
403
|
ApplicationContextRecord, ContextDiscoveryOptions, ContextDiscoveryState
|
|
404
|
+
BlobHookOptions, UseBlobUrlOptions
|
|
368
405
|
|
|
369
406
|
// Storage (mero-react)
|
|
370
407
|
localStorageTokenStorage
|
package/dist/index.cjs
CHANGED
|
@@ -1949,9 +1949,9 @@ function useDeleteGroup() {
|
|
|
1949
1949
|
const { mero } = useMero();
|
|
1950
1950
|
const { loading, error, run } = useAsyncMutation();
|
|
1951
1951
|
const deleteGroup = react.useCallback(
|
|
1952
|
-
async (groupId
|
|
1952
|
+
async (groupId) => {
|
|
1953
1953
|
if (!mero) return null;
|
|
1954
|
-
return run(() => mero.admin.deleteGroup(groupId
|
|
1954
|
+
return run(() => mero.admin.deleteGroup(groupId));
|
|
1955
1955
|
},
|
|
1956
1956
|
[mero, run]
|
|
1957
1957
|
);
|
|
@@ -1961,9 +1961,9 @@ function useSyncGroup() {
|
|
|
1961
1961
|
const { mero } = useMero();
|
|
1962
1962
|
const { loading, error, run } = useAsyncMutation();
|
|
1963
1963
|
const syncGroup = react.useCallback(
|
|
1964
|
-
async (groupId
|
|
1964
|
+
async (groupId) => {
|
|
1965
1965
|
if (!mero) return null;
|
|
1966
|
-
return run(() => mero.admin.syncGroup(groupId
|
|
1966
|
+
return run(() => mero.admin.syncGroup(groupId));
|
|
1967
1967
|
},
|
|
1968
1968
|
[mero, run]
|
|
1969
1969
|
);
|
|
@@ -2054,9 +2054,9 @@ function useDeleteNamespace() {
|
|
|
2054
2054
|
const { mero } = useMero();
|
|
2055
2055
|
const { loading, error, run } = useAsyncMutation();
|
|
2056
2056
|
const deleteNamespace = react.useCallback(
|
|
2057
|
-
async (namespaceId
|
|
2057
|
+
async (namespaceId) => {
|
|
2058
2058
|
if (!mero) return null;
|
|
2059
|
-
return run(() => mero.admin.deleteNamespace(namespaceId
|
|
2059
|
+
return run(() => mero.admin.deleteNamespace(namespaceId));
|
|
2060
2060
|
},
|
|
2061
2061
|
[mero, run]
|
|
2062
2062
|
);
|
|
@@ -2277,9 +2277,9 @@ function useInstallFromRegistry() {
|
|
|
2277
2277
|
const { mero } = useMero();
|
|
2278
2278
|
const { loading, error, run } = useAsyncMutation();
|
|
2279
2279
|
const installFromRegistry = react.useCallback(
|
|
2280
|
-
async (
|
|
2280
|
+
async (packageName, version) => {
|
|
2281
2281
|
if (!mero) return null;
|
|
2282
|
-
return run(() => mero.admin.
|
|
2282
|
+
return run(() => mero.admin.installApplication({ package: packageName, version }));
|
|
2283
2283
|
},
|
|
2284
2284
|
[mero, run]
|
|
2285
2285
|
);
|
|
@@ -2348,9 +2348,9 @@ function useRetryGroupUpgrade() {
|
|
|
2348
2348
|
const { mero } = useMero();
|
|
2349
2349
|
const { loading, error, run } = useAsyncMutation();
|
|
2350
2350
|
const retryGroupUpgrade = react.useCallback(
|
|
2351
|
-
async (groupId
|
|
2351
|
+
async (groupId) => {
|
|
2352
2352
|
if (!mero) return null;
|
|
2353
|
-
return run(() => mero.admin.retryGroupUpgrade(groupId
|
|
2353
|
+
return run(() => mero.admin.retryGroupUpgrade(groupId));
|
|
2354
2354
|
},
|
|
2355
2355
|
[mero, run]
|
|
2356
2356
|
);
|
|
@@ -2381,9 +2381,9 @@ function useDetachContextFromGroup() {
|
|
|
2381
2381
|
const { mero } = useMero();
|
|
2382
2382
|
const { loading, error, run } = useAsyncMutation();
|
|
2383
2383
|
const detachContextFromGroup = react.useCallback(
|
|
2384
|
-
async (groupId, contextId
|
|
2384
|
+
async (groupId, contextId) => {
|
|
2385
2385
|
if (!mero) return null;
|
|
2386
|
-
return run(() => mero.admin.detachContextFromGroup(groupId, contextId
|
|
2386
|
+
return run(() => mero.admin.detachContextFromGroup(groupId, contextId));
|
|
2387
2387
|
},
|
|
2388
2388
|
[mero, run]
|
|
2389
2389
|
);
|
|
@@ -2730,6 +2730,80 @@ function useMyAuthoredMigration(contextId) {
|
|
|
2730
2730
|
refresh
|
|
2731
2731
|
};
|
|
2732
2732
|
}
|
|
2733
|
+
function isBlobNotFound(error) {
|
|
2734
|
+
return !!error && error.status === 404;
|
|
2735
|
+
}
|
|
2736
|
+
function useBlobInfo(blobId, options) {
|
|
2737
|
+
const { mero } = useMero();
|
|
2738
|
+
const contextId = options?.contextId;
|
|
2739
|
+
const { data, loading, error, refetch } = useAsyncResource(
|
|
2740
|
+
mero && blobId ? () => mero.admin.getBlobInfo(blobId, contextId ? { contextId } : void 0) : null,
|
|
2741
|
+
null,
|
|
2742
|
+
[mero, blobId, contextId]
|
|
2743
|
+
);
|
|
2744
|
+
return { info: data, notFound: isBlobNotFound(error), loading, error, refetch };
|
|
2745
|
+
}
|
|
2746
|
+
function useBlobUrl(blobId, options) {
|
|
2747
|
+
const { mero } = useMero();
|
|
2748
|
+
const contextId = options?.contextId;
|
|
2749
|
+
const type = options?.type;
|
|
2750
|
+
const mountedRef = useMountedRef();
|
|
2751
|
+
const [url, setUrl] = react.useState(null);
|
|
2752
|
+
const [loading, setLoading] = react.useState(false);
|
|
2753
|
+
const [error, setError] = react.useState(null);
|
|
2754
|
+
const reqRef = react.useRef(0);
|
|
2755
|
+
const urlRef = react.useRef(null);
|
|
2756
|
+
const revoke = react.useCallback(() => {
|
|
2757
|
+
if (urlRef.current) {
|
|
2758
|
+
URL.revokeObjectURL(urlRef.current);
|
|
2759
|
+
urlRef.current = null;
|
|
2760
|
+
}
|
|
2761
|
+
}, []);
|
|
2762
|
+
const refetch = react.useCallback(async () => {
|
|
2763
|
+
const seq = ++reqRef.current;
|
|
2764
|
+
if (!mero || !blobId) return;
|
|
2765
|
+
if (mountedRef.current) {
|
|
2766
|
+
setLoading(true);
|
|
2767
|
+
setError(null);
|
|
2768
|
+
}
|
|
2769
|
+
try {
|
|
2770
|
+
const bytes = await mero.admin.getBlob(blobId, contextId ? { contextId } : void 0);
|
|
2771
|
+
if (!mountedRef.current || seq !== reqRef.current) return;
|
|
2772
|
+
revoke();
|
|
2773
|
+
const objectUrl = URL.createObjectURL(new Blob([bytes], type ? { type } : void 0));
|
|
2774
|
+
urlRef.current = objectUrl;
|
|
2775
|
+
setUrl(objectUrl);
|
|
2776
|
+
} catch (err) {
|
|
2777
|
+
if (mountedRef.current && seq === reqRef.current) setError(toError(err));
|
|
2778
|
+
} finally {
|
|
2779
|
+
if (mountedRef.current && seq === reqRef.current) setLoading(false);
|
|
2780
|
+
}
|
|
2781
|
+
}, [mero, blobId, contextId, type, revoke, mountedRef]);
|
|
2782
|
+
react.useEffect(() => {
|
|
2783
|
+
reqRef.current += 1;
|
|
2784
|
+
revoke();
|
|
2785
|
+
setUrl(null);
|
|
2786
|
+
setError(null);
|
|
2787
|
+
setLoading(false);
|
|
2788
|
+
}, [mero, blobId, contextId, type, revoke]);
|
|
2789
|
+
react.useEffect(() => {
|
|
2790
|
+
void refetch();
|
|
2791
|
+
}, [refetch]);
|
|
2792
|
+
react.useEffect(() => revoke, [revoke]);
|
|
2793
|
+
return { url, notFound: isBlobNotFound(error), loading, error, refetch };
|
|
2794
|
+
}
|
|
2795
|
+
function useUploadBlob() {
|
|
2796
|
+
const { mero } = useMero();
|
|
2797
|
+
const { loading, error, run } = useAsyncMutation();
|
|
2798
|
+
const uploadBlob = react.useCallback(
|
|
2799
|
+
async (request) => {
|
|
2800
|
+
if (!mero) return null;
|
|
2801
|
+
return run(() => mero.admin.uploadBlob(request));
|
|
2802
|
+
},
|
|
2803
|
+
[mero, run]
|
|
2804
|
+
);
|
|
2805
|
+
return { uploadBlob, loading, error };
|
|
2806
|
+
}
|
|
2733
2807
|
function MigrationPendingBanner({
|
|
2734
2808
|
contextId,
|
|
2735
2809
|
className,
|
|
@@ -2895,6 +2969,8 @@ exports.themeToCssVars = themeToCssVars;
|
|
|
2895
2969
|
exports.useAddGroupMembers = useAddGroupMembers;
|
|
2896
2970
|
exports.useAppVersion = useAppVersion;
|
|
2897
2971
|
exports.useApplicationContexts = useApplicationContexts;
|
|
2972
|
+
exports.useBlobInfo = useBlobInfo;
|
|
2973
|
+
exports.useBlobUrl = useBlobUrl;
|
|
2898
2974
|
exports.useContextDiscovery = useContextDiscovery;
|
|
2899
2975
|
exports.useContextGroup = useContextGroup;
|
|
2900
2976
|
exports.useContexts = useContexts;
|
|
@@ -2950,6 +3026,7 @@ exports.useSubscription = useSubscription;
|
|
|
2950
3026
|
exports.useSyncGroup = useSyncGroup;
|
|
2951
3027
|
exports.useUpdateMemberRole = useUpdateMemberRole;
|
|
2952
3028
|
exports.useUpgradeGroup = useUpgradeGroup;
|
|
3029
|
+
exports.useUploadBlob = useUploadBlob;
|
|
2953
3030
|
Object.keys(meroJs).forEach(function (k) {
|
|
2954
3031
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
2955
3032
|
enumerable: true,
|