@izak0s/spacebring-api 1.1.0 → 1.2.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/README.md CHANGED
@@ -85,6 +85,8 @@ Values are passed through exactly as the API sends them — no runtime conversio
85
85
 
86
86
  HTTP Basic with your **Client ID** and **Client Secret** from **Spacebring → [Network] → Network Settings → Developers**. The client builds the `Authorization: Basic …` header for you. The API's OAuth2 flow is not currently supported.
87
87
 
88
+ For development without touching live data, Spacebring offers a [test environment](https://www.spacebring.com/docs/administration/test-environment) (Network settings → Billing add-on) with free sandbox API credentials that work with this client unchanged.
89
+
88
90
  ---
89
91
 
90
92
  ## Error handling
@@ -105,6 +107,10 @@ try {
105
107
 
106
108
  Malformed successes are covered too: a 2xx with an empty or incomplete body throws a `SpacebringError` (never a bare `TypeError`), and `iterate()` throws instead of looping forever if the API repeats a page token.
107
109
 
110
+ ### Rate limits
111
+
112
+ The API allows **10 requests per second**. Rate-limited requests (429) are retried automatically — up to 3 times, honoring `Retry-After` or backing off exponentially — so `iterate()` survives the limit out of the box. Tune or disable via `maxRetries` in the config (`maxRetries: 0` turns it off); a 429 that persists past the retries is thrown as a normal `SpacebringError`.
113
+
108
114
  ---
109
115
 
110
116
  ## Escape hatch
package/dist/index.cjs CHANGED
@@ -52,7 +52,7 @@ function createClient(clientOptions) {
52
52
  const {
53
53
  baseUrl: localBaseUrl,
54
54
  fetch = baseFetch,
55
- Request = CustomRequest,
55
+ Request: Request2 = CustomRequest,
56
56
  headers,
57
57
  params = {},
58
58
  parseAs = "json",
@@ -104,7 +104,7 @@ function createClient(clientOptions) {
104
104
  };
105
105
  let id;
106
106
  let options;
107
- let request = new Request(
107
+ let request = new Request2(
108
108
  createFinalURL(schemaPath, { baseUrl: finalBaseUrl, params, querySerializer, pathSerializer }),
109
109
  requestInit
110
110
  );
@@ -134,7 +134,7 @@ function createClient(clientOptions) {
134
134
  id
135
135
  });
136
136
  if (result) {
137
- if (result instanceof Request) {
137
+ if (result instanceof Request2) {
138
138
  request = result;
139
139
  } else if (result instanceof Response) {
140
140
  response = result;
@@ -1722,8 +1722,8 @@ function createSubscriptions(client, defaults) {
1722
1722
  *
1723
1723
  * Retrieve a certain subscription.
1724
1724
  */
1725
- async get(id) {
1726
- return unwrapProp(await client.GET("/subscriptions/v1/{id}", { params: { path: { id } } }), "subscription");
1725
+ async get(subscriptionId) {
1726
+ return unwrapProp(await client.GET("/subscriptions/v1/{subscriptionId}", { params: { path: { subscriptionId } } }), "subscription");
1727
1727
  },
1728
1728
  /**
1729
1729
  * Create a subscription
@@ -1738,28 +1738,28 @@ function createSubscriptions(client, defaults) {
1738
1738
  *
1739
1739
  * Update a certain subscription.
1740
1740
  */
1741
- async update(id, body) {
1742
- return unwrap(await client.PATCH("/subscriptions/v1/{id}", { params: { path: { id } }, body }));
1741
+ async update(subscriptionId, body) {
1742
+ return unwrap(await client.PATCH("/subscriptions/v1/{subscriptionId}", { params: { path: { subscriptionId } }, body }));
1743
1743
  },
1744
1744
  /**
1745
1745
  * Delete a subscription
1746
1746
  *
1747
1747
  * Delete a certain subscription.
1748
1748
  */
1749
- async delete(id) {
1750
- return unwrap(await client.DELETE("/subscriptions/v1/{id}", { params: { path: { id } } }));
1749
+ async delete(subscriptionId) {
1750
+ return unwrap(await client.DELETE("/subscriptions/v1/{subscriptionId}", { params: { path: { subscriptionId } } }));
1751
1751
  },
1752
1752
  /** Create a subscription item */
1753
- async createItem(id, body) {
1754
- return unwrapProp(await client.POST("/subscriptions/v1/{id}/items", { params: { path: { id } }, body }), "subscription");
1753
+ async createItem(subscriptionId, body) {
1754
+ return unwrapProp(await client.POST("/subscriptions/v1/{subscriptionId}/items", { params: { path: { subscriptionId } }, body }), "subscription");
1755
1755
  },
1756
1756
  /** Delete a subscription item */
1757
- async deleteItem(id, itemId) {
1758
- return unwrap(await client.DELETE("/subscriptions/v1/{id}/items/{itemId}", { params: { path: { id, itemId } } }));
1757
+ async deleteItem(subscriptionId, itemId) {
1758
+ return unwrap(await client.DELETE("/subscriptions/v1/{subscriptionId}/items/{itemId}", { params: { path: { subscriptionId, itemId } } }));
1759
1759
  },
1760
1760
  /** Update a subscription item */
1761
- async updateItem(id, itemId, body) {
1762
- return unwrap(await client.PATCH("/subscriptions/v1/{id}/items/{itemId}", { params: { path: { id, itemId } }, body }));
1761
+ async updateItem(subscriptionId, itemId, body) {
1762
+ return unwrap(await client.PATCH("/subscriptions/v1/{subscriptionId}/items/{itemId}", { params: { path: { subscriptionId, itemId } }, body }));
1763
1763
  }
1764
1764
  };
1765
1765
  }
@@ -2089,11 +2089,25 @@ var Spacebring = class {
2089
2089
  this.raw = createClient({
2090
2090
  baseUrl: config.baseUrl ?? "https://api.spacebring.com",
2091
2091
  headers,
2092
- fetch: config.fetch
2092
+ fetch: withRetry(config.fetch ?? globalThis.fetch, config.maxRetries ?? 3)
2093
2093
  });
2094
2094
  Object.assign(this, createResources2(this.raw, { networkId: config.networkId }));
2095
2095
  }
2096
2096
  };
2097
+ function withRetry(fetchImpl, maxRetries) {
2098
+ if (maxRetries <= 0) return fetchImpl;
2099
+ return async (input, init) => {
2100
+ const request = input instanceof Request && init === void 0 ? input : new Request(input, init);
2101
+ for (let attempt = 0; ; attempt += 1) {
2102
+ const response = await fetchImpl(request.clone());
2103
+ if (response.status !== 429 || attempt >= maxRetries) return response;
2104
+ const retryAfterHeader = response.headers.get("retry-after");
2105
+ const retryAfter = retryAfterHeader === null ? Number.NaN : Number(retryAfterHeader);
2106
+ const delayMs = Number.isFinite(retryAfter) && retryAfter >= 0 ? Math.min(retryAfter * 1e3, 6e4) : Math.min(250 * 2 ** attempt + Math.random() * 100, 5e3);
2107
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
2108
+ }
2109
+ };
2110
+ }
2097
2111
  function toBase64(value) {
2098
2112
  if (typeof Buffer !== "undefined") {
2099
2113
  return Buffer.from(value, "utf8").toString("base64");