@gala-chain/gswap-sdk 2.1.0-beta.1 → 2.1.0-beta.2

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.
@@ -1,2 +1,2 @@
1
- import{isCatalogCode as e}from"../classes/gswap_error_phase_table.js";import{NOT_QUEUED_FINAL as t,classification as n}from"./classification.js";import{isWriteView as r,numberParamOf as i,stringParamOf as a,toFailureView as o}from"@gala-chain/gala-core-sdk";const s=n(`rejected`),c=n(`unknown`),l=n(`not_queued`),u=n(`not_sent`),d=n(`invalid_input`),f=n(`transient`),p={REQUEST_TIMEOUT:f,TRANSPORT_ERROR:f,REQUEST_FAILED:f,SUBMISSION_OUTCOME_UNKNOWN:c,GATEWAY_REPORTED_ERROR:c,GATEWAY_DATA_MISSING:c,GATEWAY_MODE_INVALID:c,GATEWAY_SYNC_REQUEST_TRADE:c,MISSING_UNIQUE_KEY:c,UNIQUE_KEY_MISMATCH:c,GATEWAY_WRITE_OUTCOME_UNKNOWN:c,GATEWAY_RESPONSE_INVALID:c,BRIDGE_RESPONSE_INVALID:c,BRIDGE_WRITE_OUTCOME_UNKNOWN:c,BRIDGE_OUT_OUTCOME_UNKNOWN:c,BRIDGE_DEPOSIT_OUTCOME_UNKNOWN:c,BRIDGE_STATUS_TIMEOUT:c,SHADOW_NOT_QUEUED:t,METHOD_NOT_ALLOWED:l,SIGNATURE_INVALID:l,SIGNER_MISMATCH:l,DTO_INVALID:l,DTO_OPERATION_MISMATCH:l,BOUNDS_VIOLATION:l,SYMBOL_CONFLICT:l,RATE_LIMITED:l,GATEWAY_REJECTED:l,CHAIN_REJECTED:s,INVALID_CHAIN_RESPONSE:f,INVALID_POSITION_RESPONSE:f,GATEWAY_READ_REJECTED:f,GATEWAY_READ_RESPONSE_INVALID:f,BRIDGE_READ_RESPONSE_INVALID:f,BRIDGE_TOKEN_NOT_FOUND:d,TOKEN_NOT_WRAPPABLE:d,BRIDGE_CHAIN_READ_FAILED:f,TOKEN_CLASS_NOT_FOUND:d,PAGINATION_LIMIT_EXCEEDED:d,CONFIRMATION_TIMEOUT:c,CONFIRMATION_ABORTED:c,CONFIRMATION_FAILED:c,CONFIRMATION_MALFORMED:c,CONFIRMATION_KEY_MISMATCH:c,STREAM_DISCONNECTED:f,STREAM_REQUEST_TIMEOUT:f,STREAM_CONNECT_FAILED:f,STREAM_CLOSED_BEFORE_CONNECT:f,STREAM_CONNECTION_CLOSED:f,SIGNING_FAILED:u,KEY_PROVIDER_FAILED:u,TOKEN_RESOLUTION_FAILED:u,TOKEN_NAME_RESOLUTION_FAILED:u,NO_WALLET_ADDRESS:d,NO_POOL_AVAILABLE:d,INSUFFICIENT_LIQUIDITY:d};function m(e){return a(e,`reason`)===`no_key_provided`?u:d}function h(e){return e.status??i(e,`status`)}function g(e){let n=h(e);return n===void 0?c:r(e)?n===429?l:n===408||n>=500?c:t:n===408||n===429||n>=500?f:d}function _(e){return r(e)&&h(e)===409?c:g(e)}function v(e){return r(e)?t:d}function y(e){let t=e.status;return t===void 0||t===409||t>=500?c:t===422?s:l}function b(e){let t=e.status;return t===void 0||t===409||t>=500?c:s}function x(e,t={}){let n=S(o(e));return t.afterQueued===!0&&n.outcome!==`rejected`?{...n,retryable:!1}:n}function S(t){if(t===void 0)return{...c};if(!e(t.code))return t.outcome===void 0?{...c}:t.retryable===!1?n(t.outcome,{retryable:!1}):n(t.outcome);let{code:r}=t;return r===`HTTP_ERROR`||r===`GATEWAY_HTTP_ERROR`?{...g(t)}:r===`GALACONNECT_HTTP_ERROR`?{..._(t)}:r===`GATEWAY_WRITE_REJECTED`||r===`BRIDGE_REQUEST_REJECTED`?{...b(t)}:r===`UPDATE_REQUIRED`?{...v(t)}:r===`CHAIN_DISPATCH_FAILED`?{...y(t)}:r===`INVALID_PRIVATE_KEY`?{...m(t)}:{...p[r]??d}}function C(e){return x(e).outcome===`unknown`}export{x as classifyFailure,C as mayHaveBeenQueued};
1
+ import{isCatalogCode as e}from"../classes/gswap_error_phase_table.js";import{NOT_QUEUED_FINAL as t,classification as n}from"./classification.js";import{isWriteView as r,numberParamOf as i,stringParamOf as a,toFailureView as o}from"@gala-chain/gala-core-sdk";const s=n(`rejected`),c=n(`unknown`),l=n(`not_queued`),u=n(`not_sent`),d=n(`invalid_input`),f=n(`transient`),p={REQUEST_TIMEOUT:f,TRANSPORT_ERROR:f,REQUEST_FAILED:f,SUBMISSION_OUTCOME_UNKNOWN:c,GATEWAY_REPORTED_ERROR:c,GATEWAY_DATA_MISSING:c,GATEWAY_MODE_INVALID:c,GATEWAY_SYNC_REQUEST_TRADE:c,MISSING_UNIQUE_KEY:c,UNIQUE_KEY_MISMATCH:c,GATEWAY_WRITE_OUTCOME_UNKNOWN:c,GATEWAY_RESPONSE_INVALID:c,BRIDGE_RESPONSE_INVALID:c,BRIDGE_WRITE_OUTCOME_UNKNOWN:c,BRIDGE_OUT_OUTCOME_UNKNOWN:c,BRIDGE_DEPOSIT_OUTCOME_UNKNOWN:c,BRIDGE_STATUS_TIMEOUT:c,SHADOW_NOT_QUEUED:t,METHOD_NOT_ALLOWED:l,SIGNATURE_INVALID:l,SIGNER_MISMATCH:l,DTO_INVALID:t,DTO_OPERATION_MISMATCH:t,BOUNDS_VIOLATION:l,SYMBOL_CONFLICT:l,RATE_LIMITED:l,GATEWAY_REJECTED:l,CHAIN_REJECTED:s,INVALID_CHAIN_RESPONSE:f,INVALID_POSITION_RESPONSE:f,GATEWAY_READ_REJECTED:f,GATEWAY_READ_RESPONSE_INVALID:f,BRIDGE_READ_RESPONSE_INVALID:f,BRIDGE_TOKEN_NOT_FOUND:d,TOKEN_NOT_WRAPPABLE:d,BRIDGE_CHAIN_READ_FAILED:f,TOKEN_CLASS_NOT_FOUND:d,PAGINATION_LIMIT_EXCEEDED:d,CONFIRMATION_TIMEOUT:c,CONFIRMATION_ABORTED:c,CONFIRMATION_FAILED:c,CONFIRMATION_MALFORMED:c,CONFIRMATION_KEY_MISMATCH:c,STREAM_DISCONNECTED:f,STREAM_REQUEST_TIMEOUT:f,STREAM_CONNECT_FAILED:f,STREAM_CLOSED_BEFORE_CONNECT:f,STREAM_CONNECTION_CLOSED:f,SIGNING_FAILED:u,KEY_PROVIDER_FAILED:u,TOKEN_RESOLUTION_FAILED:u,TOKEN_NAME_RESOLUTION_FAILED:u,NO_WALLET_ADDRESS:d,NO_POOL_AVAILABLE:d,INSUFFICIENT_LIQUIDITY:d};function m(e){return a(e,`reason`)===`no_key_provided`?u:d}function h(e){return e.status??i(e,`status`)}function g(e){let n=h(e);return n===void 0?c:r(e)?n===429?l:n===408||n>=500?c:t:n===408||n===429||n>=500?f:d}function _(e){return r(e)&&h(e)===409?c:g(e)}function v(e){return r(e)?t:d}function y(e){let t=e.status;return t===void 0||t===409||t>=500?c:t===422?s:l}function b(e){let t=e.status;return t===void 0||t===409||t>=500?c:s}function x(e,t={}){let n=S(o(e));return t.afterQueued===!0&&n.outcome!==`rejected`?{...n,retryable:!1}:n}function S(t){if(t===void 0)return{...c};if(!e(t.code))return t.outcome===void 0?{...c}:t.retryable===!1?n(t.outcome,{retryable:!1}):n(t.outcome);let{code:r}=t;return r===`HTTP_ERROR`||r===`GATEWAY_HTTP_ERROR`?{...g(t)}:r===`GALACONNECT_HTTP_ERROR`?{..._(t)}:r===`GATEWAY_WRITE_REJECTED`||r===`BRIDGE_REQUEST_REJECTED`?{...b(t)}:r===`UPDATE_REQUIRED`?{...v(t)}:r===`CHAIN_DISPATCH_FAILED`?{...y(t)}:r===`INVALID_PRIVATE_KEY`?{...m(t)}:{...p[r]??d}}function C(e){return x(e).outcome===`unknown`}export{x as classifyFailure,C as mayHaveBeenQueued};
2
2
  //# sourceMappingURL=failure_classification.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"failure_classification.js","names":["REJECTED","classification","UNKNOWN","NOT_QUEUED","NOT_SENT","INVALID_INPUT","TRANSIENT","BY_CODE","NOT_QUEUED_FINAL","classifyInvalidPrivateKey","stringParamOf","view","statusOfView","numberParamOf","classifyHttpError","status","isWriteView","classifyGalaConnectHttpError","classifyUpdateRequired","classifyChainDispatchFailed","classifyGatewayWriteRejected","classifyFailure","result","classifyView","toFailureView","error","options","isCatalogCode","code","mayHaveBeenQueued"],"sources":["../../../src/utils/failure_classification.ts"],"sourcesContent":["import {\n isWriteView,\n numberParamOf,\n stringParamOf,\n toFailureView,\n type FailureView,\n type GALA_ERROR_CATALOG,\n} from '@gala-chain/gala-core-sdk';\nimport type { GSWAP_ERROR_CATALOG, GSwapErrorCode } from '../classes/gswap_error_catalog.js';\nimport { isCatalogCode } from '../classes/gswap_error_phase_table.js';\nimport { classification, NOT_QUEUED_FINAL, type FailureClassification } from './classification.js';\n\n/**\n * `classifyFailure` and `mayHaveBeenQueued`: the one module that holds the decision for every\n * catalog code (~135 names, a floor no minifier can remove). It needs the code table and nothing\n * else: the classification values live in `classification.ts`, the yes/no guards in\n * `failure_guards.ts`, and the error class and catalog are only named as types.\n */\n\nconst REJECTED: FailureClassification = classification('rejected');\nconst UNKNOWN: FailureClassification = classification('unknown');\nconst NOT_QUEUED: FailureClassification = classification('not_queued');\nconst NOT_SENT: FailureClassification = classification('not_sent');\nconst INVALID_INPUT: FailureClassification = classification('invalid_input');\nconst TRANSIENT: FailureClassification = classification('transient');\n\n/**\n * The classification of every code whose meaning does not depend on the failing answer. A code\n * missing here is a compile error (unless it is `pre_send`, see {@link CodeTable}), so adding a\n * catalog code forces a decision.\n */\nconst BY_CODE = {\n // ----------------------------------------------------------------------------------- transport\n REQUEST_TIMEOUT: TRANSIENT,\n TRANSPORT_ERROR: TRANSIENT,\n REQUEST_FAILED: TRANSIENT,\n\n // ------------------------------------------------------------------------------- write: unknown\n // No answer was read, or a 2xx answer that does not prove the write was queued under the signed\n // key: it may have been applied.\n SUBMISSION_OUTCOME_UNKNOWN: UNKNOWN,\n GATEWAY_REPORTED_ERROR: UNKNOWN,\n GATEWAY_DATA_MISSING: UNKNOWN,\n GATEWAY_MODE_INVALID: UNKNOWN,\n GATEWAY_SYNC_REQUEST_TRADE: UNKNOWN,\n MISSING_UNIQUE_KEY: UNKNOWN,\n UNIQUE_KEY_MISMATCH: UNKNOWN,\n // Core `GatewayWrites` (token-contract writes): no answer read, or a 2xx that proves nothing.\n GATEWAY_WRITE_OUTCOME_UNKNOWN: UNKNOWN,\n GATEWAY_RESPONSE_INVALID: UNKNOWN,\n // Core `./bridge` (GalaConnect bridge writes): a 2xx without the id or hash the next step needs,\n // or no answer read after the request left.\n BRIDGE_RESPONSE_INVALID: UNKNOWN,\n BRIDGE_WRITE_OUTCOME_UNKNOWN: UNKNOWN,\n // Any failure after GalaConnect accepted a bridge-out request: the tokens may be locked, so\n // never a retry under a fresh key, whatever the inner failure (a BridgeTokenOut 429 included).\n BRIDGE_OUT_OUTCOME_UNKNOWN: UNKNOWN,\n // A bridge-in deposit that failed once it was being sent (sending, confirming, registering): the\n // funds may have left the wallet. A status wait that timed out: the bridge may still complete.\n BRIDGE_DEPOSIT_OUTCOME_UNKNOWN: UNKNOWN,\n BRIDGE_STATUS_TIMEOUT: UNKNOWN,\n\n // --------------------------------------------------------------------- write: refused, not queued\n SHADOW_NOT_QUEUED: NOT_QUEUED_FINAL,\n METHOD_NOT_ALLOWED: NOT_QUEUED,\n SIGNATURE_INVALID: NOT_QUEUED,\n SIGNER_MISMATCH: NOT_QUEUED,\n DTO_INVALID: NOT_QUEUED,\n DTO_OPERATION_MISMATCH: NOT_QUEUED,\n BOUNDS_VIOLATION: NOT_QUEUED,\n SYMBOL_CONFLICT: NOT_QUEUED,\n RATE_LIMITED: NOT_QUEUED,\n GATEWAY_REJECTED: NOT_QUEUED,\n\n // ----------------------------------------------------------------------- the chain's own verdict\n CHAIN_REJECTED: REJECTED,\n\n // ----------------------------------------------------------------------- reads of backend data\n INVALID_CHAIN_RESPONSE: TRANSIENT,\n INVALID_POSITION_RESPONSE: TRANSIENT,\n // Core `GatewayReads` (unsigned token-contract reads): nothing is applied, so a refusal or an\n // unusable answer is worth another try; a class that does not exist, or a data set larger than\n // the caller allowed, needs a different request.\n GATEWAY_READ_REJECTED: TRANSIENT,\n GATEWAY_READ_RESPONSE_INVALID: TRANSIENT,\n // Core `./bridge` GalaConnect reads (configurations, fee, status): the same as a gateway read;\n // a symbol GalaConnect has no verified configuration for needs a different request.\n BRIDGE_READ_RESPONSE_INVALID: TRANSIENT,\n BRIDGE_TOKEN_NOT_FOUND: INVALID_INPUT,\n // A token the wrappable list does not have (or its counterpart, or a channel a wrap does not\n // move between): a different request is needed.\n TOKEN_NOT_WRAPPABLE: INVALID_INPUT,\n // A failed or unusable read of the external chain before a bridge-in deposit: nothing was sent.\n BRIDGE_CHAIN_READ_FAILED: TRANSIENT,\n TOKEN_CLASS_NOT_FOUND: INVALID_INPUT,\n PAGINATION_LIMIT_EXCEEDED: INVALID_INPUT,\n\n // -------------------------------------------------------------------------------- confirmation\n CONFIRMATION_TIMEOUT: UNKNOWN,\n CONFIRMATION_ABORTED: UNKNOWN,\n CONFIRMATION_FAILED: UNKNOWN,\n CONFIRMATION_MALFORMED: UNKNOWN,\n CONFIRMATION_KEY_MISMATCH: UNKNOWN,\n\n // ------------------------------------------------------------------------------ the realtime stream\n STREAM_DISCONNECTED: TRANSIENT,\n STREAM_REQUEST_TIMEOUT: TRANSIENT,\n STREAM_CONNECT_FAILED: TRANSIENT,\n STREAM_CLOSED_BEFORE_CONNECT: TRANSIENT,\n STREAM_CONNECTION_CLOSED: TRANSIENT,\n\n // ----------------------------------------------- pre-send: an app-supplied callback failed\n // The signer, the key provider or the token resolver threw before anything was transmitted. The\n // original error is `cause`. A repeat cannot apply anything twice.\n SIGNING_FAILED: NOT_SENT,\n KEY_PROVIDER_FAILED: NOT_SENT,\n TOKEN_RESOLUTION_FAILED: NOT_SENT,\n TOKEN_NAME_RESOLUTION_FAILED: NOT_SENT,\n\n // ------------------------------------------- the call cannot work as written, found AFTER an answer\n // (a pre_send code needs no row: it is `invalid_input` by default, see `CodeTable`)\n NO_WALLET_ADDRESS: INVALID_INPUT,\n NO_POOL_AVAILABLE: INVALID_INPUT,\n INSUFFICIENT_LIQUIDITY: INVALID_INPUT,\n} as const satisfies CodeTable;\n\n/** Both catalogs as one type: every code a `GSwapSDKError` can carry reads its entry here. */\ntype Catalogs = typeof GALA_ERROR_CATALOG & typeof GSWAP_ERROR_CATALOG;\n\n/** A code (core or DEX) whose catalog phase is `pre_send`. */\ntype PreSendCode = {\n [C in GSwapErrorCode]: Catalogs[C]['phase'] extends 'pre_send' ? C : never;\n}[GSwapErrorCode];\n\n/** The codes whose classification depends on the failing answer, not on a table row. */\ntype AnswerDependentCode =\n | 'HTTP_ERROR'\n | 'UPDATE_REQUIRED'\n | 'CHAIN_DISPATCH_FAILED'\n | 'INVALID_PRIVATE_KEY'\n | 'GATEWAY_HTTP_ERROR'\n | 'GALACONNECT_HTTP_ERROR'\n | 'GATEWAY_WRITE_REJECTED'\n | 'BRIDGE_REQUEST_REJECTED';\n\n/**\n * Every code that is NOT `pre_send` needs a row (a new one is a compile error until it has one);\n * a `pre_send` code may have one (`SIGNING_FAILED` is `not_sent`). A `pre_send` code without a row\n * is `invalid_input`: nothing was transmitted, so it cannot be `unknown`, and it is not a\n * condition a repeat clears. That keeps the 90-odd validation codes out of the table, which is\n * what keeps `classifyFailure` small enough for an app shell.\n */\ntype CodeTable = {\n readonly [C in Exclude<GSwapErrorCode, PreSendCode | AnswerDependentCode>]: FailureClassification;\n} & {\n readonly [C in Exclude<PreSendCode, AnswerDependentCode>]?: FailureClassification;\n};\n\n/**\n * A key that is not valid is the input's fault (`invalid_input`), except `no_key_provided`: the\n * provider had nothing to give right now (a locked wallet, an empty slot). That is the same\n * situation as a provider that throws (`KEY_PROVIDER_FAILED`), and it clears once a key exists, so\n * it is `not_sent` and Retry is safe.\n */\nfunction classifyInvalidPrivateKey(view: FailureView): FailureClassification {\n return stringParamOf(view, 'reason') === 'no_key_provided' ? NOT_SENT : INVALID_INPUT;\n}\n\n/** The status of an HTTP failure: the one it carries, else its `status` param. */\nfunction statusOfView(view: FailureView): number | undefined {\n return view.status ?? numberParamOf(view, 'status');\n}\n\n/**\n * An HTTP failure with no gateway or chain code: its meaning depends on the status and on read vs\n * write. A view that states no status at all cannot be judged: `unknown`.\n */\nfunction classifyHttpError(view: FailureView): FailureClassification {\n const status = statusOfView(view);\n if (status === undefined) return UNKNOWN;\n if (isWriteView(view)) {\n // A gateway that answered 5xx or 408 may still have queued the write; 429 and 4xx did not.\n if (status === 429) return NOT_QUEUED;\n if (status === 408 || status >= 500) return UNKNOWN;\n return NOT_QUEUED_FINAL;\n }\n return status === 408 || status === 429 || status >= 500 ? TRANSIENT : INVALID_INPUT;\n}\n\n/**\n * A core `./bridge` GalaConnect HTTP failure: like {@link classifyHttpError}, except that a\n * WRITE answered 409 is `unknown`: GalaChain's \"this uniqueKey was already consumed\", after which\n * an earlier attempt under the key may be applied (a bridge-out that locked the tokens).\n */\nfunction classifyGalaConnectHttpError(view: FailureView): FailureClassification {\n return isWriteView(view) && statusOfView(view) === 409 ? UNKNOWN : classifyHttpError(view);\n}\n\n/**\n * The version wall (HTTP 426). On a write the gateway refused it before queueing anything, and a\n * repeat of the same app version is refused the same way, so it is `not_queued` with no Retry. On a\n * read nothing changed either, and only an update helps: `invalid_input`, no Retry. Both say \"show\n * the update screen\", not \"try again\".\n */\nfunction classifyUpdateRequired(view: FailureView): FailureClassification {\n return isWriteView(view) ? NOT_QUEUED_FINAL : INVALID_INPUT;\n}\n\n/**\n * The chain dispatch failed. The gateway echoes the upstream status: 409 is \"this uniqueKey was\n * already consumed\" (the first attempt may be applied), 422 is the chain evaluating the write and\n * refusing it (definite), another 5xx is an upstream outage after which the write may have landed,\n * and a 4xx is a refusal before anything was queued.\n */\nfunction classifyChainDispatchFailed(view: FailureView): FailureClassification {\n const status = view.status;\n if (status === undefined || status === 409 || status >= 500) return UNKNOWN;\n return status === 422 ? REJECTED : NOT_QUEUED;\n}\n\n/**\n * GalaChain answered a core `GatewayWrites` token-contract write with a `Status` other than 1: the\n * chain's verdict, so the write was not applied (`rejected`), except where the verdict does not\n * prove that: a 409 is \"this uniqueKey was already consumed\" (an earlier attempt under it may be\n * applied), and a 5xx, or no status at all, cannot be judged.\n */\nfunction classifyGatewayWriteRejected(view: FailureView): FailureClassification {\n const status = view.status;\n return status === undefined || status === 409 || status >= 500 ? UNKNOWN : REJECTED;\n}\n\n/**\n * Classifies any thrown value from its code (and, for a few codes, its HTTP status), never from\n * message text, so an app does not have to re-derive \"may I offer Retry?\" and \"is the outcome\n * unknown?\" for every code. The decision for each code is in this module's table; a new catalog\n * code cannot be added without one.\n *\n * A failure BEFORE the request left (`error.phase === 'pre_send'`, `isPreSendError`) can never be\n * `unknown` or `rejected`: it is `invalid_input` (the input has to change) or `not_sent` (nothing\n * was transmitted; a repeat is safe). Every app-supplied callback the SDK calls before it sends\n * (the signer, the key provider, a token resolver) has its failure wrapped into such an error\n * (`SIGNING_FAILED`, `KEY_PROVIDER_FAILED`, `TOKEN_RESOLUTION_FAILED`, the original as `cause`),\n * so an app NEVER needs an error class of its own to say \"nothing was sent\": a locked wallet,\n * a declined signature and a failed lookup all arrive as `not_sent`, retryable.\n *\n * It accepts a `GSwapSDKError` or a plain-object VIEW of one ({@link FailureView}: `{ code,\n * params?, status?, uniqueKey?, ... }`, the shape of the wrapper an app builds from an SDK error;\n * `toFailureView` reads any value), and decides from the same fields, so an app does not need to\n * keep the original error to ask. `{ afterQueued: true }` says the write is already queued (the\n * step being classified is a confirmation): `retryable` is then always `false`.\n *\n * Anything that is not an SDK error or such a view, or a view whose `code` the catalog does not\n * define, is `{ outcome: 'unknown', retryable: false }`: the SDK\n * cannot say what happened, and refusing Retry is the safe answer. That is only what escapes AFTER\n * the request left (an app requestor that rejects with its own error, a callback of yours that\n * throws once bytes were sent). An error your own code raises BEFORE it calls the SDK says so\n * itself: give it a code of its own and `outcome` (`{ code: 'POSITION_STALE', outcome: 'not_sent'\n * }`), and the SDK answers with that outcome's classification and `mayHaveBeenQueued` is `false`.\n * The declared `outcome` is read only for a code the catalog does not define.\n *\n * A failure is a WRITE's unless it states `write: false` (`GSwapSDKError.write`, set for every\n * SDK read): see {@link FailureView}. A view that lost its `uniqueKey` is therefore still judged as\n * a write (a 5xx stays `unknown`), never as a retryable read.\n *\n * To learn that a failure came from the dark (shadow) path, read `error.shadow` (a typed field of\n * every `GSwapSDKError`) or call `isShadowError(error)`, which also covers a non-SDK object that\n * escaped `swapShadow` / `submitSwapShadow` / `ChainGateway.submitShadow`. The classification is\n * the same as on the public path; an app that withholds Retry or a public fallback after a dark\n * swap applies that policy on top of `retryable`.\n *\n * @example\n * ```ts\n * try {\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * } catch (error) {\n * const { outcome, retryable } = classifyFailure(error);\n * if (outcome === 'unknown') keepPendingAndConfirm(error); // never a second swap\n * else if (retryable) offerRetry(); // a new request under a fresh key\n * }\n * ```\n */\nexport function classifyFailure(\n error: unknown,\n options: ClassifyFailureOptions = {},\n): FailureClassification {\n const result = classifyView(toFailureView(error));\n // A `rejected` outcome is the chain's definite no: the queued write was evaluated and NOT\n // applied, so a new request under a fresh key cannot double-apply it and Retry stays as it is.\n return options.afterQueued === true && result.outcome !== 'rejected'\n ? { ...result, retryable: false }\n : result;\n}\n\n/** Options of {@link classifyFailure}. */\nexport interface ClassifyFailureOptions {\n /**\n * The write is ALREADY queued (the gateway acknowledged it with this `uniqueKey`), and what is\n * being classified is a later step: confirming it, reading its apply result. Whatever the\n * outcome, `retryable` is then `false`: a NEW request would be a second trade, so the app\n * confirms the same key (or forgets it) and never offers \"Try again\". `outcome` is unchanged.\n *\n * The one exception is `rejected`: the chain evaluated the queued write and refused it (an\n * apply result of `Status: 0`, `CHAIN_REJECTED`; a 422 dispatch). That PROVES it was not\n * applied, so a new request under a fresh key cannot make a second trade, and `retryable`\n * keeps the answer it has without this option (`true`). Every outcome that does not prove\n * \"not applied\" (`unknown`, `transient`, `not_queued`, ...) is `false`.\n */\n afterQueued?: boolean | undefined;\n}\n\n/**\n * The classification of a failure view: the table decision for its code, the status-dependent\n * decision for the codes whose meaning needs it, and for a code the catalog does not define\n * (a wallet error, an ethers `ACTION_REJECTED`, a typo, an app's own error): `unknown` unless the\n * view DECLARES its `outcome` (an app's error, `{ code: 'POSITION_STALE', outcome: 'not_sent' }`),\n * then the SDK's own classification of that outcome. A value with no view is `unknown`.\n */\nfunction classifyView(view: FailureView | undefined): FailureClassification {\n if (view === undefined) return { ...UNKNOWN };\n if (!isCatalogCode(view.code)) {\n if (view.outcome === undefined) return { ...UNKNOWN };\n // Withhold-only: `retryable: false` removes Retry from the declared outcome, never adds it.\n return view.retryable === false\n ? classification(view.outcome, { retryable: false })\n : classification(view.outcome);\n }\n const { code } = view;\n // A core gateway HTTP failure without a chain envelope reads exactly like HTTP_ERROR (by its\n // status, and read vs write); so does a GalaConnect one, except a write's 409.\n if (code === 'HTTP_ERROR' || code === 'GATEWAY_HTTP_ERROR') {\n return { ...classifyHttpError(view) };\n }\n if (code === 'GALACONNECT_HTTP_ERROR') return { ...classifyGalaConnectHttpError(view) };\n // GalaChain's refusal of a core gateway or GalaConnect bridge write: by the chain's code.\n if (code === 'GATEWAY_WRITE_REJECTED' || code === 'BRIDGE_REQUEST_REJECTED') {\n return { ...classifyGatewayWriteRejected(view) };\n }\n if (code === 'UPDATE_REQUIRED') return { ...classifyUpdateRequired(view) };\n if (code === 'CHAIN_DISPATCH_FAILED') return { ...classifyChainDispatchFailed(view) };\n if (code === 'INVALID_PRIVATE_KEY') return { ...classifyInvalidPrivateKey(view) };\n const row: FailureClassification | undefined = (\n BY_CODE as Readonly<Partial<Record<GSwapErrorCode, FailureClassification>>>\n )[code];\n return { ...(row ?? INVALID_INPUT) };\n}\n\n/**\n * True when the failed call was a WRITE that may nevertheless have been queued: its outcome is\n * `unknown` (`classifyFailure(error).outcome === 'unknown'`: a timeout or broken connection after\n * the request left, an acknowledgement that proves nothing, a gateway 409 or 5xx, a failed\n * confirmation). The app must keep `error.uniqueKey` and confirm it, and must NOT submit again\n * under a fresh key. Every other outcome (`rejected`, `not_queued`, `not_sent`, `invalid_input`,\n * `transient`) proves the write is not pending.\n *\n * A value the SDK cannot judge (an app requestor's own rejection after the request left) is\n * `unknown` too, so it is `true`; handle errors your own code raised BEFORE calling the SDK first.\n * Meant for the catch of a write; a read never has a pending key to keep.\n *\n * @example\n * ```ts\n * try {\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * } catch (error) {\n * if (mayHaveBeenQueued(error)) keepPendingAndConfirm(error);\n * }\n * ```\n */\nexport function mayHaveBeenQueued(error: unknown): boolean {\n return classifyFailure(error).outcome === 'unknown';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"failure_classification.js","names":["REJECTED","classification","UNKNOWN","NOT_QUEUED","NOT_SENT","INVALID_INPUT","TRANSIENT","BY_CODE","NOT_QUEUED_FINAL","classifyInvalidPrivateKey","stringParamOf","view","statusOfView","numberParamOf","classifyHttpError","status","isWriteView","classifyGalaConnectHttpError","classifyUpdateRequired","classifyChainDispatchFailed","classifyGatewayWriteRejected","classifyFailure","result","classifyView","toFailureView","error","options","isCatalogCode","code","mayHaveBeenQueued"],"sources":["../../../src/utils/failure_classification.ts"],"sourcesContent":["import {\n isWriteView,\n numberParamOf,\n stringParamOf,\n toFailureView,\n type FailureView,\n type GALA_ERROR_CATALOG,\n} from '@gala-chain/gala-core-sdk';\nimport type { GSWAP_ERROR_CATALOG, GSwapErrorCode } from '../classes/gswap_error_catalog.js';\nimport { isCatalogCode } from '../classes/gswap_error_phase_table.js';\nimport { classification, NOT_QUEUED_FINAL, type FailureClassification } from './classification.js';\n\n/**\n * `classifyFailure` and `mayHaveBeenQueued`: the one module that holds the decision for every\n * catalog code (~135 names, a floor no minifier can remove). It needs the code table and nothing\n * else: the classification values live in `classification.ts`, the yes/no guards in\n * `failure_guards.ts`, and the error class and catalog are only named as types.\n */\n\nconst REJECTED: FailureClassification = classification('rejected');\nconst UNKNOWN: FailureClassification = classification('unknown');\nconst NOT_QUEUED: FailureClassification = classification('not_queued');\nconst NOT_SENT: FailureClassification = classification('not_sent');\nconst INVALID_INPUT: FailureClassification = classification('invalid_input');\nconst TRANSIENT: FailureClassification = classification('transient');\n\n/**\n * The classification of every code whose meaning does not depend on the failing answer. A code\n * missing here is a compile error (unless it is `pre_send`, see {@link CodeTable}), so adding a\n * catalog code forces a decision.\n */\nconst BY_CODE = {\n // ----------------------------------------------------------------------------------- transport\n REQUEST_TIMEOUT: TRANSIENT,\n TRANSPORT_ERROR: TRANSIENT,\n REQUEST_FAILED: TRANSIENT,\n\n // ------------------------------------------------------------------------------- write: unknown\n // No answer was read, or a 2xx answer that does not prove the write was queued under the signed\n // key: it may have been applied.\n SUBMISSION_OUTCOME_UNKNOWN: UNKNOWN,\n GATEWAY_REPORTED_ERROR: UNKNOWN,\n GATEWAY_DATA_MISSING: UNKNOWN,\n GATEWAY_MODE_INVALID: UNKNOWN,\n GATEWAY_SYNC_REQUEST_TRADE: UNKNOWN,\n MISSING_UNIQUE_KEY: UNKNOWN,\n UNIQUE_KEY_MISMATCH: UNKNOWN,\n // Core `GatewayWrites` (token-contract writes): no answer read, or a 2xx that proves nothing.\n GATEWAY_WRITE_OUTCOME_UNKNOWN: UNKNOWN,\n GATEWAY_RESPONSE_INVALID: UNKNOWN,\n // Core `./bridge` (GalaConnect bridge writes): a 2xx without the id or hash the next step needs,\n // or no answer read after the request left.\n BRIDGE_RESPONSE_INVALID: UNKNOWN,\n BRIDGE_WRITE_OUTCOME_UNKNOWN: UNKNOWN,\n // Any failure after GalaConnect accepted a bridge-out request: the tokens may be locked, so\n // never a retry under a fresh key, whatever the inner failure (a BridgeTokenOut 429 included).\n BRIDGE_OUT_OUTCOME_UNKNOWN: UNKNOWN,\n // A bridge-in deposit that failed once it was being sent (sending, confirming, registering): the\n // funds may have left the wallet. A status wait that timed out: the bridge may still complete.\n BRIDGE_DEPOSIT_OUTCOME_UNKNOWN: UNKNOWN,\n BRIDGE_STATUS_TIMEOUT: UNKNOWN,\n\n // --------------------------------------------------------------------- write: refused, not queued\n SHADOW_NOT_QUEUED: NOT_QUEUED_FINAL,\n METHOD_NOT_ALLOWED: NOT_QUEUED,\n SIGNATURE_INVALID: NOT_QUEUED,\n SIGNER_MISMATCH: NOT_QUEUED,\n // The gateway refuses the same signed DTO every time (its shape, or the operation it names): a\n // resend, under the same key or a fresh one, is refused again. The app has to fix or rebuild the\n // DTO, so Retry is withheld.\n DTO_INVALID: NOT_QUEUED_FINAL,\n DTO_OPERATION_MISMATCH: NOT_QUEUED_FINAL,\n BOUNDS_VIOLATION: NOT_QUEUED,\n SYMBOL_CONFLICT: NOT_QUEUED,\n RATE_LIMITED: NOT_QUEUED,\n GATEWAY_REJECTED: NOT_QUEUED,\n\n // ----------------------------------------------------------------------- the chain's own verdict\n CHAIN_REJECTED: REJECTED,\n\n // ----------------------------------------------------------------------- reads of backend data\n INVALID_CHAIN_RESPONSE: TRANSIENT,\n INVALID_POSITION_RESPONSE: TRANSIENT,\n // Core `GatewayReads` (unsigned token-contract reads): nothing is applied, so a refusal or an\n // unusable answer is worth another try; a class that does not exist, or a data set larger than\n // the caller allowed, needs a different request.\n GATEWAY_READ_REJECTED: TRANSIENT,\n GATEWAY_READ_RESPONSE_INVALID: TRANSIENT,\n // Core `./bridge` GalaConnect reads (configurations, fee, status): the same as a gateway read;\n // a symbol GalaConnect has no verified configuration for needs a different request.\n BRIDGE_READ_RESPONSE_INVALID: TRANSIENT,\n BRIDGE_TOKEN_NOT_FOUND: INVALID_INPUT,\n // A token the wrappable list does not have (or its counterpart, or a channel a wrap does not\n // move between): a different request is needed.\n TOKEN_NOT_WRAPPABLE: INVALID_INPUT,\n // A failed or unusable read of the external chain before a bridge-in deposit: nothing was sent.\n BRIDGE_CHAIN_READ_FAILED: TRANSIENT,\n TOKEN_CLASS_NOT_FOUND: INVALID_INPUT,\n PAGINATION_LIMIT_EXCEEDED: INVALID_INPUT,\n\n // -------------------------------------------------------------------------------- confirmation\n CONFIRMATION_TIMEOUT: UNKNOWN,\n CONFIRMATION_ABORTED: UNKNOWN,\n CONFIRMATION_FAILED: UNKNOWN,\n CONFIRMATION_MALFORMED: UNKNOWN,\n CONFIRMATION_KEY_MISMATCH: UNKNOWN,\n\n // ------------------------------------------------------------------------------ the realtime stream\n STREAM_DISCONNECTED: TRANSIENT,\n STREAM_REQUEST_TIMEOUT: TRANSIENT,\n STREAM_CONNECT_FAILED: TRANSIENT,\n STREAM_CLOSED_BEFORE_CONNECT: TRANSIENT,\n STREAM_CONNECTION_CLOSED: TRANSIENT,\n\n // ----------------------------------------------- pre-send: an app-supplied callback failed\n // The signer, the key provider or the token resolver threw before anything was transmitted. The\n // original error is `cause`. A repeat cannot apply anything twice.\n SIGNING_FAILED: NOT_SENT,\n KEY_PROVIDER_FAILED: NOT_SENT,\n TOKEN_RESOLUTION_FAILED: NOT_SENT,\n TOKEN_NAME_RESOLUTION_FAILED: NOT_SENT,\n\n // ------------------------------------------- the call cannot work as written, found AFTER an answer\n // (a pre_send code needs no row: it is `invalid_input` by default, see `CodeTable`)\n NO_WALLET_ADDRESS: INVALID_INPUT,\n NO_POOL_AVAILABLE: INVALID_INPUT,\n INSUFFICIENT_LIQUIDITY: INVALID_INPUT,\n} as const satisfies CodeTable;\n\n/** Both catalogs as one type: every code a `GSwapSDKError` can carry reads its entry here. */\ntype Catalogs = typeof GALA_ERROR_CATALOG & typeof GSWAP_ERROR_CATALOG;\n\n/** A code (core or DEX) whose catalog phase is `pre_send`. */\ntype PreSendCode = {\n [C in GSwapErrorCode]: Catalogs[C]['phase'] extends 'pre_send' ? C : never;\n}[GSwapErrorCode];\n\n/** The codes whose classification depends on the failing answer, not on a table row. */\ntype AnswerDependentCode =\n | 'HTTP_ERROR'\n | 'UPDATE_REQUIRED'\n | 'CHAIN_DISPATCH_FAILED'\n | 'INVALID_PRIVATE_KEY'\n | 'GATEWAY_HTTP_ERROR'\n | 'GALACONNECT_HTTP_ERROR'\n | 'GATEWAY_WRITE_REJECTED'\n | 'BRIDGE_REQUEST_REJECTED';\n\n/**\n * Every code that is NOT `pre_send` needs a row (a new one is a compile error until it has one);\n * a `pre_send` code may have one (`SIGNING_FAILED` is `not_sent`). A `pre_send` code without a row\n * is `invalid_input`: nothing was transmitted, so it cannot be `unknown`, and it is not a\n * condition a repeat clears. That keeps the 90-odd validation codes out of the table, which is\n * what keeps `classifyFailure` small enough for an app shell.\n */\ntype CodeTable = {\n readonly [C in Exclude<GSwapErrorCode, PreSendCode | AnswerDependentCode>]: FailureClassification;\n} & {\n readonly [C in Exclude<PreSendCode, AnswerDependentCode>]?: FailureClassification;\n};\n\n/**\n * A key that is not valid is the input's fault (`invalid_input`), except `no_key_provided`: the\n * provider had nothing to give right now (a locked wallet, an empty slot). That is the same\n * situation as a provider that throws (`KEY_PROVIDER_FAILED`), and it clears once a key exists, so\n * it is `not_sent` and Retry is safe.\n */\nfunction classifyInvalidPrivateKey(view: FailureView): FailureClassification {\n return stringParamOf(view, 'reason') === 'no_key_provided' ? NOT_SENT : INVALID_INPUT;\n}\n\n/** The status of an HTTP failure: the one it carries, else its `status` param. */\nfunction statusOfView(view: FailureView): number | undefined {\n return view.status ?? numberParamOf(view, 'status');\n}\n\n/**\n * An HTTP failure with no gateway or chain code: its meaning depends on the status and on read vs\n * write. A view that states no status at all cannot be judged: `unknown`.\n */\nfunction classifyHttpError(view: FailureView): FailureClassification {\n const status = statusOfView(view);\n if (status === undefined) return UNKNOWN;\n if (isWriteView(view)) {\n // A gateway that answered 5xx or 408 may still have queued the write; 429 and 4xx did not.\n if (status === 429) return NOT_QUEUED;\n if (status === 408 || status >= 500) return UNKNOWN;\n return NOT_QUEUED_FINAL;\n }\n return status === 408 || status === 429 || status >= 500 ? TRANSIENT : INVALID_INPUT;\n}\n\n/**\n * A core `./bridge` GalaConnect HTTP failure: like {@link classifyHttpError}, except that a\n * WRITE answered 409 is `unknown`: GalaChain's \"this uniqueKey was already consumed\", after which\n * an earlier attempt under the key may be applied (a bridge-out that locked the tokens).\n */\nfunction classifyGalaConnectHttpError(view: FailureView): FailureClassification {\n return isWriteView(view) && statusOfView(view) === 409 ? UNKNOWN : classifyHttpError(view);\n}\n\n/**\n * The version wall (HTTP 426). On a write the gateway refused it before queueing anything, and a\n * repeat of the same app version is refused the same way, so it is `not_queued` with no Retry. On a\n * read nothing changed either, and only an update helps: `invalid_input`, no Retry. Both say \"show\n * the update screen\", not \"try again\".\n */\nfunction classifyUpdateRequired(view: FailureView): FailureClassification {\n return isWriteView(view) ? NOT_QUEUED_FINAL : INVALID_INPUT;\n}\n\n/**\n * The chain dispatch failed. The gateway echoes the upstream status: 409 is \"this uniqueKey was\n * already consumed\" (the first attempt may be applied), 422 is the chain evaluating the write and\n * refusing it (definite), another 5xx is an upstream outage after which the write may have landed,\n * and a 4xx is a refusal before anything was queued.\n */\nfunction classifyChainDispatchFailed(view: FailureView): FailureClassification {\n const status = view.status;\n if (status === undefined || status === 409 || status >= 500) return UNKNOWN;\n return status === 422 ? REJECTED : NOT_QUEUED;\n}\n\n/**\n * GalaChain answered a core `GatewayWrites` token-contract write with a `Status` other than 1: the\n * chain's verdict, so the write was not applied (`rejected`), except where the verdict does not\n * prove that: a 409 is \"this uniqueKey was already consumed\" (an earlier attempt under it may be\n * applied), and a 5xx, or no status at all, cannot be judged.\n */\nfunction classifyGatewayWriteRejected(view: FailureView): FailureClassification {\n const status = view.status;\n return status === undefined || status === 409 || status >= 500 ? UNKNOWN : REJECTED;\n}\n\n/**\n * Classifies any thrown value from its code (and, for a few codes, its HTTP status), never from\n * message text, so an app does not have to re-derive \"may I offer Retry?\" and \"is the outcome\n * unknown?\" for every code. The decision for each code is in this module's table; a new catalog\n * code cannot be added without one.\n *\n * A failure BEFORE the request left (`error.phase === 'pre_send'`, `isPreSendError`) can never be\n * `unknown` or `rejected`: it is `invalid_input` (the input has to change) or `not_sent` (nothing\n * was transmitted; a repeat is safe). Every app-supplied callback the SDK calls before it sends\n * (the signer, the key provider, a token resolver) has its failure wrapped into such an error\n * (`SIGNING_FAILED`, `KEY_PROVIDER_FAILED`, `TOKEN_RESOLUTION_FAILED`, the original as `cause`),\n * so an app NEVER needs an error class of its own to say \"nothing was sent\": a locked wallet,\n * a declined signature and a failed lookup all arrive as `not_sent`, retryable.\n *\n * It accepts a `GSwapSDKError` or a plain-object VIEW of one ({@link FailureView}: `{ code,\n * params?, status?, uniqueKey?, ... }`, the shape of the wrapper an app builds from an SDK error;\n * `toFailureView` reads any value), and decides from the same fields, so an app does not need to\n * keep the original error to ask. `{ afterQueued: true }` says the write is already queued (the\n * step being classified is a confirmation): `retryable` is then always `false`.\n *\n * Anything that is not an SDK error or such a view, or a view whose `code` the catalog does not\n * define, is `{ outcome: 'unknown', retryable: false }`: the SDK\n * cannot say what happened, and refusing Retry is the safe answer. That is only what escapes AFTER\n * the request left (an app requestor that rejects with its own error, a callback of yours that\n * throws once bytes were sent). An error your own code raises BEFORE it calls the SDK says so\n * itself: give it a code of its own and `outcome` (`{ code: 'POSITION_STALE', outcome: 'not_sent'\n * }`), and the SDK answers with that outcome's classification and `mayHaveBeenQueued` is `false`.\n * The declared `outcome` is read only for a code the catalog does not define.\n *\n * A failure is a WRITE's unless it states `write: false` (`GSwapSDKError.write`, set for every\n * SDK read): see {@link FailureView}. A view that lost its `uniqueKey` is therefore still judged as\n * a write (a 5xx stays `unknown`), never as a retryable read.\n *\n * To learn that a failure came from the dark (shadow) path, read `error.shadow` (a typed field of\n * every `GSwapSDKError`) or call `isShadowError(error)`, which also covers a non-SDK object that\n * escaped `swapShadow` / `submitSwapShadow` / `ChainGateway.submitShadow`. The classification is\n * the same as on the public path; an app that withholds Retry or a public fallback after a dark\n * swap applies that policy on top of `retryable`.\n *\n * @example\n * ```ts\n * try {\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * } catch (error) {\n * const { outcome, retryable } = classifyFailure(error);\n * if (outcome === 'unknown') keepPendingAndConfirm(error); // never a second swap\n * else if (retryable) offerRetry(); // a new request under a fresh key\n * }\n * ```\n */\nexport function classifyFailure(\n error: unknown,\n options: ClassifyFailureOptions = {},\n): FailureClassification {\n const result = classifyView(toFailureView(error));\n // A `rejected` outcome is the chain's definite no: the queued write was evaluated and NOT\n // applied, so a new request under a fresh key cannot double-apply it and Retry stays as it is.\n return options.afterQueued === true && result.outcome !== 'rejected'\n ? { ...result, retryable: false }\n : result;\n}\n\n/** Options of {@link classifyFailure}. */\nexport interface ClassifyFailureOptions {\n /**\n * The write is ALREADY queued (the gateway acknowledged it with this `uniqueKey`), and what is\n * being classified is a later step: confirming it, reading its apply result. Whatever the\n * outcome, `retryable` is then `false`: a NEW request would be a second trade, so the app\n * confirms the same key (or forgets it) and never offers \"Try again\". `outcome` is unchanged.\n *\n * The one exception is `rejected`: the chain evaluated the queued write and refused it (an\n * apply result of `Status: 0`, `CHAIN_REJECTED`; a 422 dispatch). That PROVES it was not\n * applied, so a new request under a fresh key cannot make a second trade, and `retryable`\n * keeps the answer it has without this option (`true`). Every outcome that does not prove\n * \"not applied\" (`unknown`, `transient`, `not_queued`, ...) is `false`.\n */\n afterQueued?: boolean | undefined;\n}\n\n/**\n * The classification of a failure view: the table decision for its code, the status-dependent\n * decision for the codes whose meaning needs it, and for a code the catalog does not define\n * (a wallet error, an ethers `ACTION_REJECTED`, a typo, an app's own error): `unknown` unless the\n * view DECLARES its `outcome` (an app's error, `{ code: 'POSITION_STALE', outcome: 'not_sent' }`),\n * then the SDK's own classification of that outcome. A value with no view is `unknown`.\n */\nfunction classifyView(view: FailureView | undefined): FailureClassification {\n if (view === undefined) return { ...UNKNOWN };\n if (!isCatalogCode(view.code)) {\n if (view.outcome === undefined) return { ...UNKNOWN };\n // Withhold-only: `retryable: false` removes Retry from the declared outcome, never adds it.\n return view.retryable === false\n ? classification(view.outcome, { retryable: false })\n : classification(view.outcome);\n }\n const { code } = view;\n // A core gateway HTTP failure without a chain envelope reads exactly like HTTP_ERROR (by its\n // status, and read vs write); so does a GalaConnect one, except a write's 409.\n if (code === 'HTTP_ERROR' || code === 'GATEWAY_HTTP_ERROR') {\n return { ...classifyHttpError(view) };\n }\n if (code === 'GALACONNECT_HTTP_ERROR') return { ...classifyGalaConnectHttpError(view) };\n // GalaChain's refusal of a core gateway or GalaConnect bridge write: by the chain's code.\n if (code === 'GATEWAY_WRITE_REJECTED' || code === 'BRIDGE_REQUEST_REJECTED') {\n return { ...classifyGatewayWriteRejected(view) };\n }\n if (code === 'UPDATE_REQUIRED') return { ...classifyUpdateRequired(view) };\n if (code === 'CHAIN_DISPATCH_FAILED') return { ...classifyChainDispatchFailed(view) };\n if (code === 'INVALID_PRIVATE_KEY') return { ...classifyInvalidPrivateKey(view) };\n const row: FailureClassification | undefined = (\n BY_CODE as Readonly<Partial<Record<GSwapErrorCode, FailureClassification>>>\n )[code];\n return { ...(row ?? INVALID_INPUT) };\n}\n\n/**\n * True when the failed call was a WRITE that may nevertheless have been queued: its outcome is\n * `unknown` (`classifyFailure(error).outcome === 'unknown'`: a timeout or broken connection after\n * the request left, an acknowledgement that proves nothing, a gateway 409 or 5xx, a failed\n * confirmation). The app must keep `error.uniqueKey` and confirm it, and must NOT submit again\n * under a fresh key. Every other outcome (`rejected`, `not_queued`, `not_sent`, `invalid_input`,\n * `transient`) proves the write is not pending.\n *\n * A value the SDK cannot judge (an app requestor's own rejection after the request left) is\n * `unknown` too, so it is `true`; handle errors your own code raised BEFORE calling the SDK first.\n * Meant for the catch of a write; a read never has a pending key to keep.\n *\n * @example\n * ```ts\n * try {\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * } catch (error) {\n * if (mayHaveBeenQueued(error)) keepPendingAndConfirm(error);\n * }\n * ```\n */\nexport function mayHaveBeenQueued(error: unknown): boolean {\n return classifyFailure(error).outcome === 'unknown';\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -5913,8 +5913,8 @@ const BY_CODE = {
5913
5913
  METHOD_NOT_ALLOWED: NOT_QUEUED,
5914
5914
  SIGNATURE_INVALID: NOT_QUEUED,
5915
5915
  SIGNER_MISMATCH: NOT_QUEUED,
5916
- DTO_INVALID: NOT_QUEUED,
5917
- DTO_OPERATION_MISMATCH: NOT_QUEUED,
5916
+ DTO_INVALID: NOT_QUEUED_FINAL,
5917
+ DTO_OPERATION_MISMATCH: NOT_QUEUED_FINAL,
5918
5918
  BOUNDS_VIOLATION: NOT_QUEUED,
5919
5919
  SYMBOL_CONFLICT: NOT_QUEUED,
5920
5920
  RATE_LIMITED: NOT_QUEUED,