@calimero-network/mero-react 9.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 +77 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -2
- package/dist/index.d.ts +102 -2
- package/dist/index.js +75 -1
- 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
|
@@ -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,
|