@bounded-sh/core 0.0.71 → 0.0.73

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.
@@ -11,9 +11,56 @@ import type { SetOptions } from './operations';
11
11
  */
12
12
  export declare function deriveTransactionSignature(signedTransaction: VersionedTransaction): string;
13
13
  export declare function isDefinitiveSolanaSendFailure(error: unknown): boolean;
14
+ /**
15
+ * One expected field operation, derived from the caller's request document.
16
+ * `value`/`delta` are the JSON-round-tripped request values (what the server
17
+ * actually parsed), NOT the pre-serialization JS objects.
18
+ */
19
+ export type ExpectedFieldOperation = {
20
+ key: string;
21
+ kind: 'write';
22
+ value: unknown;
23
+ } | {
24
+ key: string;
25
+ kind: 'delete';
26
+ } | {
27
+ key: string;
28
+ kind: 'timestamp';
29
+ } | {
30
+ key: string;
31
+ kind: 'increment';
32
+ delta: number;
33
+ };
34
+ export interface ExpectedWriteIntent {
35
+ documents: Array<{
36
+ path: string;
37
+ operations: ExpectedFieldOperation[];
38
+ }>;
39
+ deletePaths: string[];
40
+ }
41
+ /**
42
+ * Build the expected write intent from the exact request the client POSTed
43
+ * (`[{ destinationPath, document }]`, a `null` document being a delete).
44
+ *
45
+ * Each document is JSON-round-tripped first: the server parsed the SERIALIZED
46
+ * body, so the intent must be derived from the same bytes - this drops
47
+ * `undefined` values, turns `NaN`/`Infinity` into `null`, and normalizes key
48
+ * order exactly as the server's JSON.parse did.
49
+ *
50
+ * Operation order mirrors the worker's transform (normalizeWriteBody ->
51
+ * resolveOperationFields -> documentToOperations): plain fields in request key
52
+ * order, then increment fields appended in the order they appeared. The
53
+ * comparison is therefore ORDERED, not a multiset - the honest builder is
54
+ * deterministic given the request, and ordering additionally catches
55
+ * duplicate-path and reordering tampering that the retired set comparison hid.
56
+ */
57
+ export declare function buildExpectedWriteIntent(documents: Array<{
58
+ destinationPath: string;
59
+ document: any;
60
+ }>): ExpectedWriteIntent;
14
61
  export declare function handlePreBuiltTransaction(tx: {
15
62
  serializedTransaction: string;
16
63
  blockhash: string;
17
64
  lastValidBlockHeight: number;
18
65
  network: string;
19
- }, authProvider: AuthProvider, options: SetOptions | undefined, expectedWritePaths: string[]): Promise<SolanaTransactionResult>;
66
+ }, authProvider: AuthProvider, options: SetOptions | undefined, expectedIntent: ExpectedWriteIntent): Promise<SolanaTransactionResult>;
@@ -4133,7 +4133,7 @@ class ServerSessionManager {
4133
4133
  const sessionConfig = (_a = this.sessionConfig) !== null && _a !== void 0 ? _a : undefined;
4134
4134
  /* fetch nonce from auth API (bound to the snapshotted app+issuer) */
4135
4135
  const nonce = await genAuthNonce(sessionConfig);
4136
- const { genSolanaMessage } = await Promise.resolve().then(function () { return require('./solana-message-Dh9ryO-Y.js'); });
4136
+ const { genSolanaMessage } = await Promise.resolve().then(function () { return require('./solana-message-Cam1-0wi.js'); });
4137
4137
  const message = await genSolanaMessage(address, nonce, sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.appId);
4138
4138
  /* sign the message */
4139
4139
  const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
@@ -6901,7 +6901,7 @@ async function doReconnectWithNewAuth() {
6901
6901
  console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
6902
6902
  }
6903
6903
  try {
6904
- const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return require('./realtime-store-BDpIOFMH.js'); });
6904
+ const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return require('./realtime-store-JPgBppmw.js'); });
6905
6905
  await reconnectRealtimeStoreWithNewAuth();
