@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
@@ -4188,7 +4188,7 @@ class ServerSessionManager {
4188
4188
  const sessionConfig = (_a = this.sessionConfig) !== null && _a !== void 0 ? _a : undefined;
4189
4189
  /* fetch nonce from auth API (bound to the snapshotted app+issuer) */
4190
4190
  const nonce = await genAuthNonce(sessionConfig);
4191
- const { genSolanaMessage } = await import('./solana-message-Deru1tF1.mjs');
4191
+ const { genSolanaMessage } = await import('./solana-message-BBKdydqt.mjs');
4192
4192
  const message = await genSolanaMessage(address, nonce, sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.appId);
4193
4193
  /* sign the message */
4194
4194
  const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
@@ -7467,7 +7467,7 @@ async function doReconnectWithNewAuth() {
7467
7467
  console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
7468
7468
  }
7469
7469
  try {
7470
- const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-C9AgPg7U.mjs');
7470
+ const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-iXGbzhjG.mjs');
7471
7471
  await reconnectRealtimeStoreWithNewAuth();
7472
7472
  }
7473
7473
  catch (error) {
@@ -7695,6 +7695,37 @@ async function wsIntentReliable(appId, roomRoutePath, intent, isServer) {
7695
7695
  });
7696
7696
  }
7697
7697
 
