@parseapi/sdk 1.7.0 → 1.9.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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- var VERSION = "1.7.0";
2
+ var VERSION = "1.9.0";
3
3
  var API_VERSION = "2.0.0";
4
4
  var DEFAULT_BASE_URL = "https://api.parseapi.com";
5
5
  var DEFAULT_TIMEOUT_MS = 1e4;
@@ -15,13 +15,16 @@ var ParseAPIError = class extends Error {
15
15
  docs;
16
16
  /** Send this if you contact support */
17
17
  requestId;
18
- constructor(status, code, message, docs, requestId) {
18
+ /** Original Retry-After response header, when supplied. */
19
+ retryAfter;
20
+ constructor(status, code, message, docs, requestId, retryAfter = null) {
19
21
  super(message);
20
22
  this.name = "ParseAPIError";
21
23
  this.status = status;
22
24
  this.code = code;
23
25
  this.docs = docs;
24
26
  this.requestId = requestId;
27
+ this.retryAfter = retryAfter;
25
28
  }
26
29
  };
27
30
  function tariffSelection(result, opts) {
@@ -30,6 +33,7 @@ function tariffSelection(result, opts) {
30
33
  }
31
34
  return result;
32
35
  }
36
+ var requestControls = (opts) => ({ signal: opts?.signal, timeoutMs: opts?.timeoutMs, retries: opts?.retries });
33
37
  function env(name) {
34
38
  return typeof process !== "undefined" ? process.env?.[name] : void 0;
35
39
  }
@@ -66,16 +70,20 @@ function metered(path, query) {
66
70
  }
67
71
  function retryDelayMs(attempt, retryAfter) {
68
72
  if (retryAfter) {
69
- const seconds = Number(retryAfter);
70
- if (Number.isFinite(seconds) && seconds >= 0) {
71
- return Math.min(seconds * 1e3, RETRY_AFTER_CAP_MS);
73
+ const value = retryAfter.trim();
74
+ if (/^[0-9]+(?:\.[0-9]+)?$/.test(value)) {
75
+ const seconds = Number(value);
76
+ return seconds > RETRY_AFTER_CAP_MS / 1e3 ? null : seconds * 1e3;
72
77
  }
73
- if (Number.isNaN(seconds)) {
74
- const date = Date.parse(retryAfter);
75
- if (Number.isFinite(date)) return Math.min(Math.max(date - Date.now(), 0), RETRY_AFTER_CAP_MS);
78
+ if (/^[A-Za-z]{3,9},? /.test(value)) {
79
+ const date = Date.parse(value);
80
+ if (Number.isFinite(date)) {
81
+ const delay = Math.max(date - Date.now(), 0);
82
+ return delay > RETRY_AFTER_CAP_MS ? null : delay;
83
+ }
76
84
  }
77
85
  }
78
- return Math.random() * 250 * 2 ** attempt;
86
+ return Math.random() * Math.min(250 * 2 ** Math.min(attempt, 16), RETRY_AFTER_CAP_MS);
79
87
  }
80
88
  function parseAPI(apiKey, options = {}) {
81
89
  const key = apiKey || env("PARSEAPI_KEY");
@@ -108,7 +116,7 @@ function parseAPI(apiKey, options = {}) {
108
116
  const onAbort = () => controller.abort(signal.reason);
109
117
  signal?.addEventListener("abort", onAbort, { once: true });
110
118
  const timer = setTimeout(() => controller.abort(new DOMException("Request timed out", "TimeoutError")), attemptTimeout);
111
- let retryAfter = null;
119
+ let retryDelay;
112
120
  try {
113
121
  let res;
114
122
  try {
@@ -134,8 +142,8 @@ function parseAPI(apiKey, options = {}) {
134
142
  res = void 0;
135
143
  }
136
144
  }
137
- if (res && RETRY_STATUS.has(res.status) && attempt < retries) {
138
- retryAfter = res.headers.get("Retry-After");
145
+ if (res) retryDelay = retryDelayMs(attempt, res.headers.get("Retry-After"));
146
+ if (res && RETRY_STATUS.has(res.status) && attempt < retries && retryDelay !== null) {
139
147
  await res.body?.cancel().catch(() => {
140
148
  });
141
149
  } else if (res) {
@@ -152,14 +160,15 @@ function parseAPI(apiKey, options = {}) {
152
160
  typeof body2.code === "string" ? body2.code : "unknown_error",
153
161
  typeof body2.message === "string" ? body2.message : `Request failed with status ${res.status}`,
154
162
  typeof body2.docs === "string" ? body2.docs : null,
155
- typeof body2.request_id === "string" ? body2.request_id : null
163
+ typeof body2.request_id === "string" ? body2.request_id : null,
164
+ res.headers.get("Retry-After")
156
165
  );
157
166
  }
158
167
  } finally {
159
168
  clearTimeout(timer);
160
169
  signal?.removeEventListener("abort", onAbort);
161
170
  }
162
- await sleep(retryDelayMs(attempt, retryAfter), signal);
171
+ await sleep(retryDelay ?? retryDelayMs(attempt, null), signal);
163
172
  }
164
173
  }
165
174
  const enc = encodeURIComponent;
@@ -173,6 +182,12 @@ function parseAPI(apiKey, options = {}) {
173
182
  for (const [name, value2] of Object.entries(query)) url.searchParams.set(name, String(value2));
174
183
  return !numericJson && url.href.length <= 8e3 ? request("/elevation", query, void 0, opts) : request("/elevation", void 0, void 0, opts, { [selector]: coordinates, ...sampling });
175
184
  }
185
+ const industry = Object.assign(
186
+ (code, opts) => request(`/industry/${enc(code)}`, deepQuery(opts), void 0, opts),
187
+ {
188
+ search: (query, opts) => request("/industry", { q: query, limit: opts?.limit, ...deepQuery(opts) }, void 0, opts)
189
+ }
190
+ );
176
191
  return {
177
192
  /** Estimate task access, capacity and additional charges. Requires a secret key. Reserves no units or money and does not enforce the supplied budget. */
178
193
  preflight: (task, opts) => request("/preflight", void 0, void 0, opts, task),
@@ -245,7 +260,17 @@ function parseAPI(apiKey, options = {}) {
245
260
  search: (query, opts) => request("/address", { q: query, country: opts?.country, postal: opts?.postal, city: opts?.city, state: opts?.state, ip: opts?.ip }, void 0, opts)
246
261
  }
247
262
  ),
248
- company: (number, opts) => request(`/company/${enc(number)}`, { country: opts?.country, ...deepQuery(opts) }, void 0, opts),
263
+ company: Object.assign(
264
+ (number, opts) => request(`/company/${enc(number)}`, { country: opts?.country, ...deepQuery(opts) }, void 0, opts),
265
+ {
266
+ /** Look up a stable directory ID. Requested deep belongs to this profile. */
267
+ id: (id, opts) => request(`/company/id/${enc(id)}`, deepQuery(opts), void 0, requestControls(opts)),
268
+ /** Return candidates without selecting a match. Use one selector, country, exact industry, filters. */
269
+ search: (opts) => request("/company", { q: opts.query, domain: opts.domain, ticker: opts.ticker, identifier: opts.identifier, country: opts.country, industry: opts.industry, industry_type: opts.industry_type, exchange: opts.exchange, authority: opts.authority, limit: opts.limit, cursor: opts.cursor, ...deepQuery(opts) }, void 0, requestControls(opts)),
270
+ /** Describe the directory edition and its counts. Counts do not establish complete country coverage. */
271
+ coverage: (opts) => request("/company/directory/coverage", void 0, void 0, requestControls(opts))
272
+ }
273
+ ),
249
274
  /**
250
275
  * Parse an email and check its format and domain.
251
276
  * `deep: true` explicitly requests a metered deliverability check using included checks or enabled on-demand usage.
@@ -258,8 +283,16 @@ function parseAPI(apiKey, options = {}) {
258
283
  from: opts?.from,
259
284
  ...deepQuery(opts)
260
285
  }, void 0, opts),
286
+ // Keep existing callers on their established HTTP contracts.
261
287
  iban: (iban, opts) => request(`/iban/${enc(iban)}`, { country: opts?.country, ...deepQuery(opts) }, void 0, opts),
288
+ bin: (bin, opts) => request(`/bin/${enc(bin)}`, { deep: opts?.deep }, void 0, opts),
262
289
  npi: (npi, opts) => request(`/npi/${enc(npi)}`, deepQuery(opts), void 0, opts),
290
+ bank: (iban, opts) => request("/bank", void 0, void 0, opts, { iban, country: opts?.country, deep: opts?.deep }),
291
+ /** Check US routing/account syntax using POST. Does not verify ownership, existence or ACH eligibility. */
292
+ bankUsAch: (input, opts) => request("/bank", void 0, void 0, opts, { format: "us_ach", country: "US", routing: input.routing, account: input.account }),
293
+ /** Describe supported input fields and check scope; this does not establish country directory coverage. */
294
+ bankRequirements: (country, opts) => request("/bank/requirements", { country, format: opts?.format }, void 0, opts),
295
+ provider: (npi, opts) => request(`/provider/${enc(npi)}`, deepQuery(opts), void 0, opts),
263
296
  /** Parse a phone number and its formats. Pass country for national numbers when needed. Deep adds numbering-plan geography. */
264
297
  phone: (number, opts) => request(`/phone/${enc(number)}`, { country: opts?.country, ...deepQuery(opts) }, void 0, opts),
265
298
  /** Request a metered carrier lookup. No automatic retries by default. */
@@ -274,8 +307,13 @@ function parseAPI(apiKey, options = {}) {
274
307
  domain: (domain, opts) => request(`/domain/${enc(domain)}`, deepQuery(opts), void 0, opts),
275
308
  asn: (asn, opts) => request(`/asn/${enc(asn)}`, void 0, void 0, opts),
276
309
  mac: (mac, opts) => request(`/mac/${enc(mac)}`, void 0, void 0, opts),
277
- /** Look up a 6-11 digit card prefix. Keep leading zeros in the input string. */
278
- bin: (bin, opts) => request(`/bin/${enc(bin)}`, { deep: opts?.deep }, void 0, opts),
310
+ /** Look up a 2-11 digit card prefix. Keep leading zeros in the input string. */
311
+ card: async (bin, opts) => {
312
+ if (typeof bin !== "string" || bin.length > 64 || !/^[0-9]{2,11}$/.test(bin.replace(/[ \t\r\n-]/g, ""))) {
313
+ throw new TypeError("parseAPI: Card requires a string containing 2 to 11 digits. Send a prefix only.");
314
+ }
315
+ return request(`/card/${enc(bin)}`, deepQuery(opts), void 0, opts);
316
+ },
279
317
  /** Parse a measurement or convert it to `to`. Without `to`, use its type's canonical unit. Invalid input is plain data with `valid: false`. */
280
318
  measure: Object.assign(
281
319
  (measure, opts) => request(`/measure/${enc(measure)}`, { to: opts?.to, locale: opts?.locale, system: opts?.system }, void 0, opts),
@@ -288,14 +326,14 @@ function parseAPI(apiKey, options = {}) {
288
326
  dns: (domain, opts) => request(`/dns/${enc(domain)}`, { type: opts?.type }, void 0, opts),
289
327
  mx: (domain, opts) => request(`/mx/${enc(domain)}`, void 0, void 0, opts),
290
328
  useragent: (ua, opts) => request("/useragent", deepQuery(opts), { "User-Agent": ua }, opts),
329
+ /** Identify a vehicle by VIN. Paid deep adds specifications and model-level recall campaigns. */
330
+ vehicle: (vin, opts) => request(`/vehicle/${enc(vin)}`, deepQuery(opts), void 0, opts),
331
+ /** Compatibility entry for VIN callers. */
291
332
  vin: (vin, opts) => request(`/vin/${enc(vin)}`, deepQuery(opts), void 0, opts),
292
333
  /** US NAICS 2022 definitions and hierarchy. */
293
- naics: Object.assign(
294
- (code, opts) => request(`/naics/${enc(code)}`, deepQuery(opts), void 0, opts),
295
- {
296
- search: (query, opts) => request("/naics", { q: query, limit: opts?.limit, ...deepQuery(opts) }, void 0, opts)
297
- }
298
- ),
334
+ industry,
335
+ /** Compatibility name for industry. */
336
+ naics: industry,
299
337
  /** Look up the general US duty schedule line. Paid deep adds units and the special and other schedule columns. Add origin with deep to resolve country-specific measures. Without origin, schedule detail remains available and origin-dependent fields are null. A null effective rate is not a zero rate. */
300
338
  tariff: Object.assign(
301
339
  (code, opts) => request(`/tariff/${enc(code)}`, { origin: opts?.origin, edition: opts?.edition, date: opts?.date, ...deepQuery(opts) }, void 0, opts).then((result) => tariffSelection(result, opts)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parseapi/sdk",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Official ParseAPI client for Node and TypeScript. One key, minimal JSON, fast.",
5
5
  "keywords": [
6
6
  "parseapi",