@weftlabs/sdk 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1889,6 +1889,9 @@ var FetchErrorResponseErrorEnum = {
1889
1889
  UnknownParameter: "UNKNOWN_PARAMETER",
1890
1890
  InvalidUrl: "INVALID_URL",
1891
1891
  InvalidMaxCostUsd: "INVALID_MAX_COST_USD",
1892
+ InvalidAllowTempoRefill: "INVALID_ALLOW_TEMPO_REFILL",
1893
+ InvalidMaxTotalCostUsd: "INVALID_MAX_TOTAL_COST_USD",
1894
+ IncompatibleFetchControls: "INCOMPATIBLE_FETCH_CONTROLS",
1892
1895
  UnsupportedMethod: "UNSUPPORTED_METHOD",
1893
1896
  InvalidBody: "INVALID_BODY",
1894
1897
  InvalidHeaders: "INVALID_HEADERS",
@@ -1896,6 +1899,7 @@ var FetchErrorResponseErrorEnum = {
1896
1899
  IdempotencyConflict: "IDEMPOTENCY_CONFLICT",
1897
1900
  AccountClosing: "ACCOUNT_CLOSING",
1898
1901
  ExceededMaxCost: "EXCEEDED_MAX_COST",
1902
+ TotalCostUnverifiable: "TOTAL_COST_UNVERIFIABLE",
1899
1903
  FundingPending: "FUNDING_PENDING",
1900
1904
  InsufficientBalance: "INSUFFICIENT_BALANCE",
1901
1905
  PaymentAuthorizationRequired: "PAYMENT_AUTHORIZATION_REQUIRED",
@@ -2080,6 +2084,8 @@ function FetchRequestFromJSONTyped(json, ignoreDiscriminator) {
2080
2084
  return {
2081
2085
  "url": json["url"],
2082
2086
  "maxCostUsd": json["max_cost_usd"] == null ? void 0 : json["max_cost_usd"],
2087
+ "allowTempoRefill": json["allow_tempo_refill"] == null ? void 0 : json["allow_tempo_refill"],
2088
+ "maxTotalCostUsd": json["max_total_cost_usd"] == null ? void 0 : json["max_total_cost_usd"],
2083
2089
  "method": json["method"] == null ? void 0 : json["method"],
2084
2090
  "body": json["body"] == null ? void 0 : FetchRequestBodyFromJSON(json["body"]),
2085
2091
  "headers": json["headers"] == null ? void 0 : json["headers"],
@@ -2098,6 +2104,8 @@ function FetchRequestToJSONTyped(value, ignoreDiscriminator = false) {
2098
2104
  return {
2099
2105
  "url": value["url"],
2100
2106
  "max_cost_usd": value["maxCostUsd"],
2107
+ "allow_tempo_refill": value["allowTempoRefill"],
2108
+ "max_total_cost_usd": value["maxTotalCostUsd"],
2101
2109
  "method": value["method"],
2102
2110
  "body": FetchRequestBodyToJSON(value["body"]),
2103
2111
  "headers": value["headers"],
@@ -4602,7 +4610,7 @@ var DocumentationApi = class extends BaseAPI {
4602
4610
  // src/generated/apis/FetchApi.ts
4603
4611
  var FetchApi = class extends BaseAPI {
4604
4612
  /**
4605
- * Universal x402/MPP fetch proxy. The caller provides a target `url`, a hard `max_cost_usd` ceiling, and optional `method` / `body` / `headers`. Weft: 1. Issues the request. 2. On `402 Payment Required`, selects a supported x402 or MPP challenge. 3. Compares the asking price to `max_cost_usd` and the buyer\'s policy (`max_tx_usd`, daily/weekly limits). 4. If an eligible mainnet MPP challenge finds its Tempo token short, creates or adopts a buyer-owned Base-to-Tempo refill and returns `409 FUNDING_PENDING` with `details.reason=funding_active`. If an overlapping payment invalidates the balance observation before a bridge is created, the same retryable response carries `details.reason=balance_changed`. The caller retries the identical fetch after `details.retry_after_seconds`; that later request obtains a fresh challenge and reruns rail selection and all payment controls. 5. Authorizes payment from the buyer\'s wallet on the selected rail. 6. Replays the request with the protocol-specific payment credential. 7. Streams the upstream artifact back, base64-encoded under `body_base64`, with `paid_usd`, `held_usd`, `payment_status`, `tx_hash`, and `protocol`. `paid_usd` is \"0.00\" until the charge is CONFIRMED settled — a signed-but- unsettled hold (the common case for x402, which settles asynchronously) reports its amount in `held_usd` instead, never in `paid_usd`. Errors are structured with a stable `error` code, and each error response carries the buyer\'s `policy`, `balance`, and a `dashboard_url` so a CLI can render an actionable message without a second round-trip. Account-scoped: the bearer must be a buyer-scoped API key, an OAuth access token carrying `fetch`, or a claimed `wbt_*` bearer. **Forwarded headers:** the caller\'s `headers` are passed through to the upstream, except a denylist of hop-by-hop and Weft-internal headers (`host`, `authorization`, `cookie`, `proxy-authorization`, `x-forwarded-*`, `x-real-ip`, `x-payment`, `connection`, `upgrade`). Up to 32 headers, 4 KB of combined value bytes.
4613
+ * Universal x402/MPP fetch proxy. The caller provides a target `url`, a merchant-principal `max_cost_usd` ceiling, and optional `method` / `body` / `headers`. Weft: 1. Issues the request. 2. On `402 Payment Required`, selects a supported x402 or MPP challenge. 3. Compares the asking price to `max_cost_usd` and the buyer\'s policy (`max_tx_usd`, daily/weekly limits). 4. If refill is allowed and an eligible mainnet MPP challenge finds its Tempo token short, creates or adopts a buyer-owned Base-to-Tempo refill and returns `409 FUNDING_PENDING` with `details.reason=funding_active`. If an overlapping payment invalidates the balance observation before a bridge is created, the same retryable response carries `details.reason=balance_changed`. The caller retries the identical fetch after `details.retry_after_seconds`; that later request obtains a fresh challenge and reruns rail selection and all payment controls. 5. Authorizes payment from the buyer\'s wallet on the selected rail. 6. Replays the request with the protocol-specific payment credential. 7. Streams the upstream artifact back, base64-encoded under `body_base64`, with `paid_usd`, `held_usd`, `payment_status`, `tx_hash`, and `protocol`. `paid_usd` is \"0.00\" until the charge is CONFIRMED settled — a signed-but- unsettled hold (the common case for x402, which settles asynchronously) reports its amount in `held_usd` instead, never in `paid_usd`. **Opt-in safety controls:** `allow_tempo_refill: false` prevents this request from creating, adopting, or enqueueing a Base-to-Tempo bridge. A Tempo shortfall returns `402 INSUFFICIENT_BALANCE`; unrelated bridges and their jobs remain unchanged. `access_method_id` is advisory and does not enforce a payment rail. `max_total_cost_usd` is an all-in ceiling covering merchant principal, network gas, provider fees, and prerequisite buyer-paid operations. It disables refill; explicitly combining it with `allow_tempo_refill: true` returns `422 INCOMPATIBLE_FETCH_CONTROLS`. Current wallet integrations provide no supported binding all-in fee guarantee. Therefore all-in requests return `402 TOTAL_COST_UNVERIFIABLE` before payment preparation, approval, reservation, or refill. This also refuses recovery of older payments and SIWX wallet signing; it admits no positive paid route. Increasing the ceiling does not resolve missing fee authority. Omitted controls preserve existing behavior. Ordinary non-402 merchant responses retain `MERCHANT_RETURNED_NON_402`. Stored-result replay is historical payment information, not a new bounded authorization. Errors are structured with a stable `error` code, and each error response carries the buyer\'s `policy`, `balance`, and a `dashboard_url` so a CLI can render an actionable message without a second round-trip. Account-scoped: the bearer must be a buyer-scoped API key, an OAuth access token carrying `fetch`, or a claimed `wbt_*` bearer. **Forwarded headers:** the caller\'s `headers` are passed through to the upstream, except a denylist of hop-by-hop and Weft-internal headers (`host`, `authorization`, `cookie`, `proxy-authorization`, `x-forwarded-*`, `x-real-ip`, `x-payment`, `connection`, `upgrade`). Up to 32 headers, 4 KB of combined value bytes.
4606
4614
  * Pay-and-fetch any URL (x402/MPP proxy)
4607
4615
  */
4608
4616
  async fetchRaw(requestParameters, initOverrides) {
@@ -4636,7 +4644,7 @@ var FetchApi = class extends BaseAPI {
4636
4644
  return new JSONApiResponse(response, (jsonValue) => FetchResponseFromJSON(jsonValue));
4637
4645
  }
4638
4646
  /**
4639
- * Universal x402/MPP fetch proxy. The caller provides a target `url`, a hard `max_cost_usd` ceiling, and optional `method` / `body` / `headers`. Weft: 1. Issues the request. 2. On `402 Payment Required`, selects a supported x402 or MPP challenge. 3. Compares the asking price to `max_cost_usd` and the buyer\'s policy (`max_tx_usd`, daily/weekly limits). 4. If an eligible mainnet MPP challenge finds its Tempo token short, creates or adopts a buyer-owned Base-to-Tempo refill and returns `409 FUNDING_PENDING` with `details.reason=funding_active`. If an overlapping payment invalidates the balance observation before a bridge is created, the same retryable response carries `details.reason=balance_changed`. The caller retries the identical fetch after `details.retry_after_seconds`; that later request obtains a fresh challenge and reruns rail selection and all payment controls. 5. Authorizes payment from the buyer\'s wallet on the selected rail. 6. Replays the request with the protocol-specific payment credential. 7. Streams the upstream artifact back, base64-encoded under `body_base64`, with `paid_usd`, `held_usd`, `payment_status`, `tx_hash`, and `protocol`. `paid_usd` is \"0.00\" until the charge is CONFIRMED settled — a signed-but- unsettled hold (the common case for x402, which settles asynchronously) reports its amount in `held_usd` instead, never in `paid_usd`. Errors are structured with a stable `error` code, and each error response carries the buyer\'s `policy`, `balance`, and a `dashboard_url` so a CLI can render an actionable message without a second round-trip. Account-scoped: the bearer must be a buyer-scoped API key, an OAuth access token carrying `fetch`, or a claimed `wbt_*` bearer. **Forwarded headers:** the caller\'s `headers` are passed through to the upstream, except a denylist of hop-by-hop and Weft-internal headers (`host`, `authorization`, `cookie`, `proxy-authorization`, `x-forwarded-*`, `x-real-ip`, `x-payment`, `connection`, `upgrade`). Up to 32 headers, 4 KB of combined value bytes.
4647
+ * Universal x402/MPP fetch proxy. The caller provides a target `url`, a merchant-principal `max_cost_usd` ceiling, and optional `method` / `body` / `headers`. Weft: 1. Issues the request. 2. On `402 Payment Required`, selects a supported x402 or MPP challenge. 3. Compares the asking price to `max_cost_usd` and the buyer\'s policy (`max_tx_usd`, daily/weekly limits). 4. If refill is allowed and an eligible mainnet MPP challenge finds its Tempo token short, creates or adopts a buyer-owned Base-to-Tempo refill and returns `409 FUNDING_PENDING` with `details.reason=funding_active`. If an overlapping payment invalidates the balance observation before a bridge is created, the same retryable response carries `details.reason=balance_changed`. The caller retries the identical fetch after `details.retry_after_seconds`; that later request obtains a fresh challenge and reruns rail selection and all payment controls. 5. Authorizes payment from the buyer\'s wallet on the selected rail. 6. Replays the request with the protocol-specific payment credential. 7. Streams the upstream artifact back, base64-encoded under `body_base64`, with `paid_usd`, `held_usd`, `payment_status`, `tx_hash`, and `protocol`. `paid_usd` is \"0.00\" until the charge is CONFIRMED settled — a signed-but- unsettled hold (the common case for x402, which settles asynchronously) reports its amount in `held_usd` instead, never in `paid_usd`. **Opt-in safety controls:** `allow_tempo_refill: false` prevents this request from creating, adopting, or enqueueing a Base-to-Tempo bridge. A Tempo shortfall returns `402 INSUFFICIENT_BALANCE`; unrelated bridges and their jobs remain unchanged. `access_method_id` is advisory and does not enforce a payment rail. `max_total_cost_usd` is an all-in ceiling covering merchant principal, network gas, provider fees, and prerequisite buyer-paid operations. It disables refill; explicitly combining it with `allow_tempo_refill: true` returns `422 INCOMPATIBLE_FETCH_CONTROLS`. Current wallet integrations provide no supported binding all-in fee guarantee. Therefore all-in requests return `402 TOTAL_COST_UNVERIFIABLE` before payment preparation, approval, reservation, or refill. This also refuses recovery of older payments and SIWX wallet signing; it admits no positive paid route. Increasing the ceiling does not resolve missing fee authority. Omitted controls preserve existing behavior. Ordinary non-402 merchant responses retain `MERCHANT_RETURNED_NON_402`. Stored-result replay is historical payment information, not a new bounded authorization. Errors are structured with a stable `error` code, and each error response carries the buyer\'s `policy`, `balance`, and a `dashboard_url` so a CLI can render an actionable message without a second round-trip. Account-scoped: the bearer must be a buyer-scoped API key, an OAuth access token carrying `fetch`, or a claimed `wbt_*` bearer. **Forwarded headers:** the caller\'s `headers` are passed through to the upstream, except a denylist of hop-by-hop and Weft-internal headers (`host`, `authorization`, `cookie`, `proxy-authorization`, `x-forwarded-*`, `x-real-ip`, `x-payment`, `connection`, `upgrade`). Up to 32 headers, 4 KB of combined value bytes.
4640
4648
  * Pay-and-fetch any URL (x402/MPP proxy)
4641
4649
  */
4642
4650
  async fetch(requestParameters, initOverrides) {
@@ -4877,6 +4885,26 @@ var SearchApi = class extends BaseAPI {
4877
4885
  };
4878
4886
 
4879
4887
  // src/error.ts
4888
+ var PRE_SIGN_FETCH_CODES = /* @__PURE__ */ new Set([
4889
+ "EXCEEDED_MAX_COST",
4890
+ "MERCHANT_RETURNED_NON_402",
4891
+ "INSUFFICIENT_BALANCE",
4892
+ "DENYLISTED_RECIPIENT",
4893
+ "WALLET_ENVIRONMENT_MISMATCH",
4894
+ "UNSUPPORTED_ASSET",
4895
+ "INVALID_REQUEST",
4896
+ "UNKNOWN_PARAMETER",
4897
+ "INVALID_URL",
4898
+ "INVALID_MAX_COST_USD",
4899
+ "UNSUPPORTED_METHOD",
4900
+ "INVALID_BODY",
4901
+ "INVALID_HEADERS",
4902
+ "INVALID_IDEMPOTENCY_KEY"
4903
+ ]);
4904
+ function fetchCharge(status, code) {
4905
+ const preSign = PRE_SIGN_FETCH_CODES.has(code) || code.startsWith("POLICY_VIOLATION_");
4906
+ return status >= 400 && status < 500 && preSign ? "none" : "possible";
4907
+ }
4880
4908
  var WeftError = class extends Error {
4881
4909
  constructor(options) {
4882
4910
  super(options.message);
@@ -4886,19 +4914,32 @@ var WeftError = class extends Error {
4886
4914
  this.requestId = options.requestId;
4887
4915
  this.retryable = options.retryable;
4888
4916
  this.details = options.details;
4917
+ this.charge = options.charge ?? "possible";
4889
4918
  }
4890
4919
  };
4891
- async function normalizeWeftError(error) {
4920
+ async function normalizeWeftError(error, paid = false) {
4892
4921
  if (error instanceof FetchError) {
4893
4922
  return new WeftError({
4894
4923
  status: 0,
4895
4924
  code: "NETWORK_ERROR",
4896
4925
  message: `Network failure before a Weft API response: ${error.cause?.message ?? error.message}`,
4897
4926
  retryable: true,
4898
- details: error.cause
4927
+ details: error.cause,
4928
+ charge: paid ? "possible" : "none"
4929
+ });
4930
+ }
4931
+ if (!(error instanceof ResponseError)) {
4932
+ if (!paid || error instanceof WeftError) return error;
4933
+ return new WeftError({
4934
+ status: 0,
4935
+ code: "RESPONSE_DECODE_ERROR",
4936
+ message: "Weft API returned a fetch response that could not be decoded",
4937
+ // A replay returns the same body; reconcile instead of retrying.
4938
+ retryable: false,
4939
+ details: error,
4940
+ charge: "possible"
4899
4941
  });
4900
4942
  }
4901
- if (!(error instanceof ResponseError)) return error;
4902
4943
  let details;
4903
4944
  try {
4904
4945
  details = await error.response.json();
@@ -4908,13 +4949,15 @@ async function normalizeWeftError(error) {
4908
4949
  const body = details;
4909
4950
  const nested = typeof body?.error === "object" ? body.error : void 0;
4910
4951
  const status = error.response.status;
4952
+ const code = nested?.code ?? body?.code ?? (typeof body?.error === "string" ? body.error : `HTTP_${status}`);
4911
4953
  return new WeftError({
4912
4954
  status,
4913
- code: nested?.code ?? body?.code ?? (typeof body?.error === "string" ? body.error : `HTTP_${status}`),
4955
+ code,
4914
4956
  message: nested?.message ?? body?.message ?? `Weft API returned HTTP ${status}`,
4915
4957
  requestId: nested?.request_id ?? body?.request_id ?? error.response.headers.get("x-request-id") ?? void 0,
4916
4958
  retryable: status === 429 || status >= 500,
4917
- details
4959
+ details,
4960
+ charge: paid ? fetchCharge(status, String(code)) : "none"
4918
4961
  });
4919
4962
  }
4920
4963
 
@@ -4952,11 +4995,11 @@ var WeftClient = class {
4952
4995
  this.fetches = new FetchApi(configuration);
4953
4996
  this.purchaseHistory = new PurchasesApi(configuration);
4954
4997
  }
4955
- async call(operation) {
4998
+ async call(operation, paid = false) {
4956
4999
  try {
4957
5000
  return await operation();
4958
5001
  } catch (error) {
4959
- throw await normalizeWeftError(error);
5002
+ throw await normalizeWeftError(error, paid);
4960
5003
  }
4961
5004
  }
4962
5005
  me() {
@@ -4975,12 +5018,27 @@ var WeftClient = class {
4975
5018
  if (!options.idempotencyKey.trim()) {
4976
5019
  throw new Error("idempotencyKey is required");
4977
5020
  }
5021
+ if (!/^[!-~]{1,255}$/.test(options.idempotencyKey)) {
5022
+ throw new Error(
5023
+ "idempotencyKey must be 1-255 visible ASCII characters without spaces"
5024
+ );
5025
+ }
5026
+ const fetchRequest = request.body != null && typeof request.body === "object" ? { ...request, body: JSON.stringify(request.body) } : request;
4978
5027
  return this.call(
4979
5028
  () => this.fetches.fetch({
4980
- fetchRequest: request,
5029
+ fetchRequest,
4981
5030
  idempotencyKey: options.idempotencyKey
4982
- })
4983
- );
5031
+ }),
5032
+ true
5033
+ ).then(async (response) => {
5034
+ if (typeof response?.status !== "number") {
5035
+ throw await normalizeWeftError(
5036
+ new Error("unreadable fetch response"),
5037
+ true
5038
+ );
5039
+ }
5040
+ return response;
5041
+ });
4984
5042
  }
4985
5043
  purchases(options = {}) {
4986
5044
  return this.call(() => this.purchaseHistory.listPurchases(options));