7698
+ const PHASES = new Set(['before_broadcast', 'broadcast_rejected', 'failed_onchain']);
7699
+ /** Tag `error` as definitively not applied. An already-tagged error keeps its (earlier, more precise) phase. */
7700
+ function markWriteNotApplied(error, phase) {
7701
+ var _a;
7702
+ if (writeNotAppliedPhase(error) !== null)
7703
+ return error;
7704
+ const target = error !== null && typeof error === 'object'
7705
+ ? error
7706
+ : new Error(String(error));
7707
+ try {
7708
+ Object.assign(target, { writeOutcome: 'not_applied', writePhase: phase });
7709
+ }
7710
+ catch (_b) {
7711
+ // A frozen throwable cannot carry the tag; wrap it so the verdict is not lost.
7712
+ const wrapped = new Error(String((_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error));
7713
+ return Object.assign(wrapped, { cause: error, writeOutcome: 'not_applied', writePhase: phase });
7714
+ }
7715
+ return target;
7716
+ }
7717
+ /** The phase a write stopped at when it was definitively not applied, else `null` (ambiguous or not a write error). */
7718
+ function writeNotAppliedPhase(error) {
7719
+ if (error === null || typeof error !== 'object')
7720
+ return null;
7721
+ const outcome = error.writeOutcome;
7722
+ const phase = error.writePhase;
7723
+ return outcome === 'not_applied' && typeof phase === 'string' && PHASES.has(phase) ? phase : null;
7724
+ }
7725
+ function isWriteNotApplied(error) {
7726
+ return writeNotAppliedPhase(error) !== null;
7727
+ }
7728
+
7698
7729
  /**
7699
7730
  * Thrown when a user's wallet doesn't have enough SOL to cover the transaction.
7700
7731
  * Apps can catch this to trigger an onramp/funding flow.
@@ -8860,7 +8891,13 @@ async function setMany(many, options) {
8860
8891
  if ((error === null || error === void 0 ? void 0 : error.statusCode) === 402 && (error === null || error === void 0 ? void 0 : error.error) === 'INSUFFICIENT_BALANCE') {
8861
8892
  const deficitLamports = Number((_e = error.deficitLamports) !== null && _e !== void 0 ? _e : 0);
8862
8893
  const deficitSol = Number((_f = error.deficitSol) !== null && _f !== void 0 ? _f : deficitLamports / 1000000000);
8863
- 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);
8894
+ 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');
8895
+ }
8896
+ // The server answered and refused the write (a rule or invariant denial, a bad request, no
8897
+ // session, a rate limit): nothing was built, signed or committed. A network failure, a 5xx,
8898
+ // a 408 or a 499 may follow a commit the response never reported, so those stay untagged.
8899
+ if (DEFINITIVE_WRITE_REFUSAL_STATUSES.has(Number(error === null || error === void 0 ? void 0 : error.statusCode))) {
8900
+ throw markWriteNotApplied(error, 'before_broadcast');
8864
8901
  }
8865
8902
  throw error;
8866
8903
  }
@@ -8871,118 +8908,142 @@ async function setMany(many, options) {
8871
8908
  if (setResponse.status === 202) {
8872
8909
  // This means that the document needs to be set on-chain (or signed for offchain),
8873
8910
  // the API did nothing and gave us the required info to run this transaction.
8874
- const { transactions, offchainTransaction } = setResponse.data;
8875
- // Handle offchain transaction flow (for 'offchain' protocol apps)
8876
- if (offchainTransaction && Array.isArray(transactions) && transactions.length > 0) {
8877
- throw new Error('Ambiguous write response mixes offchain and onchain transaction targets');
8878
- }
8879
- if (offchainTransaction) {
8880
- // Offchain signing is retired: no server mints this envelope any more
8881
- // (the realtime worker commits poofnet writes directly). Fail loudly
8882
- // rather than silently ignoring a payload the caller was asked to sign.
8883
- throw new Error('Unsupported 202 transaction shape: offchain signing is retired');
8884
- }
8885
- // Handle Solana on-chain transaction flow
8886
- if (!Array.isArray(transactions) || transactions.length !== 1) {
8887
- throw new Error(`Expected exactly one on-chain transaction, received ${Array.isArray(transactions) ? transactions.length : 0}`);
8888
- }
8889
- const curTx = transactions[0];
8890
- // The EVM onchain data plane is retired: no worker mints an EVM 202 any
8891
- // more. A payload that still carries one is an unexpected response and
8892
- // fails loudly BEFORE anything is signed - never silently ignored.
8893
- if ((curTx === null || curTx === void 0 ? void 0 : curTx.evmTransaction) != null) {
8894
- throw new Error('Unsupported 202 transaction shape: the write response carried an evmTransaction, ' +
8895
- 'but EVM onchain writes are no longer supported. Nothing was signed or submitted.');
8896
- }
8897
- const hasSerializedSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.serializedTransaction) != null;
8898
- const hasBuiltSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.transactionArgs) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.remainingAccounts) != null ||
8899
- (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;
8900
- if (hasSerializedSolanaTarget && hasBuiltSolanaTarget) {
8901
- throw new Error('Ambiguous on-chain write response mixes Solana transaction target formats');
8902
- }
8903
- // All requested paths (writes AND deletes), in request order. Used for the
8904
- // post-confirm mirror read-back, which correlates every requested path.
8905
- // The pre-sign validation derives its own full expected intent from
8906
- // `documents` (see below) and needs nothing from this list.
8907
- const requestedPaths = documents.map(d => d.destinationPath);
8908
- const requestedDocuments = buildRequestedDocuments(documents);
8909
- const appId = config.appId;
8910
- // The lane comes verbatim from the worker, and is read BEFORE anything is
8911
- // signed or submitted: a missing tag fails closed while nothing has been
8912
- // broadcast, instead of stranding a landed transaction with no receipt.
8913
- const workerChain = readWorkerChainTag((_k = setResponse.data) === null || _k === void 0 ? void 0 : _k.receipt, setResponse.data, curTx);
8914
- if (!workerChain) {
8915
- throw new Error('The write response carried no execution-chain tag, so this receipt could not state where the write ran. ' +
8916
- 'Nothing was signed or submitted.');
8917
- }
8918
- if (workerChain === 'realtime_offchain') {
8919
- // Poofnet: the wallet signs the worker's unsendable approval message and
8920
- // the worker verifies and commits it. Nothing is submitted to any chain.
8921
- if (typeof curTx.serializedTransaction !== 'string' || typeof curTx.poofnetIntentId !== 'string') {
8922
- throw new Error('Unsupported 202 transaction shape: a poofnet write needs serializedTransaction and poofnetIntentId');
8911
+ // Until something is sent, every failure below is definitively not applied.
8912
+ let sending = false;
8913
+ try {
8914
+ const { transactions, offchainTransaction } = setResponse.data;
8915
+ // Handle offchain transaction flow (for 'offchain' protocol apps)
8916
+ if (offchainTransaction && Array.isArray(transactions) && transactions.length > 0) {
8917
+ throw new Error('Ambiguous write response mixes offchain and onchain transaction targets');
8923
8918
  }
8924
- const { signPoofnetTransaction, buildExpectedWriteIntent } = await import('./solana-operations-Cm19J14R.mjs');
8925
- const signedTransaction = await signPoofnetTransaction(curTx.serializedTransaction, authProvider, options, buildExpectedWriteIntent(documents));
8926
- // Signing-only, exactly as on a real chain: signed and NOT committed.
8927
- if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
8928
- return {
8929
- status: 'signed', appId, requestedDocuments: buildRequestedDocuments(documents),
8930
- chain: 'realtime_offchain', transactionId: null, signedTransaction,
8931
- poofnetIntentId: curTx.poofnetIntentId,
8932
- };
8919
+ if (offchainTransaction) {
8920
+ // Offchain signing is retired: no server mints this envelope any more
8921
+ // (the realtime worker commits poofnet writes directly). Fail loudly
8922
+ // rather than silently ignoring a payload the caller was asked to sign.
8923
+ throw new Error('Unsupported 202 transaction shape: offchain signing is retired');
8933
8924
  }
8934
- const commitResponse = await makeApiRequest('PUT', 'items/signed', { intentId: curTx.poofnetIntentId, signedTransaction }, options === null || options === void 0 ? void 0 : options._overrides);
8935
- if (commitResponse.status !== 200) {
8936
- throw new Error(`Poofnet signed write was not committed (status ${commitResponse.status})`);
8925
+ // Handle Solana on-chain transaction flow
8926
+ if (!Array.isArray(transactions) || transactions.length !== 1) {
8927
+ throw new Error(`Expected exactly one on-chain transaction, received ${Array.isArray(transactions) ? transactions.length : 0}`);
8937
8928
  }
8938
- return directCommitResult(commitResponse, documents);
8939
- }
8940
- if (curTx.serializedTransaction) {
8941
- if (!SOLANA_CHAIN_TAGS.has(workerChain)) {
8942
- throw new Error(`The write response reported execution chain "${workerChain}" for a Solana transaction. ` +
8929
+ const curTx = transactions[0];
8930
+ // The EVM onchain data plane is retired: no worker mints an EVM 202 any
8931
+ // more. A payload that still carries one is an unexpected response and
8932
+ // fails loudly BEFORE anything is signed - never silently ignored.
8933
+ if ((curTx === null || curTx === void 0 ? void 0 : curTx.evmTransaction) != null) {
8934
+ throw new Error('Unsupported 202 transaction shape: the write response carried an evmTransaction, ' +
8935
+ 'but EVM onchain writes are no longer supported. Nothing was signed or submitted.');
8936
+ }
8937
+ const hasSerializedSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.serializedTransaction) != null;
8938
+ const hasBuiltSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.transactionArgs) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.remainingAccounts) != null ||
8939
+ (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;
8940
+ if (hasSerializedSolanaTarget && hasBuiltSolanaTarget) {
8941
+ throw new Error('Ambiguous on-chain write response mixes Solana transaction target formats');
8942
+ }
8943
+ // All requested paths (writes AND deletes), in request order. Used for the
8944
+ // post-confirm mirror read-back, which correlates every requested path.
8945
+ // The pre-sign validation derives its own full expected intent from
8946
+ // `documents` (see below) and needs nothing from this list.
8947
+ const requestedPaths = documents.map(d => d.destinationPath);
8948
+ const requestedDocuments = buildRequestedDocuments(documents);
8949
+ const appId = config.appId;
8950
+ // The lane comes verbatim from the worker, and is read BEFORE anything is
8951
+ // signed or submitted: a missing tag fails closed while nothing has been
8952
+ // broadcast, instead of stranding a landed transaction with no receipt.
8953
+ const workerChain = readWorkerChainTag((_k = setResponse.data) === null || _k === void 0 ? void 0 : _k.receipt, setResponse.data, curTx);
8954
+ if (!workerChain) {
8955
+ throw new Error('The write response carried no execution-chain tag, so this receipt could not state where the write ran. ' +
8943
8956
  'Nothing was signed or submitted.');
8944
8957
  }
8945
- const solanaChain = workerChain;
8946
- const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await import('./solana-operations-Cm19J14R.mjs');
8947
- // OA-0001: derive the expected intent from OUR OWN request (the one
8948
- // ground truth a tampered builder response cannot change) so the
8949
- // validator can reject any transaction whose operation semantics
8950
- // diverge from it BEFORE the wallet is asked to sign.
8951
- const expectedIntent = buildExpectedWriteIntent(documents);
8952
- const solanaOutcome = await handlePreBuiltTransaction(curTx, authProvider, options, expectedIntent);
8953
- // `shouldSubmitTx: false` signed WITHOUT sending — no signature exists,
8954
- // and there is nothing to mirror, so no synchronization is attempted.
8955
- if (solanaOutcome.outcome === 'signed') {
8956
- return {
8957
- status: 'signed', appId, requestedDocuments, chain: solanaChain,
8958
- transactionId: null,
8959
- signedTransaction: solanaOutcome.signedTransaction,
8960
- fence: solanaOutcome.fence,
8961
- };
8958
+ if (workerChain === 'realtime_offchain') {
8959
+ // Poofnet: the wallet signs the worker's unsendable approval message and
8960
+ // the worker verifies and commits it. Nothing is submitted to any chain.
8961
+ if (typeof curTx.serializedTransaction !== 'string' || typeof curTx.poofnetIntentId !== 'string') {
8962
+ throw new Error('Unsupported 202 transaction shape: a poofnet write needs serializedTransaction and poofnetIntentId');
8963
+ }
8964
+ const { signPoofnetTransaction, buildExpectedWriteIntent } = await import('./solana-operations-Dtow91W5.mjs');
8965
+ const signedTransaction = await signPoofnetTransaction(curTx.serializedTransaction, authProvider, options, buildExpectedWriteIntent(documents));
8966
+ // Signing-only, exactly as on a real chain: signed and NOT committed.
8967
+ if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
8968
+ return {
8969
+ status: 'signed', appId, requestedDocuments: buildRequestedDocuments(documents),
8970
+ chain: 'realtime_offchain', transactionId: null, signedTransaction,
8971
+ poofnetIntentId: curTx.poofnetIntentId,
8972
+ };
8973
+ }
8974
+ // The commit PUT is the one ambiguous step of this lane: a lost response
8975
+ // may follow a commit, so its failures stay untagged (read the path back).
8976
+ sending = true;
8977
+ const commitResponse = await makeApiRequest('PUT', 'items/signed', { intentId: curTx.poofnetIntentId, signedTransaction }, options === null || options === void 0 ? void 0 : options._overrides);
8978
+ if (commitResponse.status !== 200) {
8979
+ throw new Error(`Poofnet signed write was not committed (status ${commitResponse.status})`);
8980
+ }
8981
+ return directCommitResult(commitResponse, documents);
8962
8982
  }
8963
- if (solanaOutcome.outcome === 'submitted') {
8983
+ if (curTx.serializedTransaction) {
8984
+ if (!SOLANA_CHAIN_TAGS.has(workerChain)) {
8985
+ throw new Error(`The write response reported execution chain "${workerChain}" for a Solana transaction. ` +
8986
+ 'Nothing was signed or submitted.');
8987
+ }
8988
+ const solanaChain = workerChain;
8989
+ const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await import('./solana-operations-Dtow91W5.mjs');
8990
+ // OA-0001: derive the expected intent from OUR OWN request (the one
8991
+ // ground truth a tampered builder response cannot change) so the
8992
+ // validator can reject any transaction whose operation semantics
8993
+ // diverge from it BEFORE the wallet is asked to sign.
8994
+ const expectedIntent = buildExpectedWriteIntent(documents);
8995
+ const onBroadcast = options === null || options === void 0 ? void 0 : options.onBroadcast;
8996
+ const solanaOutcome = await handlePreBuiltTransaction(curTx, authProvider, options, expectedIntent, onBroadcast && (async (signed) => {
8997
+ await onBroadcast({
8998
+ status: 'submitted', appId, requestedDocuments, chain: solanaChain,
8999
+ transactionId: signed.transactionSignature,
9000
+ signedTransaction: signed.signedTransaction,
9001
+ fence: signed.fence,
9002
+ confirmation: { state: 'unknown', reason: 'about to broadcast' },
9003
+ });
9004
+ }));
9005
+ // Past this point the bytes may be on the network: nothing is tagged here.
9006
+ sending = true;
9007
+ // `shouldSubmitTx: false` signed WITHOUT sending — no signature exists,
9008
+ // and there is nothing to mirror, so no synchronization is attempted.
9009
+ if (solanaOutcome.outcome === 'signed') {
9010
+ return {
9011
+ status: 'signed', appId, requestedDocuments, chain: solanaChain,
9012
+ transactionId: null,
9013
+ signedTransaction: solanaOutcome.signedTransaction,
9014
+ fence: solanaOutcome.fence,
9015
+ };
9016
+ }
9017
+ if (solanaOutcome.outcome === 'submitted') {
9018
+ return {
9019
+ status: 'submitted', appId, requestedDocuments, chain: solanaChain,
9020
+ transactionId: solanaOutcome.transactionSignature,
9021
+ signedTransaction: solanaOutcome.signedTransaction,
9022
+ fence: solanaOutcome.fence,
9023
+ confirmation: { state: 'unknown', reason: solanaOutcome.reason },
9024
+ };
9025
+ }
9026
+ // Confirmed: the receipt is already complete, so the mirror read is a
9027
+ // best-effort enrichment reported in `mirrorSync`, never a rejection.
9028
+ const mirrorSync = await attemptMirrorSync(requestedPaths, options);
8964
9029
  return {
8965
- status: 'submitted', appId, requestedDocuments, chain: solanaChain,
9030
+ status: 'confirmed', appId, requestedDocuments, chain: solanaChain,
8966
9031
  transactionId: solanaOutcome.transactionSignature,
8967
9032
  signedTransaction: solanaOutcome.signedTransaction,
8968
9033
  fence: solanaOutcome.fence,
8969
- confirmation: { state: 'unknown', reason: solanaOutcome.reason },
9034
+ confirmationContextSlot: solanaOutcome.confirmationContextSlot,
9035
+ mirrorSync,
8970
9036
  };
8971
9037
  }
8972
- // Confirmed: the receipt is already complete, so the mirror read is a
8973
- // best-effort enrichment reported in `mirrorSync`, never a rejection.
8974
- const mirrorSync = await attemptMirrorSync(requestedPaths, options);
8975
- return {
8976
- status: 'confirmed', appId, requestedDocuments, chain: solanaChain,
8977
- transactionId: solanaOutcome.transactionSignature,
8978
- signedTransaction: solanaOutcome.signedTransaction,
8979
- fence: solanaOutcome.fence,
8980
- confirmationContextSlot: solanaOutcome.confirmationContextSlot,
8981
- mirrorSync,
8982
- };
9038
+ else {
9039
+ throw new Error('Unsupported 202 transaction shape: expected serializedTransaction');
9040
+ }
8983
9041
  }
8984
- else {
8985
- throw new Error('Unsupported 202 transaction shape: expected serializedTransaction');
9042
+ catch (error) {
9043
+ // The Solana lane tags its own broadcast and on-chain failures; anything
9044
+ // untagged from it, and every poofnet step before the commit PUT, failed
9045
+ // before a single byte was sent.
9046
+ throw sending ? error : markWriteNotApplied(error, 'before_broadcast');
8986
9047
  }
8987
9048
  }
8988
9049
  else if (setResponse.status === 200) {
@@ -9003,6 +9064,8 @@ async function setMany(many, options) {
9003
9064
  * of a result document (a delete returns the PREVIOUS document, so its stale
9004
9065
  * `_transaction_hash` would misreport the delete).
9005
9066
  */
9067
+ /** HTTP statuses on the write request that prove the server refused it before anything happened. */
9068
+ const DEFINITIVE_WRITE_REFUSAL_STATUSES = new Set([400, 401, 402, 403, 404, 409, 422, 429]);
9006
9069
  function directCommitResult(response, documents) {
9007
9070
  var _a;
9008
9071
  const body = (response.data && typeof response.data === 'object' && !Array.isArray(response.data))
@@ -9271,48 +9334,6 @@ var operations = /*#__PURE__*/Object.freeze({
9271
9334
  syncItems: syncItems
9272
9335
  });
9273
9336
 
9274
- /** The highest Solana transaction version this SDK reads back (SIMD-0385 v1). */
9275
- const SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION = 1;
9276
- /**
9277
- * getTransaction that survives a SIMD-0385 v1 response. @solana/web3.js 1.98.x
9278
- * validates `version` against `0 | 'legacy'` and THROWS on 1 even when the request
9279
- * raised the ceiling, so a real `Connection` (which exposes `rpcEndpoint`) is read
9280
- * as raw JSON-RPC instead. An injected double without an endpoint goes through its
9281
- * own `getTransaction` with the ceiling raised. Returns the RPC `result` (null when
9282
- * the signature is unknown); RPC and transport failures throw so callers keep
9283
- * treating a failed lookup as inconclusive rather than as absence.
9284
- */
9285
- async function getTransactionJson(rpc, signature, commitment = 'confirmed') {
9286
- var _a;
9287
- const endpoint = typeof (rpc === null || rpc === void 0 ? void 0 : rpc.rpcEndpoint) === 'string' ? rpc.rpcEndpoint.trim() : '';
9288
- if (!endpoint) {
9289
- return rpc.getTransaction(signature, {
9290
- maxSupportedTransactionVersion: SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION,
9291
- commitment,
9292
- });
9293
- }
9294
- const response = await fetch(endpoint, {
9295
- method: 'POST',
9296
- headers: { 'content-type': 'application/json' },
9297
- body: JSON.stringify({
9298
- jsonrpc: '2.0',
9299
- id: 1,
9300
- method: 'getTransaction',
9301
- params: [signature, {
9302
- commitment,
9303
- encoding: 'json',
9304
- maxSupportedTransactionVersion: SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION,
9305
- }],
9306
- }),
9307
- });
9308
- if (!response.ok)
9309
- throw new Error(`getTransaction http ${response.status}`);
9310
- const body = await response.json();
9311
- if (body === null || body === void 0 ? void 0 : body.error)
9312
- throw new Error(`getTransaction rpc error ${body.error.code}: ${body.error.message}`);
9313
- return (_a = body === null || body === void 0 ? void 0 : body.result) !== null && _a !== void 0 ? _a : null;
9314
- }
9315
-
9316
9337
  const DEFAULT_TIMEOUT_MS = 60000;
9317
9338
  const DEFAULT_MAX_ATTEMPTS$1 = 20;
9318
9339
  const DEFAULT_POLL_INTERVAL_MS = 3000;
@@ -9386,20 +9407,50 @@ async function resolveSolanaRpc(config, options) {
9386
9407
  return new Connection(rpcUrl, 'confirmed');
9387
9408
  }
9388
9409
  /**
9389
- * Solana reconciliation NEVER broadcasts. It reaches `expired_not_landed` only
9390
- * after the fence is observed expired AND history successfully proves the
9391
- * signature is absent; a failed or inconclusive lookup stays `submitted`.
9410
+ * Whether a signature status is final enough to decide on. `processed` can still belong to a fork
9411
+ * that is dropped, so only `confirmed` or `finalized` settle a write; an RPC that omits
9412
+ * `confirmationStatus` reports a rooted transaction as `confirmations: null`.
9413
+ */
9414
+ function settledStatus(status) {
9415
+ if (!status)
9416
+ return false;
9417
+ if (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized')
9418
+ return true;
9419
+ return status.confirmationStatus == null && status.confirmations === null;
9420
+ }
9421
+ /**
9422
+ * Solana reconciliation NEVER broadcasts. It settles on a `confirmed` or `finalized` status only,
9423
+ * and reaches `expired_not_landed` only from the chain's own blockhash expiry: the FINALIZED block
9424
+ * height is past the fence, so the network can never accept these bytes again, and a
9425
+ * history-searching status lookup from a node that has seen that finalized slot finds no record.
9426
+ * Anything short of that - a `processed` status, a lookup that fails, a node behind the expiry
9427
+ * point - stays `submitted`.
9392
9428
  */
9393
9429
  async function reconcileSolana(receipt, config, options) {
9394
9430
  var _a, _b, _c, _d;
9395
9431
  const rpc = await resolveSolanaRpc(config, options);
9432
+ if (typeof rpc.getSignatureStatuses !== 'function') {
9433
+ return stillSubmitted(receipt, 'the configured Solana RPC cannot report signature statuses');
9434
+ }
9435
+ // Expiry is read BEFORE the status, so an absence observed afterwards is an absence after the
9436
+ // fence closed for good. A finalized height past lastValidBlockHeight is final: the blockhash
9437
+ // can never become valid again.
9438
+ let finalizedHeight;
9439
+ let finalizedSlot;
9440
+ try {
9441
+ if (typeof rpc.getBlockHeight === 'function')
9442
+ finalizedHeight = await rpc.getBlockHeight('finalized');
9443
+ if (typeof rpc.getSlot === 'function')
9444
+ finalizedSlot = await rpc.getSlot('finalized');
9445
+ }
9446
+ catch (error) {
9447
+ return stillSubmitted(receipt, `finalized height lookup failed: ${errorText(error)}`);
9448
+ }
9449
+ const expired = typeof finalizedHeight === 'number' && finalizedHeight > receipt.fence.lastValidBlockHeight;
9396
9450
  let statusValue = null;
9397
9451
  let contextSlot;
9398
9452
  try {
9399
- if (typeof rpc.getSignatureStatuses !== 'function') {
9400
- return stillSubmitted(receipt, 'the configured Solana RPC cannot report signature statuses');
9401
- }
9402
- const statuses = await rpc.getSignatureStatuses([receipt.transactionId]);
9453
+ const statuses = await rpc.getSignatureStatuses([receipt.transactionId], { searchTransactionHistory: true });
9403
9454
  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;
9404
9455
  contextSlot = (_c = statuses === null || statuses === void 0 ? void 0 : statuses.context) === null || _c === void 0 ? void 0 : _c.slot;
9405
9456
  }
@@ -9407,9 +9458,12 @@ async function reconcileSolana(receipt, config, options) {
9407
9458
  return stillSubmitted(receipt, `signature status lookup failed: ${errorText(error)}`);
9408
9459
  }
9409
9460
  if (statusValue) {
9461
+ if (!settledStatus(statusValue)) {
9462
+ return stillSubmitted(receipt, `seen at ${String((_d = statusValue.confirmationStatus) !== null && _d !== void 0 ? _d : 'an unconfirmed')} commitment; waiting for confirmed`);
9463
+ }
9410
9464
  // Landed. A landed-but-failed transaction is a genuine failure, not `submitted`.
9411
9465
  if (statusValue.err) {
9412
- throw new Error(`Transaction failed: ${JSON.stringify(statusValue.err)}`);
9466
+ throw markWriteNotApplied(new Error(`Transaction failed: ${JSON.stringify(statusValue.err)}`), 'failed_onchain');
9413
9467
  }
9414
9468
  const confirmed = {
9415
9469
  status: 'confirmed',
@@ -9419,50 +9473,21 @@ async function reconcileSolana(receipt, config, options) {
9419
9473
  transactionId: receipt.transactionId,
9420
9474
  signedTransaction: receipt.signedTransaction,
9421
9475
  fence: receipt.fence,
9422
- confirmationContextSlot: contextSlot !== null && contextSlot !== void 0 ? contextSlot : 0,
9476
+ confirmationContextSlot: typeof statusValue.slot === 'number' ? statusValue.slot : (contextSlot !== null && contextSlot !== void 0 ? contextSlot : 0),
9423
9477
  mirrorSync: await mirrorFor(receipt, options),
9424
9478
  };
9425
9479
  return confirmed;
9426
9480
  }
9427
- // Absent from the status window. Expiry has to be OBSERVED before absence can
9428
- // mean anything: before the fence expires the transaction may still land.
9429
- let blockHeight;
9430
- try {
9431
- blockHeight = typeof rpc.getBlockHeight === 'function' ? await rpc.getBlockHeight('confirmed') : undefined;
9481
+ if (typeof finalizedHeight !== 'number') {
9482
+ return stillSubmitted(receipt, 'expiry could not be observed: the RPC reported no finalized block height');
9432
9483
  }
9433
- catch (error) {
9434
- return stillSubmitted(receipt, `block height lookup failed: ${errorText(error)}`);
9435
- }
9436
- if (typeof blockHeight !== 'number') {
9437
- return stillSubmitted(receipt, 'expiry could not be observed: the RPC reported no block height');
9438
- }
9439
- if (blockHeight <= receipt.fence.lastValidBlockHeight) {
9440
- return stillSubmitted(receipt, `not yet expired: block height ${blockHeight} is within the fence (lastValidBlockHeight ${receipt.fence.lastValidBlockHeight})`);
9441
- }
9442
- // Expired. Prove absence from history before declaring the write retry-safe.
9443
- let history;
9444
- try {
9445
- history = await getTransactionJson(rpc, receipt.transactionId, 'confirmed');
9484
+ if (!expired) {
9485
+ return stillSubmitted(receipt, `not yet expired: finalized block height ${finalizedHeight} is within the fence (lastValidBlockHeight ${receipt.fence.lastValidBlockHeight})`);
9446
9486
  }
9447
- catch (error) {
9448
- return stillSubmitted(receipt, `history lookup failed after expiry: ${errorText(error)}`);
9449
- }
9450
- if (history) {
9451
- if ((_d = history === null || history === void 0 ? void 0 : history.meta) === null || _d === void 0 ? void 0 : _d.err) {
9452
- throw new Error(`Transaction failed: ${JSON.stringify(history.meta.err)}`);
9453
- }
9454
- const confirmed = {
9455
- status: 'confirmed',
9456
- appId: receipt.appId,
9457
- requestedDocuments: receipt.requestedDocuments,
9458
- chain: receipt.chain,
9459
- transactionId: receipt.transactionId,
9460
- signedTransaction: receipt.signedTransaction,
9461
- fence: receipt.fence,
9462
- confirmationContextSlot: typeof history.slot === 'number' ? history.slot : (contextSlot !== null && contextSlot !== void 0 ? contextSlot : 0),
9463
- mirrorSync: await mirrorFor(receipt, options),
9464
- };
9465
- return confirmed;
9487
+ // A load-balanced RPC may answer the status from a node behind the one that reported the
9488
+ // finalized height. Its absence proves nothing until that node has seen the finalized slot.
9489
+ if (typeof finalizedSlot !== 'number' || typeof contextSlot !== 'number' || contextSlot < finalizedSlot) {
9490
+ 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'}`);
9466
9491
  }
9467
9492
  return {
9468
9493
  status: 'expired_not_landed',
@@ -10725,7 +10750,7 @@ function toMillis(seconds) {
10725
10750
 
10726
10751
  /** SIWS message creation is async already, so its onchain module can be lazy. */
10727
10752
  async function genSolanaMessage(address, nonce) {
10728
- const implementation = await import('./solana-message-Deru1tF1.mjs');
10753
+ const implementation = await import('./solana-message-BBKdydqt.mjs');
10729
10754
  return implementation.genSolanaMessage(address, nonce);
10730
10755
  }
10731
10756
 
@@ -11274,5 +11299,5 @@ function defineLiveModule(mod) {
11274
11299
  return mod;
11275
11300
  }
11276
11301
 
11277
- export { serverTimestamp as $, subscribe as A, closeAllSubscriptions as B, clearCache as C, getCachedData as D, reconnectWithNewAuth as E, createClient as F, ScopedClientReadOnlyError as G, exchangeAppToken as H, InsufficientBalanceError as I, clearAppTokenCache as J, createAppTokenAuthHeaders as K, AppTokenExchangeError as L, subscribeQuery as M, closeAllQuerySubscriptions as N, canonicalizeArgs as O, canonicalizeResult as P, DEFAULT_QUERY_POLL_INTERVAL_MS as Q, MIN_QUERY_POLL_INTERVAL_MS as R, SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION as S, BoundedDeclineError as T, deriveDecline as U, dispatchDeclineEvent as V, DECLINE_EVENT_NAME as W, dispatchRateLimitEvent as X, RATE_LIMIT_EVENT_NAME as Y, hasActiveConnection as Z, increment as _, getTransactionJson as a, now as a0, toSeconds as a1, toMillis as a2, getIdToken as a3, deriveUserIdentityFromIdToken as a4, getSessionGeneration as a5, WebSessionManager as a6, ReactNativeSessionManager as a7, getActiveSessionManager as a8, onSessionCleared as a9, intent as aA, status as aB, subscribeView as aC, LiveIntentError as aD, withEffects as aE, isEffectResult as aF, defineLiveModule as aG, EFFECT_INTENT_ADDRESS as aH, SessionEnvironmentError as aa, UNSUPPORTED_ENVIRONMENT_CODE as ab, AUTH_CHANGED_CODE as ac, AUTH_EXPIRED_CODE as ad, isAuthExpiredError as ae, getFreshAuthToken as af, ServerSessionManager as ag, createSessionWithSignature as ah, refreshSession as ai, revokeSession as aj, signSessionCreateMessage as ak, genAuthNonce as al, genSolanaMessage as am, createSolanaSignInRequest as an, createSolanaSignInMessageText as ao, genSolanaSignInRequest as ap, genSolanaSignInMessage as aq, browserSignInDomain as ar, SOLANA_SIGN_IN_STATEMENT as as, genSiweMessage as at, normalizeEvmAddress as au, toLowerEvmAddress as av, functions as aw, invoke as ax, FunctionInvokeError as ay, live as az, bufferExports as b, getWebhookKeysUrl as c, getConfiguredAppId as d, get as e, getMany as f, getConfig as g, docId as h, init as i, setMany as j, setFile as k, getFiles as l, search as m, runQueryMany as n, runExpression as o, runExpressionMany as p, queryAggregate as q, runQuery as r, set as s, signMessage as t, signTransaction as u, signAndSubmitTransaction as v, count as w, aggregate as x, reconcileSetResult as y, waitForSetResult as z };
11278
- //# sourceMappingURL=index-CLst0IiM.mjs.map
11302
+ export { increment as $, isWriteNotApplied as A, writeNotAppliedPhase as B, subscribe as C, closeAllSubscriptions as D, clearCache as E, getCachedData as F, reconnectWithNewAuth as G, createClient as H, InsufficientBalanceError as I, exchangeAppToken as J, clearAppTokenCache as K, createAppTokenAuthHeaders as L, AppTokenExchangeError as M, subscribeQuery as N, closeAllQuerySubscriptions as O, canonicalizeArgs as P, canonicalizeResult as Q, DEFAULT_QUERY_POLL_INTERVAL_MS as R, ScopedClientReadOnlyError as S, MIN_QUERY_POLL_INTERVAL_MS as T, BoundedDeclineError as U, deriveDecline as V, dispatchDeclineEvent as W, DECLINE_EVENT_NAME as X, dispatchRateLimitEvent as Y, RATE_LIMIT_EVENT_NAME as Z, hasActiveConnection as _, getWebhookKeysUrl as a, serverTimestamp as a0, now as a1, toSeconds as a2, toMillis as a3, getIdToken as a4, deriveUserIdentityFromIdToken as a5, getSessionGeneration as a6, WebSessionManager as a7, ReactNativeSessionManager as a8, getActiveSessionManager as a9, live as aA, intent as aB, status as aC, subscribeView as aD, LiveIntentError as aE, withEffects as aF, isEffectResult as aG, defineLiveModule as aH, EFFECT_INTENT_ADDRESS as aI, onSessionCleared as aa, SessionEnvironmentError as ab, UNSUPPORTED_ENVIRONMENT_CODE as ac, AUTH_CHANGED_CODE as ad, AUTH_EXPIRED_CODE as ae, isAuthExpiredError as af, getFreshAuthToken as ag, ServerSessionManager as ah, createSessionWithSignature as ai, refreshSession as aj, revokeSession as ak, signSessionCreateMessage as al, genAuthNonce as am, genSolanaMessage as an, createSolanaSignInRequest as ao, createSolanaSignInMessageText as ap, genSolanaSignInRequest as aq, genSolanaSignInMessage as ar, browserSignInDomain as as, SOLANA_SIGN_IN_STATEMENT as at, genSiweMessage as au, normalizeEvmAddress as av, toLowerEvmAddress as aw, functions as ax, invoke as ay, FunctionInvokeError as az, bufferExports as b, getConfiguredAppId as c, get as d, getMany as e, docId as f, getConfig as g, setMany as h, init as i, setFile as j, getFiles as k, search as l, markWriteNotApplied as m, runQueryMany as n, runExpression as o, runExpressionMany as p, queryAggregate as q, runQuery as r, set as s, signMessage as t, signTransaction as u, signAndSubmitTransaction as v, count as w, aggregate as x, reconcileSetResult as y, waitForSetResult as z };
11303
+ //# sourceMappingURL=index-No5Vz0YM.mjs.map