@bounded-sh/core 0.0.107 → 0.0.108

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.
Files changed (36) hide show
  1. package/dist/client/operations.d.ts +9 -1
  2. package/dist/client/solana-operations.d.ts +8 -2
  3. package/dist/client/solana-v1-operations.d.ts +2 -1
  4. package/dist/client/write-outcome.d.ts +32 -0
  5. package/dist/{index-62gjQH30.js → index-BawH-9Rq.js} +220 -194
  6. package/dist/index-BawH-9Rq.js.map +1 -0
  7. package/dist/{index-CLst0IiM.mjs → index-No5Vz0YM.mjs} +218 -193
  8. package/dist/index-No5Vz0YM.mjs.map +1 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +3 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +1 -1
  13. package/dist/{realtime-store-9SCt1RPf.js → realtime-store-D_M3T5mH.js} +2 -2
  14. package/dist/{realtime-store-9SCt1RPf.js.map → realtime-store-D_M3T5mH.js.map} +1 -1
  15. package/dist/{realtime-store-C9AgPg7U.mjs → realtime-store-iXGbzhjG.mjs} +2 -2
  16. package/dist/{realtime-store-C9AgPg7U.mjs.map → realtime-store-iXGbzhjG.mjs.map} +1 -1
  17. package/dist/{solana-message-Deru1tF1.mjs → solana-message-BBKdydqt.mjs} +2 -2
  18. package/dist/{solana-message-Deru1tF1.mjs.map → solana-message-BBKdydqt.mjs.map} +1 -1
  19. package/dist/{solana-message-Bf2q_JO1.js → solana-message-DQVrh3NC.js} +2 -2
  20. package/dist/{solana-message-Bf2q_JO1.js.map → solana-message-DQVrh3NC.js.map} +1 -1
  21. package/dist/{solana-operations-eTiLgak2.js → solana-operations-CIiyUG3b.js} +63 -16
  22. package/dist/solana-operations-CIiyUG3b.js.map +1 -0
  23. package/dist/{solana-operations-Cm19J14R.mjs → solana-operations-Dtow91W5.mjs} +61 -15
  24. package/dist/solana-operations-Dtow91W5.mjs.map +1 -0
  25. package/dist/{solana-v1-operations-Dxs4XM_F.mjs → solana-v1-operations-BF5aGRmx.mjs} +9 -8
  26. package/dist/solana-v1-operations-BF5aGRmx.mjs.map +1 -0
  27. package/dist/{solana-v1-operations-CnmiKiyj.js → solana-v1-operations-DfZr0cjj.js} +10 -9
  28. package/dist/solana-v1-operations-DfZr0cjj.js.map +1 -0
  29. package/dist/types.d.ts +1 -0
  30. package/package.json +1 -1
  31. package/dist/index-62gjQH30.js.map +0 -1
  32. package/dist/index-CLst0IiM.mjs.map +0 -1
  33. package/dist/solana-operations-Cm19J14R.mjs.map +0 -1
  34. package/dist/solana-operations-eTiLgak2.js.map +0 -1
  35. package/dist/solana-v1-operations-CnmiKiyj.js.map +0 -1
  36. package/dist/solana-v1-operations-Dxs4XM_F.mjs.map +0 -1
@@ -4190,7 +4190,7 @@ class ServerSessionManager {
4190
4190
  const sessionConfig = (_a = this.sessionConfig) !== null && _a !== void 0 ? _a : undefined;
4191
4191
  /* fetch nonce from auth API (bound to the snapshotted app+issuer) */
4192
4192
  const nonce = await genAuthNonce(sessionConfig);
4193
- const { genSolanaMessage } = await Promise.resolve().then(function () { return require('./solana-message-Bf2q_JO1.js'); });
4193
+ const { genSolanaMessage } = await Promise.resolve().then(function () { return require('./solana-message-DQVrh3NC.js'); });
4194
4194
  const message = await genSolanaMessage(address, nonce, sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.appId);
4195
4195
  /* sign the message */
4196
4196
  const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
@@ -7469,7 +7469,7 @@ async function doReconnectWithNewAuth() {
7469
7469
  console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
7470
7470
  }
7471
7471
  try {
7472
- const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return require('./realtime-store-9SCt1RPf.js'); });
7472
+ const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return require('./realtime-store-D_M3T5mH.js'); });
7473
7473
  await reconnectRealtimeStoreWithNewAuth();
7474
7474
  }
7475
7475
  catch (error) {
@@ -7697,6 +7697,37 @@ async function wsIntentReliable(appId, roomRoutePath, intent, isServer) {
7697
7697
  });
7698
7698
  }
7699
7699
 