6906
6906
  }
6907
6907
  catch (error) {
@@ -8327,7 +8327,11 @@ async function setMany(many, options) {
8327
8327
  if (hasSerializedSolanaTarget && hasBuiltSolanaTarget) {
8328
8328
  throw new Error('Ambiguous on-chain write response mixes Solana transaction target formats');
8329
8329
  }
8330
- const expectedWritePaths = documents.map(d => d.destinationPath);
8330
+ // All requested paths (writes AND deletes), in request order. Used for the
8331
+ // post-confirm mirror read-back, which correlates every requested path.
8332
+ // The pre-sign validation derives its own full expected intent from
8333
+ // `documents` (see below) and needs nothing from this list.
8334
+ const requestedPaths = documents.map(d => d.destinationPath);
8331
8335
  const requestedDocuments = buildRequestedDocuments(documents);
8332
8336
  const appId = config.appId;
8333
8337
  // The lane comes verbatim from the worker, and is read BEFORE anything is
@@ -8344,8 +8348,13 @@ async function setMany(many, options) {
8344
8348
  'Nothing was signed or submitted.');
8345
8349
  }
8346
8350
  const solanaChain = workerChain;
8347
- const { handlePreBuiltTransaction } = await Promise.resolve().then(function () { return require('./solana-operations-DGEGqFd9.js'); });
8348
- const solanaOutcome = await handlePreBuiltTransaction(curTx, authProvider, options, expectedWritePaths);
8351
+ const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await Promise.resolve().then(function () { return require('./solana-operations-DycSSUls.js'); });
8352
+ // OA-0001: derive the expected intent from OUR OWN request (the one
8353
+ // ground truth a tampered builder response cannot change) so the
8354
+ // validator can reject any transaction whose operation semantics
8355
+ // diverge from it BEFORE the wallet is asked to sign.
8356
+ const expectedIntent = buildExpectedWriteIntent(documents);
8357
+ const solanaOutcome = await handlePreBuiltTransaction(curTx, authProvider, options, expectedIntent);
8349
8358
  // `shouldSubmitTx: false` signed WITHOUT sending — no signature exists,
8350
8359
  // and there is nothing to mirror, so no synchronization is attempted.
8351
8360
  if (solanaOutcome.outcome === 'signed') {
@@ -8367,7 +8376,7 @@ async function setMany(many, options) {
8367
8376
  }
8368
8377
  // Confirmed: the receipt is already complete, so the mirror read is a
8369
8378
  // best-effort enrichment reported in `mirrorSync`, never a rejection.
8370
- const mirrorSync = await attemptMirrorSync(expectedWritePaths, options);
8379
+ const mirrorSync = await attemptMirrorSync(requestedPaths, options);
8371
8380
  return {
8372
8381
  status: 'confirmed', appId, requestedDocuments, chain: solanaChain,
8373
8382
  transactionId: solanaOutcome.transactionSignature,
@@ -9785,7 +9794,7 @@ function toMillis(seconds) {
9785
9794
 
9786
9795
  /** SIWS message creation is async already, so its onchain module can be lazy. */
9787
9796
  async function genSolanaMessage(address, nonce) {
9788
- const implementation = await Promise.resolve().then(function () { return require('./solana-message-Dh9ryO-Y.js'); });
9797
+ const implementation = await Promise.resolve().then(function () { return require('./solana-message-Cam1-0wi.js'); });
9789
9798
  return implementation.genSolanaMessage(address, nonce);
9790
9799
  }
9791
9800
 
@@ -10346,4 +10355,4 @@ exports.toMillis = toMillis;
10346
10355
  exports.toSeconds = toSeconds;
10347
10356
  exports.waitForSetResult = waitForSetResult;
10348
10357
  exports.withEffects = withEffects;
10349
- //# sourceMappingURL=index-yT8ZfzrL.js.map
10358
+ //# sourceMappingURL=index-C9ezg0Ef.js.map