@arcadiasystems/morse-uploader 0.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/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/dist/chunk-2FYBTRTH.js +562 -0
- package/dist/chunk-2FYBTRTH.js.map +1 -0
- package/dist/chunk-A2AMSP5S.js +136 -0
- package/dist/chunk-A2AMSP5S.js.map +1 -0
- package/dist/index.d.ts +241 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +472 -0
- package/dist/types-COaFRpWK.d.ts +251 -0
- package/dist/ui.d.ts +27 -0
- package/dist/ui.js +225 -0
- package/dist/ui.js.map +1 -0
- package/dist/wallet-standard.d.ts +29 -0
- package/dist/wallet-standard.js +3 -0
- package/dist/wallet-standard.js.map +1 -0
- package/package.json +116 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { toSuiAddress, TransportError, toSuiObjectId } from '@arcadiasystems/morse-sdk';
|
|
2
|
+
|
|
3
|
+
// src/wallet-standard/wallet-standard-adapter.ts
|
|
4
|
+
|
|
5
|
+
// src/wallet-standard/failed-transaction-message.ts
|
|
6
|
+
function failedTransactionMessage(failed) {
|
|
7
|
+
const error = failed.effects?.status?.error;
|
|
8
|
+
if (error) {
|
|
9
|
+
return `transaction failed: ${error.message}`;
|
|
10
|
+
}
|
|
11
|
+
return "transaction failed without effects status";
|
|
12
|
+
}
|
|
13
|
+
function collectCreatedObjects(changes, objectTypes) {
|
|
14
|
+
const out = [];
|
|
15
|
+
for (const change of changes) {
|
|
16
|
+
if (change.idOperation !== "Created") continue;
|
|
17
|
+
const objectType = objectTypes[change.objectId];
|
|
18
|
+
if (objectType === void 0) continue;
|
|
19
|
+
out.push({ objectId: toSuiObjectId(change.objectId), objectType });
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function collectDeletedObjects(changes) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const change of changes) {
|
|
26
|
+
if (change.idOperation !== "Deleted") continue;
|
|
27
|
+
out.push({ objectId: toSuiObjectId(change.objectId) });
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/wallet-standard/compute-gas-used-mist.ts
|
|
33
|
+
function computeGasUsedMist(gas) {
|
|
34
|
+
return BigInt(gas.computationCost) + BigInt(gas.storageCost) - BigInt(gas.storageRebate);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/wallet-standard/parse-tx-receipt.ts
|
|
38
|
+
function parseTxReceipt(tx, objectTypes = tx.objectTypes ?? {}) {
|
|
39
|
+
const effects = tx.effects;
|
|
40
|
+
if (!effects) {
|
|
41
|
+
throw new TransportError(
|
|
42
|
+
`transaction effects missing despite include flag (receipt keys: ${Object.keys(
|
|
43
|
+
tx
|
|
44
|
+
).join(", ")})`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
digest: tx.digest,
|
|
49
|
+
gasUsedMist: computeGasUsedMist(effects.gasUsed),
|
|
50
|
+
createdObjects: collectCreatedObjects(effects.changedObjects, objectTypes),
|
|
51
|
+
deletedObjects: collectDeletedObjects(effects.changedObjects)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/wallet-standard/resolve-object-types.ts
|
|
56
|
+
async function resolveObjectTypes(client, ids, signal) {
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const objectId of ids) {
|
|
59
|
+
try {
|
|
60
|
+
const response = await client.getObject({
|
|
61
|
+
objectId,
|
|
62
|
+
...signal === void 0 ? {} : { signal }
|
|
63
|
+
});
|
|
64
|
+
out[objectId] = response.object.type;
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/wallet-standard/wallet-standard-adapter.ts
|
|
72
|
+
var WalletStandardAdapter = class {
|
|
73
|
+
constructor(address, signAndExecute, client) {
|
|
74
|
+
this.signAndExecute = signAndExecute;
|
|
75
|
+
this.client = client;
|
|
76
|
+
this.address = toSuiAddress(address);
|
|
77
|
+
}
|
|
78
|
+
signAndExecute;
|
|
79
|
+
client;
|
|
80
|
+
address;
|
|
81
|
+
async signAndExecuteTransaction(tx, signal) {
|
|
82
|
+
let digest;
|
|
83
|
+
try {
|
|
84
|
+
const result = await this.signAndExecute({ transaction: tx });
|
|
85
|
+
digest = result.digest;
|
|
86
|
+
} catch (cause) {
|
|
87
|
+
throw new TransportError(
|
|
88
|
+
`wallet sign-and-execute failed: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
89
|
+
{ cause }
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const final = await this.client.waitForTransaction({
|
|
93
|
+
digest,
|
|
94
|
+
include: { effects: true, objectTypes: true },
|
|
95
|
+
...signal === void 0 ? {} : { signal }
|
|
96
|
+
});
|
|
97
|
+
if (final.$kind === "FailedTransaction") {
|
|
98
|
+
throw new TransportError(
|
|
99
|
+
failedTransactionMessage(final.FailedTransaction)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const executed = final.Transaction;
|
|
103
|
+
const objectTypes = {
|
|
104
|
+
...executed.objectTypes ?? {}
|
|
105
|
+
};
|
|
106
|
+
const missing = (executed.effects?.changedObjects ?? []).filter(
|
|
107
|
+
(change) => change.idOperation === "Created" && objectTypes[change.objectId] === void 0
|
|
108
|
+
).map((change) => change.objectId);
|
|
109
|
+
if (missing.length > 0) {
|
|
110
|
+
Object.assign(
|
|
111
|
+
objectTypes,
|
|
112
|
+
await resolveObjectTypes(this.client, missing, signal)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return parseTxReceipt(executed, objectTypes);
|
|
116
|
+
}
|
|
117
|
+
async simulateTransaction(tx, signal) {
|
|
118
|
+
tx.setSenderIfNotSet(this.address);
|
|
119
|
+
const result = await this.client.simulateTransaction({
|
|
120
|
+
transaction: tx,
|
|
121
|
+
include: { effects: true, commandResults: true },
|
|
122
|
+
...signal === void 0 ? {} : { signal }
|
|
123
|
+
});
|
|
124
|
+
if (result.$kind === "FailedTransaction") {
|
|
125
|
+
throw new TransportError(
|
|
126
|
+
failedTransactionMessage(result.FailedTransaction)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const commandResults = result.commandResults ?? [];
|
|
130
|
+
return commandResults.map((cmd) => cmd.returnValues.map((rv) => rv.bcs));
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export { WalletStandardAdapter };
|
|
135
|
+
//# sourceMappingURL=chunk-A2AMSP5S.js.map
|
|
136
|
+
//# sourceMappingURL=chunk-A2AMSP5S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/wallet-standard/failed-transaction-message.ts","../src/wallet-standard/collect-created-objects.ts","../src/wallet-standard/collect-deleted-objects.ts","../src/wallet-standard/compute-gas-used-mist.ts","../src/wallet-standard/parse-tx-receipt.ts","../src/wallet-standard/resolve-object-types.ts","../src/wallet-standard/wallet-standard-adapter.ts"],"names":["toSuiObjectId","TransportError"],"mappings":"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","file":"chunk-A2AMSP5S.js","sourcesContent":["import type { SuiClientTypes } from \"@mysten/sui/client\";\n\n/** Human-readable message for a failed transaction's effects status. */\nexport function failedTransactionMessage(\n\tfailed: SuiClientTypes.Transaction<{ effects: true }>,\n): string {\n\tconst error = failed.effects?.status?.error;\n\tif (error) {\n\t\treturn `transaction failed: ${error.message}`;\n\t}\n\treturn \"transaction failed without effects status\";\n}\n","import { type TxCreatedObject, toSuiObjectId } from \"@arcadiasystems/morse-sdk\";\nimport type { SuiClientTypes } from \"@mysten/sui/client\";\n\n/**\n * Created objects from a transaction's changed-object set, paired with their\n * on-chain types. Skips changes with no resolved type.\n */\nexport function collectCreatedObjects(\n\tchanges: readonly SuiClientTypes.ChangedObject[],\n\tobjectTypes: Record<string, string>,\n): TxCreatedObject[] {\n\tconst out: TxCreatedObject[] = [];\n\tfor (const change of changes) {\n\t\tif (change.idOperation !== \"Created\") continue;\n\t\tconst objectType = objectTypes[change.objectId];\n\t\tif (objectType === undefined) continue;\n\t\tout.push({ objectId: toSuiObjectId(change.objectId), objectType });\n\t}\n\treturn out;\n}\n","import { type TxDeletedObject, toSuiObjectId } from \"@arcadiasystems/morse-sdk\";\nimport type { SuiClientTypes } from \"@mysten/sui/client\";\n\n/** Deleted objects from a transaction's changed-object set. */\nexport function collectDeletedObjects(\n\tchanges: readonly SuiClientTypes.ChangedObject[],\n): TxDeletedObject[] {\n\tconst out: TxDeletedObject[] = [];\n\tfor (const change of changes) {\n\t\tif (change.idOperation !== \"Deleted\") continue;\n\t\tout.push({ objectId: toSuiObjectId(change.objectId) });\n\t}\n\treturn out;\n}\n","import type { SuiClientTypes } from \"@mysten/sui/client\";\n\n/** Net gas spent in MIST: computation + storage cost, minus the storage rebate. */\nexport function computeGasUsedMist(gas: SuiClientTypes.GasCostSummary): bigint {\n\treturn (\n\t\tBigInt(gas.computationCost) +\n\t\tBigInt(gas.storageCost) -\n\t\tBigInt(gas.storageRebate)\n\t);\n}\n","import { TransportError, type TxReceipt } from \"@arcadiasystems/morse-sdk\";\nimport type { SuiClientTypes } from \"@mysten/sui/client\";\nimport { collectCreatedObjects } from \"./collect-created-objects.js\";\nimport { collectDeletedObjects } from \"./collect-deleted-objects.js\";\nimport { computeGasUsedMist } from \"./compute-gas-used-mist.js\";\n\n/**\n * Map a finalized Sui transaction (with effects + objectTypes included) into\n * the SDK's `TxReceipt` shape. `objectTypes` defaults to the transaction's own\n * `objectTypes`, but the adapter can pass an augmented map when it had to\n * resolve created-object types separately.\n *\n * @throws {TransportError} when effects are missing despite the include flag.\n */\nexport function parseTxReceipt(\n\ttx: SuiClientTypes.Transaction<{ effects: true; objectTypes: true }>,\n\tobjectTypes: Record<string, string> = tx.objectTypes ?? {},\n): TxReceipt {\n\tconst effects = tx.effects;\n\tif (!effects) {\n\t\tthrow new TransportError(\n\t\t\t`transaction effects missing despite include flag (receipt keys: ${Object.keys(\n\t\t\t\ttx,\n\t\t\t).join(\", \")})`,\n\t\t);\n\t}\n\treturn {\n\t\tdigest: tx.digest,\n\t\tgasUsedMist: computeGasUsedMist(effects.gasUsed),\n\t\tcreatedObjects: collectCreatedObjects(effects.changedObjects, objectTypes),\n\t\tdeletedObjects: collectDeletedObjects(effects.changedObjects),\n\t};\n}\n","import type { ObjectReader } from \"@arcadiasystems/morse-sdk\";\n\n/**\n * Resolve the Move type of each object id via `getObject`, returning an\n * `id -> type` map. Ids that cannot be read are omitted (best effort). Used to\n * backfill created-object types the transaction receipt did not include, which\n * happens when the executing client (e.g. a wallet's fullnode) and the reading\n * client (the SDK's gRPC client) are different endpoints.\n */\nexport async function resolveObjectTypes(\n\tclient: ObjectReader,\n\tids: readonly string[],\n\tsignal?: AbortSignal,\n): Promise<Record<string, string>> {\n\tconst out: Record<string, string> = {};\n\tfor (const objectId of ids) {\n\t\ttry {\n\t\t\tconst response = await client.getObject({\n\t\t\t\tobjectId,\n\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t});\n\t\t\tout[objectId] = response.object.type;\n\t\t} catch {\n\t\t\t// Skip ids that cannot be resolved; findCreatedId surfaces the rest.\n\t\t}\n\t}\n\treturn out;\n}\n","import {\n\ttype ObjectReader,\n\ttype SimulationReturnValues,\n\ttype SuiAddress,\n\ttype TransactionExecutor,\n\tTransportError,\n\ttype TxReceipt,\n\ttoSuiAddress,\n\ttype WalletAdapter,\n} from \"@arcadiasystems/morse-sdk\";\nimport type { Transaction } from \"@mysten/sui/transactions\";\nimport { failedTransactionMessage } from \"./failed-transaction-message.js\";\nimport { parseTxReceipt } from \"./parse-tx-receipt.js\";\nimport { resolveObjectTypes } from \"./resolve-object-types.js\";\nimport type { WalletSignAndExecute } from \"./types.js\";\n\n/**\n * Browser `WalletAdapter` for morse-sdk ops, backed by a wallet's\n * sign-and-execute callback (e.g. dapp-kit). Vendored here as an optional\n * helper so consumers do not hand-copy it. The user's key never leaves the\n * wallet; the dapp-kit wiring stays in the host app.\n */\nexport class WalletStandardAdapter implements WalletAdapter {\n\treadonly address: SuiAddress;\n\n\tconstructor(\n\t\taddress: string,\n\t\tprivate readonly signAndExecute: WalletSignAndExecute,\n\t\tprivate readonly client: TransactionExecutor & ObjectReader,\n\t) {\n\t\tthis.address = toSuiAddress(address);\n\t}\n\n\tasync signAndExecuteTransaction(\n\t\ttx: Transaction,\n\t\tsignal?: AbortSignal,\n\t): Promise<TxReceipt> {\n\t\tlet digest: string;\n\t\ttry {\n\t\t\tconst result = await this.signAndExecute({ transaction: tx });\n\t\t\tdigest = result.digest;\n\t\t} catch (cause) {\n\t\t\tthrow new TransportError(\n\t\t\t\t`wallet sign-and-execute failed: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t\t{ cause },\n\t\t\t);\n\t\t}\n\n\t\tconst final = await this.client.waitForTransaction({\n\t\t\tdigest,\n\t\t\tinclude: { effects: true, objectTypes: true },\n\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t});\n\t\tif (final.$kind === \"FailedTransaction\") {\n\t\t\tthrow new TransportError(\n\t\t\t\tfailedTransactionMessage(final.FailedTransaction),\n\t\t\t);\n\t\t}\n\t\tconst executed = final.Transaction;\n\n\t\t// When the wallet executes on a different endpoint than this client reads\n\t\t// from, waitForTransaction may omit created-object types. Backfill the\n\t\t// missing ones via getObject so the SDK can find the created objects.\n\t\tconst objectTypes: Record<string, string> = {\n\t\t\t...(executed.objectTypes ?? {}),\n\t\t};\n\t\tconst missing = (executed.effects?.changedObjects ?? [])\n\t\t\t.filter(\n\t\t\t\t(change) =>\n\t\t\t\t\tchange.idOperation === \"Created\" &&\n\t\t\t\t\tobjectTypes[change.objectId] === undefined,\n\t\t\t)\n\t\t\t.map((change) => change.objectId);\n\t\tif (missing.length > 0) {\n\t\t\tObject.assign(\n\t\t\t\tobjectTypes,\n\t\t\t\tawait resolveObjectTypes(this.client, missing, signal),\n\t\t\t);\n\t\t}\n\t\treturn parseTxReceipt(executed, objectTypes);\n\t}\n\n\tasync simulateTransaction(\n\t\ttx: Transaction,\n\t\tsignal?: AbortSignal,\n\t): Promise<SimulationReturnValues> {\n\t\ttx.setSenderIfNotSet(this.address);\n\t\tconst result = await this.client.simulateTransaction({\n\t\t\ttransaction: tx,\n\t\t\tinclude: { effects: true, commandResults: true },\n\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t});\n\t\tif (result.$kind === \"FailedTransaction\") {\n\t\t\tthrow new TransportError(\n\t\t\t\tfailedTransactionMessage(result.FailedTransaction),\n\t\t\t);\n\t\t}\n\t\tconst commandResults = result.commandResults ?? [];\n\t\treturn commandResults.map((cmd) => cmd.returnValues.map((rv) => rv.bcs));\n\t}\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { B as BuildShareLinkArgs, U as UsdCostArgs, a as UsdCostEstimate, M as MorseFilesHandles, P as ParsedShareLink, F as FilePhase, b as UseFileDecryptResult, S as SealMaterial, c as UseFileDownloadResult, d as UseFileUploadResult, e as UseMorseFilesOptions, f as UseMorseFilesResult, g as UseRecipientFileResult, h as UseStorageCostEstimateOptions, i as UseStorageCostEstimateResult } from './types-COaFRpWK.js';
|
|
2
|
+
export { C as CompletedFileUpload, j as FileDecryptInput, k as FileDecryptStatus, l as FileDownloadStatus, m as FileUploadInput, n as FileUploadStatus, L as LoadStatus, o as MorseFileDownloaderProps, p as MorseFileUploaderProps, q as MorseFilesWalletCallbacks, r as SetupStatus } from './types-COaFRpWK.js';
|
|
3
|
+
import { ReactNode, ReactElement, RefObject, ChangeEvent, MouseEvent, KeyboardEvent, DragEvent } from 'react';
|
|
4
|
+
import { RecipientFileId } from '@arcadiasystems/morse-sdk';
|
|
5
|
+
import '@mysten/seal';
|
|
6
|
+
import '@mysten/sui/cryptography';
|
|
7
|
+
import '@mysten/sui/grpc';
|
|
8
|
+
import '@mysten/sui/transactions';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Approximate bytes the Seal envelope adds to a ciphertext, used to nudge the
|
|
12
|
+
* cost estimate when encryption is enabled. Small relative to most files;
|
|
13
|
+
* consumers override per-call via `encryptionOverheadBytes`.
|
|
14
|
+
*/
|
|
15
|
+
declare const DEFAULT_ENCRYPTION_OVERHEAD_BYTES = 150;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build a share link for a RecipientFile. The fileId goes in the URL fragment
|
|
19
|
+
* and the Seal material (prefix + nonce, if any) as hex `p`/`n` queries inside
|
|
20
|
+
* the fragment, so they never reach a server log. The seal material is not a
|
|
21
|
+
* secret: decryption is still gated by the file's recipient set. SSR-safe
|
|
22
|
+
* (falls back to an empty base outside a browser).
|
|
23
|
+
*/
|
|
24
|
+
declare function buildShareLink(args: BuildShareLinkArgs): string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Nominal Walrus epoch length in milliseconds for a network. Used by the USD
|
|
28
|
+
* cost estimate to convert an epoch count into months. These are the documented
|
|
29
|
+
* nominal lengths (testnet 1 day, mainnet 2 weeks), not a live chain read.
|
|
30
|
+
*/
|
|
31
|
+
declare function epochDurationMs(network: "testnet" | "mainnet"): number;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Estimate Walrus storage cost in USD from a blob size and a storage duration.
|
|
35
|
+
* Duration is given in epochs plus the epoch length (read from chain), since an
|
|
36
|
+
* epoch is 1 day on testnet and 2 weeks on mainnet. Encryption overhead can be
|
|
37
|
+
* folded in so the encrypt toggle moves the figure.
|
|
38
|
+
*/
|
|
39
|
+
declare function estimateStorageUsd(args: UsdCostArgs): UsdCostEstimate;
|
|
40
|
+
|
|
41
|
+
/** Props for {@link MorseFilesProvider}. */
|
|
42
|
+
interface MorseFilesProviderProps {
|
|
43
|
+
/** The handles from {@link useMorseFiles} (memoized by that hook). */
|
|
44
|
+
readonly value: MorseFilesHandles;
|
|
45
|
+
readonly children: ReactNode;
|
|
46
|
+
}
|
|
47
|
+
/** Provides the injected files-flow handles to the hooks and components. */
|
|
48
|
+
declare function MorseFilesProvider({ value, children, }: MorseFilesProviderProps): ReactElement;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parse a share link produced by {@link buildShareLink}. Reads the fileId and
|
|
52
|
+
* optional Seal material (prefix + nonce) from the URL fragment. Returns null
|
|
53
|
+
* when the fragment does not match the expected shape; `sealIdPrefix`/`sealNonce`
|
|
54
|
+
* are null for a public file (no seal material in the link).
|
|
55
|
+
*/
|
|
56
|
+
declare function parseShareLink(href: string): ParsedShareLink | null;
|
|
57
|
+
|
|
58
|
+
/** Human-readable label for an upload phase, for the progress stepper. */
|
|
59
|
+
declare function uploadPhaseLabel(phase: FilePhase): string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The ordered upload phases for the progress stepper. Public uploads skip the
|
|
63
|
+
* `encrypting` phase since there is no Seal step.
|
|
64
|
+
*/
|
|
65
|
+
declare function uploadPhases(encrypted: boolean): readonly FilePhase[];
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Decrypt + return the plaintext of an encrypted RecipientFile. Reads the
|
|
69
|
+
* ciphertext from Walrus, creates (and reuses) a Seal SessionKey, and decrypts
|
|
70
|
+
* under the file's recipient policy. Re-signs once on a session-expired error.
|
|
71
|
+
* The SessionKey is reusable across files for the same package, so it is cached
|
|
72
|
+
* for the lifetime of the hook.
|
|
73
|
+
*/
|
|
74
|
+
declare function useFileDecrypt(): UseFileDecryptResult;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Drive the single-file download page. Loads the on-chain metadata, then
|
|
78
|
+
* fetches the bytes on demand. A RecipientFile has no on-chain "encrypted"
|
|
79
|
+
* flag, so the caller signals encryption by passing the Seal material from the
|
|
80
|
+
* share link: when present, the bytes are decrypted (one SessionKey signature);
|
|
81
|
+
* when null, they are read straight from Walrus as a public file.
|
|
82
|
+
*
|
|
83
|
+
* @param fileId The on-chain RecipientFile id (from the share link).
|
|
84
|
+
* @param seal The Seal material (prefix + nonce) for encrypted files; null for public.
|
|
85
|
+
*/
|
|
86
|
+
declare function useFileDownload(fileId: RecipientFileId | null, seal: SealMaterial | null): UseFileDownloadResult;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Upload one file as a RecipientFile. With `encrypt`, it encrypts under Seal
|
|
90
|
+
* (only the recipients, plus the connected wallet, can decrypt); otherwise it
|
|
91
|
+
* uploads a public file. Either way the recipient set is set at creation in two
|
|
92
|
+
* wallet popups. Tracks the phase stepper and returns the fileId, the seal
|
|
93
|
+
* material (encrypted only), and a share link.
|
|
94
|
+
*/
|
|
95
|
+
declare function useFileUpload(): UseFileUploadResult;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Setup hook for the files flow. Builds the SDK handles from the connected
|
|
99
|
+
* wallet: a `WalletStandardAdapter` for on-chain ops, a `WalletStandardSigner`
|
|
100
|
+
* (via fromAccountAsync + a pubkey cache) for Walrus writes and the Seal
|
|
101
|
+
* SessionKey, the Walrus write/read adapters, the Seal adapter, and the files
|
|
102
|
+
* reader. Async because fromAccountAsync may probe the wallet once.
|
|
103
|
+
*
|
|
104
|
+
* Wrap your tree in `<MorseFilesProvider value={result.handles}>` once `status`
|
|
105
|
+
* is `"ready"`.
|
|
106
|
+
*/
|
|
107
|
+
declare function useMorseFiles(options: UseMorseFilesOptions): UseMorseFilesResult;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Read the files-flow handles.
|
|
111
|
+
*
|
|
112
|
+
* @throws {Error} when used outside a {@link MorseFilesProvider}.
|
|
113
|
+
*/
|
|
114
|
+
declare function useMorseFilesContext(): MorseFilesHandles;
|
|
115
|
+
|
|
116
|
+
/** Load a RecipientFile's on-chain metadata by id. Loads on mount. */
|
|
117
|
+
declare function useRecipientFile(fileId: RecipientFileId | null): UseRecipientFileResult;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Deterministic USD storage-cost estimate for a pending upload. Uses Walrus
|
|
121
|
+
* predictable pricing ($0.023/GB/month) with the network's nominal epoch length
|
|
122
|
+
* from the active handles. When `encrypt` is set, the Seal envelope overhead is
|
|
123
|
+
* folded into the priced size so the figure tracks the encrypt toggle.
|
|
124
|
+
*/
|
|
125
|
+
declare function useStorageCostEstimate(options: UseStorageCostEstimateOptions): UseStorageCostEstimateResult;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Shared types for the headless `useDropzone` primitive. The 0.2.0 files-flow
|
|
129
|
+
* types live in `src/files/types.ts`.
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
/** Options for {@link useDropzone}. */
|
|
133
|
+
interface UseDropzoneOptions {
|
|
134
|
+
/** Called with the chosen files from a drop or the file dialog. */
|
|
135
|
+
readonly onFiles: (files: File[]) => void;
|
|
136
|
+
/** The file input `accept` attribute (e.g. "image/*"). */
|
|
137
|
+
readonly accept?: string;
|
|
138
|
+
/** Allow selecting multiple files (default true). */
|
|
139
|
+
readonly multiple?: boolean;
|
|
140
|
+
readonly disabled?: boolean;
|
|
141
|
+
/** Accessible label applied to the root button via `aria-label`. */
|
|
142
|
+
readonly ariaLabel?: string;
|
|
143
|
+
}
|
|
144
|
+
/** Props to spread on the dropzone root element. */
|
|
145
|
+
interface DropzoneRootProps {
|
|
146
|
+
readonly role: "button";
|
|
147
|
+
readonly tabIndex: number;
|
|
148
|
+
readonly "aria-disabled": boolean;
|
|
149
|
+
readonly "aria-label"?: string;
|
|
150
|
+
readonly "data-dragging": "true" | undefined;
|
|
151
|
+
readonly onClick: (event: MouseEvent<HTMLElement>) => void;
|
|
152
|
+
readonly onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
153
|
+
readonly onDragEnter: (event: DragEvent<HTMLElement>) => void;
|
|
154
|
+
readonly onDragOver: (event: DragEvent<HTMLElement>) => void;
|
|
155
|
+
readonly onDragLeave: (event: DragEvent<HTMLElement>) => void;
|
|
156
|
+
readonly onDrop: (event: DragEvent<HTMLElement>) => void;
|
|
157
|
+
}
|
|
158
|
+
/** Props to spread on the hidden file input element. */
|
|
159
|
+
interface DropzoneInputProps {
|
|
160
|
+
readonly ref: RefObject<HTMLInputElement>;
|
|
161
|
+
readonly type: "file";
|
|
162
|
+
readonly multiple: boolean;
|
|
163
|
+
readonly tabIndex: number;
|
|
164
|
+
readonly accept?: string;
|
|
165
|
+
readonly disabled: boolean;
|
|
166
|
+
readonly onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
167
|
+
/** Stops the programmatic click from bubbling to the root and re-opening. */
|
|
168
|
+
readonly onClick: (event: MouseEvent<HTMLInputElement>) => void;
|
|
169
|
+
}
|
|
170
|
+
/** Return value of {@link useClipboard}. */
|
|
171
|
+
interface UseClipboardResult {
|
|
172
|
+
/** True for `resetMs` after a successful copy. */
|
|
173
|
+
readonly copied: boolean;
|
|
174
|
+
/** Copy text; resolves to whether the write succeeded. */
|
|
175
|
+
readonly copy: (text: string) => Promise<boolean>;
|
|
176
|
+
}
|
|
177
|
+
/** Return value of {@link useDropzone}. */
|
|
178
|
+
interface UseDropzoneResult {
|
|
179
|
+
readonly isDragging: boolean;
|
|
180
|
+
/** Open the file dialog programmatically. */
|
|
181
|
+
readonly open: () => void;
|
|
182
|
+
readonly getRootProps: () => DropzoneRootProps;
|
|
183
|
+
readonly getInputProps: () => DropzoneInputProps;
|
|
184
|
+
readonly inputRef: RefObject<HTMLInputElement>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Copy text to the clipboard and flag success for a short window so a button
|
|
189
|
+
* can show "Copied". The `copied` flag resets after `resetMs`. No-op (returns
|
|
190
|
+
* false) when the Clipboard API is unavailable or the write is rejected.
|
|
191
|
+
*/
|
|
192
|
+
declare function useClipboard(resetMs?: number): UseClipboardResult;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Headless drag-and-drop file selection. Returns prop-getters for a root
|
|
196
|
+
* element (keyboard-operable button with ARIA + drag state) and a hidden file
|
|
197
|
+
* input, so consumers own all markup and styling. No `window`/`document`
|
|
198
|
+
* access; SSR-safe.
|
|
199
|
+
*/
|
|
200
|
+
declare function useDropzone(options: UseDropzoneOptions): UseDropzoneResult;
|
|
201
|
+
|
|
202
|
+
/** Encode bytes as a lowercase hex string (no `0x` prefix). */
|
|
203
|
+
declare function bytesToHex(bytes: Uint8Array): string;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Format a byte count as a human-readable size (e.g. 2348 -> "2.29 KB").
|
|
207
|
+
*
|
|
208
|
+
* Uses binary units. Whole bytes are shown without a fraction; larger units use
|
|
209
|
+
* `fractionDigits` decimals. Non-finite or non-positive input returns "0 B".
|
|
210
|
+
*/
|
|
211
|
+
declare function formatBytes(bytes: number, fractionDigits?: number): string;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Format a USD amount for display. A cent or more uses two decimals; sub-cent
|
|
215
|
+
* amounts (common for short Walrus leases) show two significant digits in plain
|
|
216
|
+
* decimal notation; amounts below a millionth of a dollar show a `< $` floor.
|
|
217
|
+
* Never uses scientific notation.
|
|
218
|
+
*/
|
|
219
|
+
declare function formatUsd(usd: number): string;
|
|
220
|
+
|
|
221
|
+
/** Decode a hex string (with or without a `0x` prefix) into bytes. */
|
|
222
|
+
declare function hexToBytes(hex: string): Uint8Array;
|
|
223
|
+
|
|
224
|
+
/** True when a content type is a browser-previewable image. */
|
|
225
|
+
declare function isPreviewableImage(contentType: string): boolean;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Return the current browser origin (e.g. "https://app.example.com"), or null
|
|
229
|
+
* when running outside a browser (SSR, Node, web worker without a location).
|
|
230
|
+
*
|
|
231
|
+
* SSR-safe: never throws on a missing `window`.
|
|
232
|
+
*/
|
|
233
|
+
declare function resolveOrigin(): string | null;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Trigger a browser download of an object URL under a given filename. Creates a
|
|
237
|
+
* transient anchor, clicks it, and removes it. No-op outside a DOM.
|
|
238
|
+
*/
|
|
239
|
+
declare function triggerDownload(objectUrl: string, filename: string): void;
|
|
240
|
+
|
|
241
|
+
export { BuildShareLinkArgs, DEFAULT_ENCRYPTION_OVERHEAD_BYTES, type DropzoneInputProps, type DropzoneRootProps, FilePhase, MorseFilesHandles, MorseFilesProvider, type MorseFilesProviderProps, ParsedShareLink, SealMaterial, UsdCostArgs, UsdCostEstimate, type UseClipboardResult, type UseDropzoneOptions, type UseDropzoneResult, UseFileDecryptResult, UseFileDownloadResult, UseFileUploadResult, UseMorseFilesOptions, UseMorseFilesResult, UseRecipientFileResult, UseStorageCostEstimateOptions, UseStorageCostEstimateResult, buildShareLink, bytesToHex, epochDurationMs, estimateStorageUsd, formatBytes, formatUsd, hexToBytes, isPreviewableImage, parseShareLink, resolveOrigin, triggerDownload, uploadPhaseLabel, uploadPhases, useClipboard, useDropzone, useFileDecrypt, useFileDownload, useFileUpload, useMorseFiles, useMorseFilesContext, useRecipientFile, useStorageCostEstimate };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { MorseFilesContext } from './chunk-2FYBTRTH.js';
|
|
2
|
+
export { DEFAULT_ENCRYPTION_OVERHEAD_BYTES, buildShareLink, bytesToHex, epochDurationMs, estimateStorageUsd, formatBytes, formatUsd, isPreviewableImage, resolveOrigin, triggerDownload, uploadPhaseLabel, uploadPhases, useClipboard, useDropzone, useFileDecrypt, useFileDownload, useFileUpload, useMorseFilesContext, useRecipientFile, useStorageCostEstimate } from './chunk-2FYBTRTH.js';
|
|
3
|
+
import { WalletStandardAdapter } from './chunk-A2AMSP5S.js';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
import { morseConfig, WalletStandardSigner, DefaultWalrusWriteAdapter, HttpAggregatorReadAdapter, DefaultSealAdapter, RpcRecipientFilesReader, BrowserStoragePubkeyCache } from '@arcadiasystems/morse-sdk';
|
|
6
|
+
import { SuiGrpcClient } from '@mysten/sui/grpc';
|
|
7
|
+
import { useState, useRef, useEffect } from 'react';
|
|
8
|
+
|
|
9
|
+
function MorseFilesProvider({
|
|
10
|
+
value,
|
|
11
|
+
children
|
|
12
|
+
}) {
|
|
13
|
+
return /* @__PURE__ */ jsx(MorseFilesContext.Provider, { value, children });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/utils/hex-to-bytes.ts
|
|
17
|
+
function hexToBytes(hex) {
|
|
18
|
+
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
19
|
+
const out = new Uint8Array(Math.floor(clean.length / 2));
|
|
20
|
+
for (let i = 0; i < out.length; i++) {
|
|
21
|
+
out[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/files/parse-share-link.ts
|
|
27
|
+
var FRAGMENT_RE = /^\/?f\/([^?]+)(?:\?(.*))?$/;
|
|
28
|
+
function parseShareLink(href) {
|
|
29
|
+
const hashIndex = href.indexOf("#");
|
|
30
|
+
const fragment = hashIndex >= 0 ? href.slice(hashIndex + 1) : href;
|
|
31
|
+
const match = FRAGMENT_RE.exec(fragment);
|
|
32
|
+
const rawFileId = match?.[1];
|
|
33
|
+
if (rawFileId === void 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const params = new URLSearchParams(match?.[2] ?? "");
|
|
37
|
+
const prefix = params.get("p");
|
|
38
|
+
const nonce = params.get("n");
|
|
39
|
+
const hasSeal = prefix !== null && nonce !== null;
|
|
40
|
+
return {
|
|
41
|
+
fileId: decodeURIComponent(rawFileId),
|
|
42
|
+
sealIdPrefix: hasSeal ? hexToBytes(prefix) : null,
|
|
43
|
+
sealNonce: hasSeal ? hexToBytes(nonce) : null
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function pickPubkeyCache() {
|
|
47
|
+
if (typeof window === "undefined") {
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return new BrowserStoragePubkeyCache();
|
|
52
|
+
} catch {
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/files/use-morse-files.ts
|
|
58
|
+
function useMorseFiles(options) {
|
|
59
|
+
const { network } = options;
|
|
60
|
+
const address = options.account.address;
|
|
61
|
+
const [result, setResult] = useState({
|
|
62
|
+
status: "idle",
|
|
63
|
+
handles: null,
|
|
64
|
+
error: null
|
|
65
|
+
});
|
|
66
|
+
const optionsRef = useRef(options);
|
|
67
|
+
optionsRef.current = options;
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
let cancelled = false;
|
|
70
|
+
setResult({ status: "preparing", handles: null, error: null });
|
|
71
|
+
(async () => {
|
|
72
|
+
try {
|
|
73
|
+
const current = optionsRef.current;
|
|
74
|
+
const { account, callbacks } = current;
|
|
75
|
+
const config = morseConfig({ network });
|
|
76
|
+
const client = current.suiClient ?? new SuiGrpcClient({ network, baseUrl: config.rpcUrl });
|
|
77
|
+
const adapter = new WalletStandardAdapter(
|
|
78
|
+
address,
|
|
79
|
+
callbacks.signAndExecuteTransaction,
|
|
80
|
+
client
|
|
81
|
+
);
|
|
82
|
+
const pubkeyCache = current.pubkeyCache ?? pickPubkeyCache();
|
|
83
|
+
const signer = await WalletStandardSigner.fromAccountAsync(
|
|
84
|
+
account,
|
|
85
|
+
{
|
|
86
|
+
signTransaction: callbacks.signTransaction,
|
|
87
|
+
signPersonalMessage: callbacks.signPersonalMessage
|
|
88
|
+
},
|
|
89
|
+
pubkeyCache === void 0 ? void 0 : { pubkeyCache }
|
|
90
|
+
);
|
|
91
|
+
if (cancelled) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const walrusWrite = current.walrusWrite ?? DefaultWalrusWriteAdapter.fromConfig(
|
|
95
|
+
{
|
|
96
|
+
network,
|
|
97
|
+
suiClient: client,
|
|
98
|
+
...current.walrusWriteConfig ?? {}
|
|
99
|
+
},
|
|
100
|
+
signer
|
|
101
|
+
);
|
|
102
|
+
const walrusRead = current.walrusRead ?? HttpAggregatorReadAdapter.fromMorseConfig(config, client);
|
|
103
|
+
const seal = DefaultSealAdapter.fromMorseConfig(config, {}, client);
|
|
104
|
+
const reader = RpcRecipientFilesReader.fromConfig(client, {
|
|
105
|
+
packageId: config.packageId
|
|
106
|
+
});
|
|
107
|
+
setResult({
|
|
108
|
+
status: "ready",
|
|
109
|
+
handles: {
|
|
110
|
+
network,
|
|
111
|
+
config,
|
|
112
|
+
client,
|
|
113
|
+
address: adapter.address,
|
|
114
|
+
adapter,
|
|
115
|
+
signer,
|
|
116
|
+
walrusWrite,
|
|
117
|
+
walrusRead,
|
|
118
|
+
seal,
|
|
119
|
+
reader
|
|
120
|
+
},
|
|
121
|
+
error: null
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (!cancelled) {
|
|
125
|
+
setResult({ status: "error", handles: null, error });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
return () => {
|
|
130
|
+
cancelled = true;
|
|
131
|
+
};
|
|
132
|
+
}, [network, address]);
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export { MorseFilesProvider, hexToBytes, parseShareLink, useMorseFiles };
|
|
137
|
+
//# sourceMappingURL=index.js.map
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/files/morse-files-provider.tsx","../src/utils/hex-to-bytes.ts","../src/files/parse-share-link.ts","../src/files/pick-pubkey-cache.ts","../src/files/use-morse-files.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ReactElement, ReactNode } from \"react\";\nimport { MorseFilesContext } from \"./context.js\";\nimport type { MorseFilesHandles } from \"./types.js\";\n\n/** Props for {@link MorseFilesProvider}. */\nexport interface MorseFilesProviderProps {\n\t/** The handles from {@link useMorseFiles} (memoized by that hook). */\n\treadonly value: MorseFilesHandles;\n\treadonly children: ReactNode;\n}\n\n/** Provides the injected files-flow handles to the hooks and components. */\nexport function MorseFilesProvider({\n\tvalue,\n\tchildren,\n}: MorseFilesProviderProps): ReactElement {\n\treturn (\n\t\t<MorseFilesContext.Provider value={value}>\n\t\t\t{children}\n\t\t</MorseFilesContext.Provider>\n\t);\n}\n","/** Decode a hex string (with or without a `0x` prefix) into bytes. */\nexport function hexToBytes(hex: string): Uint8Array {\n\tconst clean = hex.startsWith(\"0x\") ? hex.slice(2) : hex;\n\tconst out = new Uint8Array(Math.floor(clean.length / 2));\n\tfor (let i = 0; i < out.length; i++) {\n\t\tout[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n\t}\n\treturn out;\n}\n","import { hexToBytes } from \"../utils/hex-to-bytes.js\";\nimport type { ParsedShareLink } from \"./types.js\";\n\n// Matches the fragment shape \"f/<fileId>\" optionally followed by a query.\nconst FRAGMENT_RE = /^\\/?f\\/([^?]+)(?:\\?(.*))?$/;\n\n/**\n * Parse a share link produced by {@link buildShareLink}. Reads the fileId and\n * optional Seal material (prefix + nonce) from the URL fragment. Returns null\n * when the fragment does not match the expected shape; `sealIdPrefix`/`sealNonce`\n * are null for a public file (no seal material in the link).\n */\nexport function parseShareLink(href: string): ParsedShareLink | null {\n\tconst hashIndex = href.indexOf(\"#\");\n\tconst fragment = hashIndex >= 0 ? href.slice(hashIndex + 1) : href;\n\tconst match = FRAGMENT_RE.exec(fragment);\n\tconst rawFileId = match?.[1];\n\tif (rawFileId === undefined) {\n\t\treturn null;\n\t}\n\tconst params = new URLSearchParams(match?.[2] ?? \"\");\n\tconst prefix = params.get(\"p\");\n\tconst nonce = params.get(\"n\");\n\tconst hasSeal = prefix !== null && nonce !== null;\n\treturn {\n\t\tfileId: decodeURIComponent(rawFileId),\n\t\tsealIdPrefix: hasSeal ? hexToBytes(prefix) : null,\n\t\tsealNonce: hasSeal ? hexToBytes(nonce) : null,\n\t};\n}\n","import {\n\tBrowserStoragePubkeyCache,\n\ttype PubkeyCache,\n} from \"@arcadiasystems/morse-sdk\";\n\n/**\n * A localStorage-backed pubkey cache in browsers, undefined elsewhere. Lets\n * fromAccountAsync skip the Phantom probe popup on subsequent reloads.\n */\nexport function pickPubkeyCache(): PubkeyCache | undefined {\n\tif (typeof window === \"undefined\") {\n\t\treturn undefined;\n\t}\n\ttry {\n\t\treturn new BrowserStoragePubkeyCache();\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n","import {\n\tDefaultSealAdapter,\n\tDefaultWalrusWriteAdapter,\n\tHttpAggregatorReadAdapter,\n\tmorseConfig,\n\tRpcRecipientFilesReader,\n\tWalletStandardSigner,\n\ttype WalrusAdapterConfig,\n} from \"@arcadiasystems/morse-sdk\";\nimport type { SealCompatibleClient } from \"@mysten/seal\";\nimport { SuiGrpcClient } from \"@mysten/sui/grpc\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { WalletStandardAdapter } from \"../wallet-standard/wallet-standard-adapter.js\";\nimport { pickPubkeyCache } from \"./pick-pubkey-cache.js\";\nimport type { UseMorseFilesOptions, UseMorseFilesResult } from \"./types.js\";\n\n/**\n * Setup hook for the files flow. Builds the SDK handles from the connected\n * wallet: a `WalletStandardAdapter` for on-chain ops, a `WalletStandardSigner`\n * (via fromAccountAsync + a pubkey cache) for Walrus writes and the Seal\n * SessionKey, the Walrus write/read adapters, the Seal adapter, and the files\n * reader. Async because fromAccountAsync may probe the wallet once.\n *\n * Wrap your tree in `<MorseFilesProvider value={result.handles}>` once `status`\n * is `\"ready\"`.\n */\nexport function useMorseFiles(\n\toptions: UseMorseFilesOptions,\n): UseMorseFilesResult {\n\tconst { network } = options;\n\tconst address = options.account.address;\n\tconst [result, setResult] = useState<UseMorseFilesResult>({\n\t\tstatus: \"idle\",\n\t\thandles: null,\n\t\terror: null,\n\t});\n\n\t// Rebuild only when the network or wallet address changes. Everything else\n\t// (the account object, callbacks, and optional adapters) is read from the\n\t// latest options via a ref, so passing fresh object/callback literals on\n\t// every render does not retrigger setup and loop.\n\tconst optionsRef = useRef(options);\n\toptionsRef.current = options;\n\n\tuseEffect(() => {\n\t\tlet cancelled = false;\n\t\tsetResult({ status: \"preparing\", handles: null, error: null });\n\t\t(async () => {\n\t\t\ttry {\n\t\t\t\tconst current = optionsRef.current;\n\t\t\t\tconst { account, callbacks } = current;\n\t\t\t\tconst config = morseConfig({ network });\n\t\t\t\t// Use the provided gRPC client (e.g. a dedicated RPC) or build one on\n\t\t\t\t// the network's public RPC.\n\t\t\t\tconst client =\n\t\t\t\t\tcurrent.suiClient ??\n\t\t\t\t\tnew SuiGrpcClient({ network, baseUrl: config.rpcUrl });\n\t\t\t\tconst adapter = new WalletStandardAdapter(\n\t\t\t\t\taddress,\n\t\t\t\t\tcallbacks.signAndExecuteTransaction,\n\t\t\t\t\tclient,\n\t\t\t\t);\n\t\t\t\tconst pubkeyCache = current.pubkeyCache ?? pickPubkeyCache();\n\t\t\t\tconst signer = await WalletStandardSigner.fromAccountAsync(\n\t\t\t\t\taccount,\n\t\t\t\t\t{\n\t\t\t\t\t\tsignTransaction: callbacks.signTransaction,\n\t\t\t\t\t\tsignPersonalMessage: callbacks.signPersonalMessage,\n\t\t\t\t\t},\n\t\t\t\t\tpubkeyCache === undefined ? undefined : { pubkeyCache },\n\t\t\t\t);\n\t\t\t\tif (cancelled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst walrusWrite =\n\t\t\t\t\tcurrent.walrusWrite ??\n\t\t\t\t\tDefaultWalrusWriteAdapter.fromConfig(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnetwork,\n\t\t\t\t\t\t\tsuiClient: client,\n\t\t\t\t\t\t\t...(current.walrusWriteConfig ?? {}),\n\t\t\t\t\t\t} as WalrusAdapterConfig,\n\t\t\t\t\t\tsigner,\n\t\t\t\t\t);\n\t\t\t\tconst walrusRead =\n\t\t\t\t\tcurrent.walrusRead ??\n\t\t\t\t\tHttpAggregatorReadAdapter.fromMorseConfig(config, client);\n\t\t\t\tconst seal = DefaultSealAdapter.fromMorseConfig(config, {}, client);\n\t\t\t\tconst reader = RpcRecipientFilesReader.fromConfig(client, {\n\t\t\t\t\tpackageId: config.packageId,\n\t\t\t\t});\n\t\t\t\tsetResult({\n\t\t\t\t\tstatus: \"ready\",\n\t\t\t\t\thandles: {\n\t\t\t\t\t\tnetwork,\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tclient: client as unknown as SealCompatibleClient,\n\t\t\t\t\t\taddress: adapter.address,\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tsigner,\n\t\t\t\t\t\twalrusWrite,\n\t\t\t\t\t\twalrusRead,\n\t\t\t\t\t\tseal,\n\t\t\t\t\t\treader,\n\t\t\t\t\t},\n\t\t\t\t\terror: null,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (!cancelled) {\n\t\t\t\t\tsetResult({ status: \"error\", handles: null, error });\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, [network, address]);\n\n\treturn result;\n}\n"]}
|