7700
+ const PHASES = new Set(['before_broadcast', 'broadcast_rejected', 'failed_onchain']);
7701
+ /** Tag `error` as definitively not applied. An already-tagged error keeps its (earlier, more precise) phase. */
7702
+ function markWriteNotApplied(error, phase) {
7703
+ var _a;
7704
+ if (writeNotAppliedPhase(error) !== null)
7705
+ return error;
7706
+ const target = error !== null && typeof error === 'object'
7707
+ ? error
7708
+ : new Error(String(error));
7709
+ try {
7710
+ Object.assign(target, { writeOutcome: 'not_applied', writePhase: phase });
7711
+ }
7712
+ catch (_b) {
7713
+ // A frozen throwable cannot carry the tag; wrap it so the verdict is not lost.
7714
+ const wrapped = new Error(String((_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error));
7715
+ return Object.assign(wrapped, { cause: error, writeOutcome: 'not_applied', writePhase: phase });
7716
+ }
7717
+ return target;
7718
+ }
7719
+ /** The phase a write stopped at when it was definitively not applied, else `null` (ambiguous or not a write error). */
7720
+ function writeNotAppliedPhase(error) {
7721
+ if (error === null || typeof error !== 'object')
7722
+ return null;
7723
+ const outcome = error.writeOutcome;
7724
+ const phase = error.writePhase;
7725
+ return outcome === 'not_applied' && typeof phase === 'string' && PHASES.has(phase) ? phase : null;
7726
+ }
7727
+ function isWriteNotApplied(error) {
7728
+ return writeNotAppliedPhase(error) !== null;
7729
+ }
7730
+
7700
7731
  /**
7701
7732
  * Thrown when a user's wallet doesn't have enough SOL to cover the transaction.
7702
7733
  * Apps can catch this to trigger an onramp/funding flow.
@@ -8862,7 +8893,13 @@ async function setMany(many, options) {
8862
8893
  if ((error === null || error === void 0 ? void 0 : error.statusCode) === 402 && (error === null || error === void 0 ? void 0 : error.error) === 'INSUFFICIENT_BALANCE') {
8863
8894
  const deficitLamports = Number((_e = error.deficitLamports) !== null && _e !== void 0 ? _e : 0);
8864
8895
  const deficitSol = Number((_f = error.deficitSol) !== null && _f !== void 0 ? _f : deficitLamports / 1000000000);
8865
- throw new InsufficientBalanceError(String((_g = error.address) !== null && _g !== void 0 ? _g : ''), Number((_h = error.balanceLamports) !== null && _h !== void 0 ? _h : 0), Number((_j = error.estimatedCostLamports) !== null && _j !== void 0 ? _j : 0), deficitLamports, deficitSol);
8896
+ throw markWriteNotApplied(new InsufficientBalanceError(String((_g = error.address) !== null && _g !== void 0 ? _g : ''), Number((_h = error.balanceLamports) !== null && _h !== void 0 ? _h : 0), Number((_j = error.estimatedCostLamports) !== null && _j !== void 0 ? _j : 0), deficitLamports, deficitSol), 'before_broadcast');
8897
+ }
8898
+ // The server answered and refused the write (a rule or invariant denial, a bad request, no
8899
+ // session, a rate limit): nothing was built, signed or committed. A network failure, a 5xx,
8900
+ // a 408 or a 499 may follow a commit the response never reported, so those stay untagged.
8901
+ if (DEFINITIVE_WRITE_REFUSAL_STATUSES.has(Number(error === null || error === void 0 ? void 0 : error.statusCode))) {
8902
+ throw markWriteNotApplied(error, 'before_broadcast');
8866
8903
  }
8867
8904
  throw error;
8868
8905
  }
@@ -8873,118 +8910,142 @@ async function setMany(many, options) {
8873
8910
  if (setResponse.status === 202) {
8874
8911
  // This means that the document needs to be set on-chain (or signed for offchain),
8875
8912
  // the API did nothing and gave us the required info to run this transaction.
8876
- const { transactions, offchainTransaction } = setResponse.data;
8877
- // Handle offchain transaction flow (for 'offchain' protocol apps)
8878
- if (offchainTransaction && Array.isArray(transactions) && transactions.length > 0) {
8879
- throw new Error('Ambiguous write response mixes offchain and onchain transaction targets');
8880
- }
8881
- if (offchainTransaction) {
8882
- // Offchain signing is retired: no server mints this envelope any more
8883
- // (the realtime worker commits poofnet writes directly). Fail loudly
8884
- // rather than silently ignoring a payload the caller was asked to sign.
8885
- throw new Error('Unsupported 202 transaction shape: offchain signing is retired');
8886
- }
8887
- // Handle Solana on-chain transaction flow
8888
- if (!Array.isArray(transactions) || transactions.length !== 1) {
8889
- throw new Error(`Expected exactly one on-chain transaction, received ${Array.isArray(transactions) ? transactions.length : 0}`);
8890
- }
8891
- const curTx = transactions[0];
8892
- // The EVM onchain data plane is retired: no worker mints an EVM 202 any
8893
- // more. A payload that still carries one is an unexpected response and
8894
- // fails loudly BEFORE anything is signed - never silently ignored.
8895
- if ((curTx === null || curTx === void 0 ? void 0 : curTx.evmTransaction) != null) {
8896
- throw new Error('Unsupported 202 transaction shape: the write response carried an evmTransaction, ' +
8897
- 'but EVM onchain writes are no longer supported. Nothing was signed or submitted.');
8898
- }
8899
- const hasSerializedSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.serializedTransaction) != null;
8900
- const hasBuiltSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.transactionArgs) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.remainingAccounts) != null ||
8901
- (curTx === null || curTx === void 0 ? void 0 : curTx.idl) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.txData) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.preInstructions) != null;
8902
- if (hasSerializedSolanaTarget && hasBuiltSolanaTarget) {
8903
- throw new Error('Ambiguous on-chain write response mixes Solana transaction target formats');
8904
- }
8905
- // All requested paths (writes AND deletes), in request order. Used for the
8906
- // post-confirm mirror read-back, which correlates every requested path.
8907
- // The pre-sign validation derives its own full expected intent from
8908
- // `documents` (see below) and needs nothing from this list.
8909
- const requestedPaths = documents.map(d => d.destinationPath);
8910
- const requestedDocuments = buildRequestedDocuments(documents);
8911
- const appId = config.appId;
8912
- // The lane comes verbatim from the worker, and is read BEFORE anything is
8913
- // signed or submitted: a missing tag fails closed while nothing has been
8914
- // broadcast, instead of stranding a landed transaction with no receipt.
8915
- const workerChain = readWorkerChainTag((_k = setResponse.data) === null || _k === void 0 ? void 0 : _k.receipt, setResponse.data, curTx);
8916
- if (!workerChain) {
8917
- throw new Error('The write response carried no execution-chain tag, so this receipt could not state where the write ran. ' +
8918
- 'Nothing was signed or submitted.');
8919
- }
8920
- if (workerChain === 'realtime_offchain') {
8921
- // Poofnet: the wallet signs the worker's unsendable approval message and
8922
- // the worker verifies and commits it. Nothing is submitted to any chain.
8923
- if (typeof curTx.serializedTransaction !== 'string' || typeof curTx.poofnetIntentId !== 'string') {
8924
- throw new Error('Unsupported 202 transaction shape: a poofnet write needs serializedTransaction and poofnetIntentId');
8913
+ // Until something is sent, every failure below is definitively not applied.
8914
+ let sending = false;
8915
+ try {
8916
+ const { transactions, offchainTransaction } = setResponse.data;
8917
+ // Handle offchain transaction flow (for 'offchain' protocol apps)
8918
+ if (offchainTransaction && Array.isArray(transactions) && transactions.length > 0) {
8919
+ throw new Error('Ambiguous write response mixes offchain and onchain transaction targets');
8925
8920
  }
8926
- const { signPoofnetTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-eTiLgak2.js'); });
8927
- const signedTransaction = await signPoofnetTransaction(curTx.serializedTransaction, authProvider, options, buildExpectedWriteIntent(documents));
8928
- // Signing-only, exactly as on a real chain: signed and NOT committed.
8929
- if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
8930
- return {
8931
- status: 'signed', appId, requestedDocuments: buildRequestedDocuments(documents),
8932
- chain: 'realtime_offchain', transactionId: null, signedTransaction,
8933
- poofnetIntentId: curTx.poofnetIntentId,
8934
- };
8921
+ if (offchainTransaction) {
8922
+ // Offchain signing is retired: no server mints this envelope any more
8923
+ // (the realtime worker commits poofnet writes directly). Fail loudly
8924
+ // rather than silently ignoring a payload the caller was asked to sign.
8925
+ throw new Error('Unsupported 202 transaction shape: offchain signing is retired');
8935
8926
  }
8936
- const commitResponse = await makeApiRequest('PUT', 'items/signed', { intentId: curTx.poofnetIntentId, signedTransaction }, options === null || options === void 0 ? void 0 : options._overrides);
8937
- if (commitResponse.status !== 200) {
8938
- throw new Error(`Poofnet signed write was not committed (status ${commitResponse.status})`);
8927
+ // Handle Solana on-chain transaction flow
8928
+ if (!Array.isArray(transactions) || transactions.length !== 1) {
8929
+ throw new Error(`Expected exactly one on-chain transaction, received ${Array.isArray(transactions) ? transactions.length : 0}`);
8939
8930
  }
8940
- return directCommitResult(commitResponse, documents);
8941
- }
8942
- if (curTx.serializedTransaction) {
8943
- if (!SOLANA_CHAIN_TAGS.has(workerChain)) {
8944
- throw new Error(`The write response reported execution chain "${workerChain}" for a Solana transaction. ` +
8931
+ const curTx = transactions[0];
8932
+ // The EVM onchain data plane is retired: no worker mints an EVM 202 any
8933
+ // more. A payload that still carries one is an unexpected response and
8934
+ // fails loudly BEFORE anything is signed - never silently ignored.
8935
+ if ((curTx === null || curTx === void 0 ? void 0 : curTx.evmTransaction) != null) {
8936
+ throw new Error('Unsupported 202 transaction shape: the write response carried an evmTransaction, ' +
8937
+ 'but EVM onchain writes are no longer supported. Nothing was signed or submitted.');
8938
+ }
8939
+ const hasSerializedSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.serializedTransaction) != null;
8940
+ const hasBuiltSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.transactionArgs) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.remainingAccounts) != null ||
8941
+ (curTx === null || curTx === void 0 ? void 0 : curTx.idl) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.txData) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.preInstructions) != null;
8942
+ if (hasSerializedSolanaTarget && hasBuiltSolanaTarget) {
8943
+ throw new Error('Ambiguous on-chain write response mixes Solana transaction target formats');
8944
+ }
8945
+ // All requested paths (writes AND deletes), in request order. Used for the
8946
+ // post-confirm mirror read-back, which correlates every requested path.
8947
+ // The pre-sign validation derives its own full expected intent from
8948
+ // `documents` (see below) and needs nothing from this list.
8949
+ const requestedPaths = documents.map(d => d.destinationPath);
8950
+ const requestedDocuments = buildRequestedDocuments(documents);
8951
+ const appId = config.appId;
8952
+ // The lane comes verbatim from the worker, and is read BEFORE anything is
8953
+ // signed or submitted: a missing tag fails closed while nothing has been
8954
+ // broadcast, instead of stranding a landed transaction with no receipt.
8955
+ const workerChain = readWorkerChainTag((_k = setResponse.data) === null || _k === void 0 ? void 0 : _k.receipt, setResponse.data, curTx);
8956
+ if (!workerChain) {
8957
+ throw new Error('The write response carried no execution-chain tag, so this receipt could not state where the write ran. ' +
8945
8958
  'Nothing was signed or submitted.');
8946
8959
  }
8947
- const solanaChain = workerChain;
8948
- const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-eTiLgak2.js'); });
8949
- // OA-0001: derive the expected intent from OUR OWN request (the one
8950
- // ground truth a tampered builder response cannot change) so the
8951
- // validator can reject any transaction whose operation semantics
8952
- // diverge from it BEFORE the wallet is asked to sign.
8953
- const expectedIntent = buildExpectedWriteIntent(documents);
8954
- const solanaOutcome = await handlePreBuiltTransaction(curTx, authProvider, options, expectedIntent);
8955
- // `shouldSubmitTx: false` signed WITHOUT sending — no signature exists,
8956
- // and there is nothing to mirror, so no synchronization is attempted.
8957
- if (solanaOutcome.outcome === 'signed') {
8958
- return {
8959
- status: 'signed', appId, requestedDocuments, chain: solanaChain,
8960
- transactionId: null,
8961
- signedTransaction: solanaOutcome.signedTransaction,
8962
- fence: solanaOutcome.fence,
8963
- };
8960
+ if (workerChain === 'realtime_offchain') {
8961
+ // Poofnet: the wallet signs the worker's unsendable approval message and
8962
+ // the worker verifies and commits it. Nothing is submitted to any chain.
8963
+ if (typeof curTx.serializedTransaction !== 'string' || typeof curTx.poofnetIntentId !== 'string') {
8964
+ throw new Error('Unsupported 202 transaction shape: a poofnet write needs serializedTransaction and poofnetIntentId');
8965
+ }
8966
+ const { signPoofnetTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-CIiyUG3b.js'); });
8967
+ const signedTransaction = await signPoofnetTransaction(curTx.serializedTransaction, authProvider, options, buildExpectedWriteIntent(documents));
8968
+ // Signing-only, exactly as on a real chain: signed and NOT committed.
8969
+ if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
8970
+ return {
8971
+ status: 'signed', appId, requestedDocuments: buildRequestedDocuments(documents),
8972
+ chain: 'realtime_offchain', transactionId: null, signedTransaction,
8973
+ poofnetIntentId: curTx.poofnetIntentId,
8974
+ };
8975
+ }
8976
+ // The commit PUT is the one ambiguous step of this lane: a lost response
8977
+ // may follow a commit, so its failures stay untagged (read the path back).
8978
+ sending = true;
8979
+ const commitResponse = await makeApiRequest('PUT', 'items/signed', { intentId: curTx.poofnetIntentId, signedTransaction }, options === null || options === void 0 ? void 0 : options._overrides);
8980
+ if (commitResponse.status !== 200) {
8981
+ throw new Error(`Poofnet signed write was not committed (status ${commitResponse.status})`);
8982
+ }
8983
+ return directCommitResult(commitResponse, documents);
8964
8984
  }
8965
- if (solanaOutcome.outcome === 'submitted') {
8985
+ if (curTx.serializedTransaction) {
8986
+ if (!SOLANA_CHAIN_TAGS.has(workerChain)) {
8987
+ throw new Error(`The write response reported execution chain "${workerChain}" for a Solana transaction. ` +
8988
+ 'Nothing was signed or submitted.');
8989
+ }
8990
+ const solanaChain = workerChain;
8991
+ const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-CIiyUG3b.js'); });
8992
+ // OA-0001: derive the expected intent from OUR OWN request (the one
8993
+ // ground truth a tampered builder response cannot change) so the
8994
+ // validator can reject any transaction whose operation semantics
8995
+ // diverge from it BEFORE the wallet is asked to sign.
8996
+ const expectedIntent = buildExpectedWriteIntent(documents);
8997
+ const onBroadcast = options === null || options === void 0 ? void 0 : options.onBroadcast;
8998
+ const solanaOutcome = await handlePreBuiltTransaction(curTx, authProvider, options, expectedIntent, onBroadcast && (async (signed) => {
8999
+ await onBroadcast({
9000
+ status: 'submitted', appId, requestedDocuments, chain: solanaChain,
9001
+ transactionId: signed.transactionSignature,
9002
+ signedTransaction: signed.signedTransaction,
9003
+ fence: signed.fence,
9004
+ confirmation: { state: 'unknown', reason: 'about to broadcast' },
9005
+ });
9006
+ }));
9007
+ // Past this point the bytes may be on the network: nothing is tagged here.
9008
+ sending = true;
9009
+ // `shouldSubmitTx: false` signed WITHOUT sending — no signature exists,
9010
+ // and there is nothing to mirror, so no synchronization is attempted.
9011
+ if (solanaOutcome.outcome === 'signed') {
9012
+ return {
9013
+ status: 'signed', appId, requestedDocuments, chain: solanaChain,
9014
+ transactionId: null,
9015
+ signedTransaction: solanaOutcome.signedTransaction,
9016
+ fence: solanaOutcome.fence,
9017
+ };
9018
+ }
9019
+ if (solanaOutcome.outcome === 'submitted') {
9020
+ return {
9021
+ status: 'submitted', appId, requestedDocuments, chain: solanaChain,
9022
+ transactionId: solanaOutcome.transactionSignature,
9023
+ signedTransaction: solanaOutcome.signedTransaction,
9024
+ fence: solanaOutcome.fence,
9025
+ confirmation: { state: 'unknown', reason: solanaOutcome.reason },
9026
+ };
9027
+ }
9028
+ // Confirmed: the receipt is already complete, so the mirror read is a
9029
+ // best-effort enrichment reported in `mirrorSync`, never a rejection.
9030
+ const mirrorSync = await attemptMirrorSync(requestedPaths, options);
8966
9031
  return {
8967
- status: 'submitted', appId, requestedDocuments, chain: solanaChain,
9032
+ status: 'confirmed', appId, requestedDocuments, chain: solanaChain,
8968
9033
  transactionId: solanaOutcome.transactionSignature,
8969
9034
  signedTransaction: solanaOutcome.signedTransaction,
8970
9035
  fence: solanaOutcome.fence,
8971
- confirmation: { state: 'unknown', reason: solanaOutcome.reason },
9036
+ confirmationContextSlot: solanaOutcome.confirmationContextSlot,
9037
+ mirrorSync,
8972
9038
  };
8973
9039
  }
8974
- // Confirmed: the receipt is already complete, so the mirror read is a
8975
- // best-effort enrichment reported in `mirrorSync`, never a rejection.
8976
- const mirrorSync = await attemptMirrorSync(requestedPaths, options);
8977
- return {
8978
- status: 'confirmed', appId, requestedDocuments, chain: solanaChain,
8979
- transactionId: solanaOutcome.transactionSignature,
8980
- signedTransaction: solanaOutcome.signedTransaction,
8981
- fence: solanaOutcome.fence,
8982
- confirmationContextSlot: solanaOutcome.confirmationContextSlot,
8983
- mirrorSync,
8984
- };
9040
+ else {
9041
+ throw new Error('Unsupported 202 transaction shape: expected serializedTransaction');
9042
+ }
8985
9043
  }
8986
- else {
8987
- throw new Error('Unsupported 202 transaction shape: expected serializedTransaction');
9044
+ catch (error) {
9045
+ // The Solana lane tags its own broadcast and on-chain failures; anything
9046
+ // untagged from it, and every poofnet step before the commit PUT, failed
9047
+ // before a single byte was sent.
9048
+ throw sending ? error : markWriteNotApplied(error, 'before_broadcast');
8988
9049
  }
8989
9050
  }
8990
9051
  else if (setResponse.status === 200) {
@@ -9005,6 +9066,8 @@ async function setMany(many, options) {
9005
9066
  * of a result document (a delete returns the PREVIOUS document, so its stale
9006
9067
  * `_transaction_hash` would misreport the delete).
9007
9068
  */
9069
+ /** HTTP statuses on the write request that prove the server refused it before anything happened. */
9070
+ const DEFINITIVE_WRITE_REFUSAL_STATUSES = new Set([400, 401, 402, 403, 404, 409, 422, 429]);
9008
9071
  function directCommitResult(response, documents) {
9009
9072
  var _a;
9010
9073
  const body = (response.data && typeof response.data === 'object' && !Array.isArray(response.data))
@@ -9273,48 +9336,6 @@ var operations = /*#__PURE__*/Object.freeze({
9273
9336
  syncItems: syncItems
9274
9337
  });
9275
9338
 
9276
- /** The highest Solana transaction version this SDK reads back (SIMD-0385 v1). */
9277
- const SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION = 1;
9278
- /**
9279
- * getTransaction that survives a SIMD-0385 v1 response. @solana/web3.js 1.98.x
9280
- * validates `version` against `0 | 'legacy'` and THROWS on 1 even when the request
9281
- * raised the ceiling, so a real `Connection` (which exposes `rpcEndpoint`) is read
9282
- * as raw JSON-RPC instead. An injected double without an endpoint goes through its
9283
- * own `getTransaction` with the ceiling raised. Returns the RPC `result` (null when
9284
- * the signature is unknown); RPC and transport failures throw so callers keep
9285
- * treating a failed lookup as inconclusive rather than as absence.
9286
- */
9287
- async function getTransactionJson(rpc, signature, commitment = 'confirmed') {
9288
- var _a;
9289
- const endpoint = typeof (rpc === null || rpc === void 0 ? void 0 : rpc.rpcEndpoint) === 'string' ? rpc.rpcEndpoint.trim() : '';
9290
- if (!endpoint) {
9291
- return rpc.getTransaction(signature, {
9292
- maxSupportedTransactionVersion: SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION,
9293
- commitment,
9294
- });
9295
- }
9296
- const response = await fetch(endpoint, {
9297
- method: 'POST',
9298
- headers: { 'content-type': 'application/json' },
9299
- body: JSON.stringify({
9300
- jsonrpc: '2.0',
9301
- id: 1,
9302
- method: 'getTransaction',
9303
- params: [signature, {
9304
- commitment,
9305
- encoding: 'json',
9306
- maxSupportedTransactionVersion: SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION,
9307
- }],
9308
- }),
9309
- });
9310
- if (!response.ok)
9311
- throw new Error(`getTransaction http ${response.status}`);
9312
- const body = await response.json();
9313
- if (body === null || body === void 0 ? void 0 : body.error)
9314
- throw new Error(`getTransaction rpc error ${body.error.code}: ${body.error.message}`);
9315
- return (_a = body === null || body === void 0 ? void 0 : body.result) !== null && _a !== void 0 ? _a : null;
9316
- }
9317
-
9318
9339
  const DEFAULT_TIMEOUT_MS = 60000;
9319
9340
  const DEFAULT_MAX_ATTEMPTS$1 = 20;
9320
9341
  const DEFAULT_POLL_INTERVAL_MS = 3000;
@@ -9388,20 +9409,50 @@ async function resolveSolanaRpc(config, options) {
9388
9409
  return new Connection(rpcUrl, 'confirmed');
9389
9410
  }
9390
9411
  /**
9391
- * Solana reconciliation NEVER broadcasts. It reaches `expired_not_landed` only
9392
- * after the fence is observed expired AND history successfully proves the
9393
- * signature is absent; a failed or inconclusive lookup stays `submitted`.
9412
+ * Whether a signature status is final enough to decide on. `processed` can still belong to a fork
9413
+ * that is dropped, so only `confirmed` or `finalized` settle a write; an RPC that omits
9414
+ * `confirmationStatus` reports a rooted transaction as `confirmations: null`.
9415
+ */
9416
+ function settledStatus(status) {
9417
+ if (!status)
9418
+ return false;
9419
+ if (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized')
9420
+ return true;
9421
+ return status.confirmationStatus == null && status.confirmations === null;
9422
+ }
9423
+ /**
9424
+ * Solana reconciliation NEVER broadcasts. It settles on a `confirmed` or `finalized` status only,
9425
+ * and reaches `expired_not_landed` only from the chain's own blockhash expiry: the FINALIZED block
9426
+ * height is past the fence, so the network can never accept these bytes again, and a
9427
+ * history-searching status lookup from a node that has seen that finalized slot finds no record.
9428
+ * Anything short of that - a `processed` status, a lookup that fails, a node behind the expiry
9429
+ * point - stays `submitted`.
9394
9430
  */
9395
9431
  async function reconcileSolana(receipt, config, options) {
9396
9432
  var _a, _b, _c, _d;
9397
9433
  const rpc = await resolveSolanaRpc(config, options);
9434
+ if (typeof rpc.getSignatureStatuses !== 'function') {
9435
+ return stillSubmitted(receipt, 'the configured Solana RPC cannot report signature statuses');
9436
+ }
9437
+ // Expiry is read BEFORE the status, so an absence observed afterwards is an absence after the
9438
+ // fence closed for good. A finalized height past lastValidBlockHeight is final: the blockhash
9439
+ // can never become valid again.
9440
+ let finalizedHeight;
9441
+ let finalizedSlot;
9442
+ try {
9443
+ if (typeof rpc.getBlockHeight === 'function')
9444
+ finalizedHeight = await rpc.getBlockHeight('finalized');
9445
+ if (typeof rpc.getSlot === 'function')
9446
+ finalizedSlot = await rpc.getSlot('finalized');
9447
+ }
9448
+ catch (error) {
9449
+ return stillSubmitted(receipt, `finalized height lookup failed: ${errorText(error)}`);
9450
+ }
9451
+ const expired = typeof finalizedHeight === 'number' && finalizedHeight > receipt.fence.lastValidBlockHeight;
9398
9452
  let statusValue = null;
9399
9453
  let contextSlot;
9400
9454
  try {
9401
- if (typeof rpc.getSignatureStatuses !== 'function') {
9402
- return stillSubmitted(receipt, 'the configured Solana RPC cannot report signature statuses');
9403
- }
9404
- const statuses = await rpc.getSignatureStatuses([receipt.transactionId]);
9455
+ const statuses = await rpc.getSignatureStatuses([receipt.transactionId], { searchTransactionHistory: true });
9405
9456
  statusValue = (_b = (_a = statuses === null || statuses === void 0 ? void 0 : statuses.value) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null;
9406
9457
  contextSlot = (_c = statuses === null || statuses === void 0 ? void 0 : statuses.context) === null || _c === void 0 ? void 0 : _c.slot;
9407
9458
  }
@@ -9409,9 +9460,12 @@ async function reconcileSolana(receipt, config, options) {
9409
9460
  return stillSubmitted(receipt, `signature status lookup failed: ${errorText(error)}`);
9410
9461
  }
9411
9462
  if (statusValue) {
9463
+ if (!settledStatus(statusValue)) {
9464
+ return stillSubmitted(receipt, `seen at ${String((_d = statusValue.confirmationStatus) !== null && _d !== void 0 ? _d : 'an unconfirmed')} commitment; waiting for confirmed`);
9465
+ }
9412
9466
  // Landed. A landed-but-failed transaction is a genuine failure, not `submitted`.
9413
9467
  if (statusValue.err) {
9414
- throw new Error(`Transaction failed: ${JSON.stringify(statusValue.err)}`);
9468
+ throw markWriteNotApplied(new Error(`Transaction failed: ${JSON.stringify(statusValue.err)}`), 'failed_onchain');
9415
9469
  }
9416
9470
  const confirmed = {
9417
9471
  status: 'confirmed',
@@ -9421,50 +9475,21 @@ async function reconcileSolana(receipt, config, options) {
9421
9475
  transactionId: receipt.transactionId,
9422
9476
  signedTransaction: receipt.signedTransaction,
9423
9477
  fence: receipt.fence,
9424
- confirmationContextSlot: contextSlot !== null && contextSlot !== void 0 ? contextSlot : 0,
9478
+ confirmationContextSlot: typeof statusValue.slot === 'number' ? statusValue.slot : (contextSlot !== null && contextSlot !== void 0 ? contextSlot : 0),
9425
9479
  mirrorSync: await mirrorFor(receipt, options),
9426
9480
  };
9427
9481
  return confirmed;
9428
9482
  }
9429
- // Absent from the status window. Expiry has to be OBSERVED before absence can
9430
- // mean anything: before the fence expires the transaction may still land.
9431
- let blockHeight;
9432
- try {
9433
- blockHeight = typeof rpc.getBlockHeight === 'function' ? await rpc.getBlockHeight('confirmed') : undefined;
9483
+ if (typeof finalizedHeight !== 'number') {
9484
+ return stillSubmitted(receipt, 'expiry could not be observed: the RPC reported no finalized block height');
9434
9485
  }
9435
- catch (error) {
9436
- return stillSubmitted(receipt, `block height lookup failed: ${errorText(error)}`);
9437
- }
9438
- if (typeof blockHeight !== 'number') {
9439
- return stillSubmitted(receipt, 'expiry could not be observed: the RPC reported no block height');
9440
- }
9441
- if (blockHeight <= receipt.fence.lastValidBlockHeight) {
9442
- return stillSubmitted(receipt, `not yet expired: block height ${blockHeight} is within the fence (lastValidBlockHeight ${receipt.fence.lastValidBlockHeight})`);
9443
- }
9444
- // Expired. Prove absence from history before declaring the write retry-safe.
9445
- let history;
9446
- try {
9447
- history = await getTransactionJson(rpc, receipt.transactionId, 'confirmed');
9486
+ if (!expired) {
9487
+ return stillSubmitted(receipt, `not yet expired: finalized block height ${finalizedHeight} is within the fence (lastValidBlockHeight ${receipt.fence.lastValidBlockHeight})`);
9448
9488
  }
9449
- catch (error) {
9450
- return stillSubmitted(receipt, `history lookup failed after expiry: ${errorText(error)}`);
9451
- }
9452
- if (history) {
9453
- if ((_d = history === null || history === void 0 ? void 0 : history.meta) === null || _d === void 0 ? void 0 : _d.err) {
9454
- throw new Error(`Transaction failed: ${JSON.stringify(history.meta.err)}`);
9455
- }
9456
- const confirmed = {
9457
- status: 'confirmed',
9458
- appId: receipt.appId,
9459
- requestedDocuments: receipt.requestedDocuments,
9460
- chain: receipt.chain,
9461
- transactionId: receipt.transactionId,
9462
- signedTransaction: receipt.signedTransaction,
9463
- fence: receipt.fence,
9464
- confirmationContextSlot: typeof history.slot === 'number' ? history.slot : (contextSlot !== null && contextSlot !== void 0 ? contextSlot : 0),
9465
- mirrorSync: await mirrorFor(receipt, options),
9466
- };
9467
- return confirmed;
9489
+ // A load-balanced RPC may answer the status from a node behind the one that reported the
9490
+ // finalized height. Its absence proves nothing until that node has seen the finalized slot.
9491
+ if (typeof finalizedSlot !== 'number' || typeof contextSlot !== 'number' || contextSlot < finalizedSlot) {
9492
+ return stillSubmitted(receipt, `expired, but the status node (slot ${contextSlot !== null && contextSlot !== void 0 ? contextSlot : 'unknown'}) has not reached the finalized slot ${finalizedSlot !== null && finalizedSlot !== void 0 ? finalizedSlot : 'unknown'}`);
9468
9493
  }
9469
9494
  return {
9470
9495
  status: 'expired_not_landed',
@@ -10727,7 +10752,7 @@ function toMillis(seconds) {
10727
10752
 
10728
10753
  /** SIWS message creation is async already, so its onchain module can be lazy. */
10729
10754
  async function genSolanaMessage(address, nonce) {
10730
- const implementation = await Promise.resolve().then(function () { return require('./solana-message-Bf2q_JO1.js'); });
10755
+ const implementation = await Promise.resolve().then(function () { return require('./solana-message-DQVrh3NC.js'); });
10731
10756
  return implementation.genSolanaMessage(address, nonce);
10732
10757
  }
10733
10758
 
@@ -11289,7 +11314,6 @@ exports.LiveIntentError = LiveIntentError;
11289
11314
  exports.MIN_QUERY_POLL_INTERVAL_MS = MIN_QUERY_POLL_INTERVAL_MS;
11290
11315
  exports.RATE_LIMIT_EVENT_NAME = RATE_LIMIT_EVENT_NAME;
11291
11316
  exports.ReactNativeSessionManager = ReactNativeSessionManager;
11292
- exports.SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION = SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION;
11293
11317
  exports.SOLANA_SIGN_IN_STATEMENT = SOLANA_SIGN_IN_STATEMENT;
11294
11318
  exports.ScopedClientReadOnlyError = ScopedClientReadOnlyError;
11295
11319
  exports.ServerSessionManager = ServerSessionManager;
@@ -11334,7 +11358,6 @@ exports.getFreshAuthToken = getFreshAuthToken;
11334
11358
  exports.getIdToken = getIdToken;
11335
11359
  exports.getMany = getMany;
11336
11360
  exports.getSessionGeneration = getSessionGeneration;
11337
- exports.getTransactionJson = getTransactionJson;
11338
11361
  exports.getWebhookKeysUrl = getWebhookKeysUrl;
11339
11362
  exports.hasActiveConnection = hasActiveConnection;
11340
11363
  exports.increment = increment;
@@ -11343,7 +11366,9 @@ exports.intent = intent;
11343
11366
  exports.invoke = invoke;
11344
11367
  exports.isAuthExpiredError = isAuthExpiredError;
11345
11368
  exports.isEffectResult = isEffectResult;
11369
+ exports.isWriteNotApplied = isWriteNotApplied;
11346
11370
  exports.live = live;
11371
+ exports.markWriteNotApplied = markWriteNotApplied;
11347
11372
  exports.normalizeEvmAddress = normalizeEvmAddress;
11348
11373
  exports.now = now;
11349
11374
  exports.onSessionCleared = onSessionCleared;
@@ -11374,4 +11399,5 @@ exports.toMillis = toMillis;
11374
11399
  exports.toSeconds = toSeconds;
11375
11400
  exports.waitForSetResult = waitForSetResult;
11376
11401
  exports.withEffects = withEffects;
11377
- //# sourceMappingURL=index-62gjQH30.js.map
11402
+ exports.writeNotAppliedPhase = writeNotAppliedPhase;
11403
+ //# sourceMappingURL=index-BawH-9Rq.js.map