@getanyapi/sdk 0.22.0 → 0.23.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
@@ -79,6 +79,12 @@ billing. This handwritten discovery client safety-scans the response, projects k
79
79
  preserves schemas as opaque JSON, and ignores safe additions. It does not recompute gateway
80
80
  business rules. Generated per-SKU methods remain a separate OpenAPI-driven surface.
81
81
 
82
+ Every catalog and search result carries the gateway-authored `method`, `path`, and execution
83
+ mode. Lanes carry their public source identity and complete health sample counts when health is
84
+ available. Eligible APIs may carry `tryMaxItems`; ranked search carries the gateway's failover
85
+ facts. `describe` also returns `latency`, either the complete trailing-window p50/p95/p99
86
+ distribution or `null` when no sample is available.
87
+
82
88
  ## Pagination
83
89
 
84
90
  Paginated SKUs expose an iterator that yields items across pages and follows the cursor for
@@ -162,13 +168,13 @@ No other `409` retries: `idempotency_conflict` (the same key with different inpu
162
168
  Automatic network retry of a billed `run()` requires structured runtime evidence that the
163
169
  request body was not sent:
164
170
 
165
- | Runtime | Automatic billed-run network retry | Evidence available to the SDK |
166
- | ------- | ---------------------------------- | ----------------------------- |
167
- | Node 18+ with built-in undici `fetch` | Yes | DNS and connect codes, connect-phase timeouts, or an undici socket reporting zero bytes written |
168
- | Bun 1.3.11 | Yes | `ConnectionRefused`, which Bun 1.3.11 emits only while establishing the origin or proxy connection |
169
- | Cloudflare Workers | No | `retryable: true` means transient, not undelivered; it can appear after the origin received the full body |
170
- | Deno | No | Fetch exposes only prose without a structured connection code |
171
- | Browsers | No | Fetch generally exposes an opaque `TypeError` |
171
+ | Runtime | Automatic billed-run network retry | Evidence available to the SDK |
172
+ | ------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------- |
173
+ | Node 18+ with built-in undici `fetch` | Yes | DNS and connect codes, connect-phase timeouts, or an undici socket reporting zero bytes written |
174
+ | Bun 1.3.11 | Yes | `ConnectionRefused`, which Bun 1.3.11 emits only while establishing the origin or proxy connection |
175
+ | Cloudflare Workers | No | `retryable: true` means transient, not undelivered; it can appear after the origin received the full body |
176
+ | Deno | No | Fetch exposes only prose without a structured connection code |
177
+ | Browsers | No | Fetch generally exposes an opaque `TypeError` |
172
178
 
173
179
  On a runtime without strict non-delivery evidence, the SDK makes no automatic network retry for a
174
180
  billed `run()`. HTTP 429 retry is unchanged. Handle other retries explicitly only when your
package/dist/index.cjs CHANGED
@@ -232,23 +232,92 @@ function pageIdempotencyKey(key, pageNumber) {
232
232
 
233
233
  // src/core/account.ts
234
234
  var DEFAULT_BASE_URL = "https://api.getanyapi.com";
235
+ function mapProfile(raw) {
236
+ const profile = {
237
+ id: raw.id,
238
+ status: raw.status,
239
+ createdAt: raw.createdAt,
240
+ onboardingComplete: raw.onboardingComplete
241
+ };
242
+ if (raw.email !== void 0 && raw.email !== null) {
243
+ profile.email = raw.email;
244
+ }
245
+ return profile;
246
+ }
247
+ async function agentSignup(options = {}) {
248
+ const fetchImpl = options.fetch ?? globalThis.fetch;
249
+ if (typeof fetchImpl !== "function") {
250
+ throw new AnyAPIError(
251
+ "no fetch implementation available: pass options.fetch or run on a runtime with global fetch",
252
+ 0
253
+ );
254
+ }
255
+ const base = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
256
+ const body = {};
257
+ if (options.sponsorEmail !== void 0) {
258
+ body["sponsorEmail"] = options.sponsorEmail;
259
+ }
260
+ if (options.label !== void 0) {
261
+ body["label"] = options.label;
262
+ }
263
+ let response;
264
+ try {
265
+ response = await fetchImpl(`${base}/agent/signup`, {
266
+ method: "POST",
267
+ headers: {
268
+ "Content-Type": "application/json",
269
+ Accept: "application/json"
270
+ },
271
+ body: JSON.stringify(body)
272
+ });
273
+ } catch (err) {
274
+ throw new ConnectionError(
275
+ err instanceof Error ? err.message : "connection failed",
276
+ 0
277
+ );
278
+ }
279
+ const requestId = requestIdOf(response.headers);
280
+ const text = await response.text().catch(() => "");
281
+ if (response.status !== 200) {
282
+ let message = `request failed with status ${response.status}`;
283
+ let code;
284
+ try {
285
+ const parsed2 = JSON.parse(text);
286
+ if (typeof parsed2.error === "string" && parsed2.error !== "") {
287
+ message = parsed2.error;
288
+ }
289
+ if (typeof parsed2.code === "string" && parsed2.code !== "") {
290
+ code = parsed2.code;
291
+ }
292
+ } catch {
293
+ }
294
+ throw errorFromStatus(response.status, message, requestId, code);
295
+ }
296
+ const parsed = JSON.parse(text);
297
+ return {
298
+ secret: parsed.secret,
299
+ capUsd: parsed.capUsd,
300
+ claimToken: parsed.claimToken,
301
+ claimUrl: parsed.claimUrl
302
+ };
303
+ }
304
+
305
+ // src/core/discovery-validation.ts
235
306
  function malformed(path) {
236
307
  throw new AnyAPIError(`malformed discovery response: ${path}`, 0);
237
308
  }
238
- function rejectUnsafeDiscoveryFields(value, path) {
309
+ function rejectUnsafeFields(value, path) {
239
310
  if (Array.isArray(value)) {
240
311
  value.forEach(
241
- (item, index) => rejectUnsafeDiscoveryFields(item, `${path}[${index}]`)
312
+ (item, index) => rejectUnsafeFields(item, `${path}[${index}]`)
242
313
  );
243
314
  return;
244
315
  }
245
316
  if (typeof value !== "object" || value === null) return;
246
317
  for (const [key, item] of Object.entries(value)) {
247
318
  if (key.toLowerCase().includes("credit")) malformed(`${path}.${key}`);
248
- if (key === "provider" && item !== "AnyAPI") {
249
- malformed(`${path}.${key}`);
250
- }
251
- rejectUnsafeDiscoveryFields(item, `${path}.${key}`);
319
+ if (key === "provider" && item !== "AnyAPI") malformed(`${path}.${key}`);
320
+ rejectUnsafeFields(item, `${path}.${key}`);
252
321
  }
253
322
  }
254
323
  function record(value, path) {
@@ -259,8 +328,7 @@ function record(value, path) {
259
328
  }
260
329
  function stringField(raw, key, path) {
261
330
  const value = raw[key];
262
- if (typeof value !== "string") return malformed(`${path}.${key}`);
263
- return value;
331
+ return typeof value === "string" ? value : malformed(`${path}.${key}`);
264
332
  }
265
333
  function numberField(raw, key, path) {
266
334
  const value = raw[key];
@@ -271,7 +339,16 @@ function numberField(raw, key, path) {
271
339
  }
272
340
  function integerField(raw, key, path) {
273
341
  const value = numberField(raw, key, path);
274
- if (!Number.isInteger(value)) return malformed(`${path}.${key}`);
342
+ return Number.isInteger(value) ? value : malformed(`${path}.${key}`);
343
+ }
344
+ function methodField(raw, key, path) {
345
+ return stringField(raw, key, path) === "POST" ? "POST" : malformed(`${path}.${key}`);
346
+ }
347
+ function pathField(raw, key, path) {
348
+ const value = stringField(raw, key, path);
349
+ if (value.length < 2 || !value.startsWith("/") || value.startsWith("//")) {
350
+ return malformed(`${path}.${key}`);
351
+ }
275
352
  return value;
276
353
  }
277
354
  function boundedNumberField(raw, key, path, minimumExclusive, maximumInclusive) {
@@ -287,22 +364,17 @@ function parseOffer(value, path) {
287
364
  const unit = stringField(raw, "unit", path);
288
365
  const maxUsd = numberField(raw, "maxUsd", path);
289
366
  if (model === "flat") {
290
- if (unit !== "request") {
291
- return malformed(path);
292
- }
293
- return { model, unit, maxUsd };
294
- }
295
- if (model === "linear") {
296
- if (unit.length === 0) return malformed(`${path}.unit`);
297
- return {
298
- model,
299
- unit,
300
- baseUsd: numberField(raw, "baseUsd", path),
301
- perUnitUsd: numberField(raw, "perUnitUsd", path),
302
- maxUsd
303
- };
367
+ return unit === "request" ? { model, unit, maxUsd } : malformed(path);
304
368
  }
305
- return malformed(`${path}.model`);
369
+ if (model !== "linear") return malformed(`${path}.model`);
370
+ if (unit.length === 0) return malformed(`${path}.unit`);
371
+ return {
372
+ model,
373
+ unit,
374
+ baseUsd: numberField(raw, "baseUsd", path),
375
+ perUnitUsd: numberField(raw, "perUnitUsd", path),
376
+ maxUsd
377
+ };
306
378
  }
307
379
  function parsePricing(value, path) {
308
380
  const raw = record(value, path);
@@ -311,31 +383,64 @@ function parsePricing(value, path) {
311
383
  failoverMaxUsd: numberField(raw, "failoverMaxUsd", path)
312
384
  };
313
385
  }
386
+ function parseExecution(value, path) {
387
+ const raw = record(value, path);
388
+ const mode = stringField(raw, "mode", path);
389
+ return mode === "sync" || mode === "durable" ? { mode } : malformed(`${path}.mode`);
390
+ }
391
+ function parseSource(value, path) {
392
+ const raw = record(value, path);
393
+ const kind = stringField(raw, "kind", path);
394
+ if (kind !== "anonymous" && kind !== "brand")
395
+ return malformed(`${path}.kind`);
396
+ return {
397
+ id: stringField(raw, "id", path),
398
+ name: stringField(raw, "name", path),
399
+ kind,
400
+ artworkKey: stringField(raw, "artworkKey", path)
401
+ };
402
+ }
314
403
  function parseHealth(value, path) {
315
404
  const raw = record(value, path);
316
405
  return {
317
406
  window: stringField(raw, "window", path),
318
407
  uptimePct: boundedNumberField(raw, "uptimePct", path, void 0, 100),
319
408
  latencyP50Ms: integerField(raw, "latencyP50Ms", path),
320
- requests: integerField(raw, "requests", path)
409
+ uptimeSample: integerField(raw, "uptimeSample", path),
410
+ latencySample: integerField(raw, "latencySample", path),
411
+ requests: integerField(raw, "requests", path),
412
+ servedRequests: integerField(raw, "servedRequests", path)
321
413
  };
322
414
  }
323
415
  function parseLane(value, path) {
324
416
  const raw = record(value, path);
325
417
  const lane = {
326
- pricing: parseOffer(raw["pricing"], `${path}.pricing`)
418
+ pricing: parseOffer(raw["pricing"], `${path}.pricing`),
419
+ source: parseSource(raw["source"], `${path}.source`)
327
420
  };
328
- if (raw["health"] !== void 0) {
421
+ if (raw["health"] !== void 0)
329
422
  lane.health = parseHealth(raw["health"], `${path}.health`);
330
- }
331
423
  return lane;
332
424
  }
333
- function parseProvider(raw, path) {
334
- if (raw["provider"] !== "AnyAPI") return malformed(`${path}.provider`);
335
- return "AnyAPI";
425
+ function parseLatency(value, path) {
426
+ const raw = record(value, path);
427
+ const basis = stringField(raw, "basis", path);
428
+ if (basis !== "service_time_excludes_caller_requested_delay") {
429
+ return malformed(`${path}.basis`);
430
+ }
431
+ const sample = integerField(raw, "sample", path);
432
+ if (sample < 1) return malformed(`${path}.sample`);
433
+ return {
434
+ window: stringField(raw, "window", path),
435
+ p50Ms: integerField(raw, "p50Ms", path),
436
+ p95Ms: integerField(raw, "p95Ms", path),
437
+ p99Ms: integerField(raw, "p99Ms", path),
438
+ sample,
439
+ basis
440
+ };
336
441
  }
337
- function parseSchema(value, path) {
338
- return record(value, path);
442
+ function parseProvider(raw, path) {
443
+ return raw["provider"] === "AnyAPI" ? "AnyAPI" : malformed(`${path}.provider`);
339
444
  }
340
445
  function parseHighlight(value, path) {
341
446
  const raw = record(value, path);
@@ -346,31 +451,22 @@ function parseHighlight(value, path) {
346
451
  if (raw["why"] !== void 0) field.why = stringField(raw, "why", path);
347
452
  return field;
348
453
  }
349
- function mapProfile(raw) {
350
- const profile = {
351
- id: raw.id,
352
- status: raw.status,
353
- createdAt: raw.createdAt,
354
- onboardingComplete: raw.onboardingComplete
355
- };
356
- if (raw.email !== void 0 && raw.email !== null) {
357
- profile.email = raw.email;
358
- }
359
- return profile;
360
- }
454
+
455
+ // src/core/discovery.ts
361
456
  function mapCatalogEntry(raw) {
362
- rejectUnsafeDiscoveryFields(raw, "api");
457
+ rejectUnsafeFields(raw, "api");
363
458
  const value = record(raw, "api");
364
459
  const lanesRaw = value["lanes"];
365
- if (!Array.isArray(lanesRaw)) {
366
- return malformed("api.lanes");
367
- }
460
+ if (!Array.isArray(lanesRaw)) return malformed("api.lanes");
368
461
  const entry = {
369
462
  id: stringField(value, "id", "api"),
370
463
  slug: stringField(value, "slug", "api"),
371
464
  category: stringField(value, "category", "api"),
372
465
  name: stringField(value, "name", "api"),
373
466
  description: stringField(value, "description", "api"),
467
+ method: methodField(value, "method", "api"),
468
+ path: pathField(value, "path", "api"),
469
+ execution: parseExecution(value["execution"], "api.execution"),
374
470
  provider: parseProvider(value, "api"),
375
471
  pricing: parsePricing(value["pricing"], "api.pricing"),
376
472
  lanes: lanesRaw.map(
@@ -379,37 +475,42 @@ function mapCatalogEntry(raw) {
379
475
  heavy: value["heavy"] === void 0 ? false : value["heavy"] === true,
380
476
  tryEligible: value["tryEligible"] === true
381
477
  };
382
- if (value["heavy"] !== void 0 && typeof value["heavy"] !== "boolean") {
383
- return malformed("api.heavy");
478
+ if (value["heavy"] !== void 0 && typeof value["heavy"] !== "boolean")
479
+ malformed("api.heavy");
480
+ if (typeof value["tryEligible"] !== "boolean") malformed("api.tryEligible");
481
+ if (value["tryMaxItems"] !== void 0) {
482
+ const tryMaxItems = integerField(value, "tryMaxItems", "api");
483
+ if (tryMaxItems < 1) malformed("api.tryMaxItems");
484
+ entry.tryMaxItems = tryMaxItems;
384
485
  }
385
- if (typeof value["tryEligible"] !== "boolean")
386
- return malformed("api.tryEligible");
387
486
  if (value["failover"] !== void 0) {
388
- if (typeof value["failover"] !== "boolean")
389
- return malformed("api.failover");
487
+ if (typeof value["failover"] !== "boolean") malformed("api.failover");
390
488
  entry.failover = value["failover"];
391
489
  }
392
490
  if (value["excludesCallerDelay"] !== void 0) {
393
491
  if (typeof value["excludesCallerDelay"] !== "boolean")
394
- return malformed("api.excludesCallerDelay");
492
+ malformed("api.excludesCallerDelay");
395
493
  entry.excludesCallerDelay = value["excludesCallerDelay"];
396
494
  }
397
- if (value["inputSchema"] !== void 0) {
398
- entry.inputSchema = parseSchema(value["inputSchema"], "api.inputSchema");
399
- }
400
- if (value["outputSchema"] !== void 0) {
401
- entry.outputSchema = parseSchema(value["outputSchema"], "api.outputSchema");
495
+ if (value["inputSchema"] !== void 0)
496
+ entry.inputSchema = record(value["inputSchema"], "api.inputSchema");
497
+ if (value["outputSchema"] !== void 0)
498
+ entry.outputSchema = record(value["outputSchema"], "api.outputSchema");
499
+ if (value["latency"] !== void 0) {
500
+ entry.latency = value["latency"] === null ? null : parseLatency(value["latency"], "api.latency");
402
501
  }
403
502
  return entry;
404
503
  }
405
504
  function mapCatalogDetail(raw) {
406
505
  const entry = mapCatalogEntry(raw);
506
+ const value = record(raw, "api");
407
507
  if (entry.inputSchema === void 0) return malformed("api.inputSchema");
408
508
  if (entry.outputSchema === void 0) return malformed("api.outputSchema");
509
+ if (!("latency" in value)) return malformed("api.latency");
409
510
  return entry;
410
511
  }
411
512
  function mapCatalogList(raw) {
412
- rejectUnsafeDiscoveryFields(raw, "catalog");
513
+ rejectUnsafeFields(raw, "catalog");
413
514
  const envelope = record(raw, "catalog");
414
515
  if (!Array.isArray(envelope["apis"])) return malformed("catalog.apis");
415
516
  return envelope["apis"].map(mapCatalogEntry);
@@ -422,13 +523,27 @@ function mapSearchResult(value, path) {
422
523
  name: stringField(raw, "name", path),
423
524
  description: stringField(raw, "description", path),
424
525
  category: stringField(raw, "category", path),
526
+ method: methodField(raw, "method", path),
527
+ path: pathField(raw, "path", path),
528
+ execution: parseExecution(raw["execution"], `${path}.execution`),
425
529
  provider: parseProvider(raw, path),
426
530
  pricing: parsePricing(raw["pricing"], `${path}.pricing`),
531
+ failover: typeof raw["failover"] === "boolean" ? raw["failover"] : malformed(`${path}.failover`),
427
532
  relevance: boundedNumberField(raw, "relevance", path, 0, 1)
428
533
  };
534
+ if (raw["tryMaxItems"] !== void 0) {
535
+ const tryMaxItems = integerField(raw, "tryMaxItems", path);
536
+ if (tryMaxItems < 1) malformed(`${path}.tryMaxItems`);
537
+ result.tryMaxItems = tryMaxItems;
538
+ }
539
+ if (raw["excludesCallerDelay"] !== void 0) {
540
+ if (typeof raw["excludesCallerDelay"] !== "boolean")
541
+ malformed(`${path}.excludesCallerDelay`);
542
+ result.excludesCallerDelay = raw["excludesCallerDelay"];
543
+ }
429
544
  if (raw["highlightFields"] !== void 0) {
430
545
  if (!Array.isArray(raw["highlightFields"]))
431
- return malformed(`${path}.highlightFields`);
546
+ malformed(`${path}.highlightFields`);
432
547
  result.highlightFields = raw["highlightFields"].map(
433
548
  (field, index) => parseHighlight(field, `${path}.highlightFields[${index}]`)
434
549
  );
@@ -436,7 +551,7 @@ function mapSearchResult(value, path) {
436
551
  return result;
437
552
  }
438
553
  function mapCatalogSearch(raw) {
439
- rejectUnsafeDiscoveryFields(raw, "search");
554
+ rejectUnsafeFields(raw, "search");
440
555
  const envelope = record(raw, "search");
441
556
  if (!Array.isArray(envelope["results"])) return malformed("search.results");
442
557
  const ranking = envelope["ranking"];
@@ -450,63 +565,6 @@ function mapCatalogSearch(raw) {
450
565
  ranking
451
566
  };
452
567
  }
453
- async function agentSignup(options = {}) {
454
- const fetchImpl = options.fetch ?? globalThis.fetch;
455
- if (typeof fetchImpl !== "function") {
456
- throw new AnyAPIError(
457
- "no fetch implementation available: pass options.fetch or run on a runtime with global fetch",
458
- 0
459
- );
460
- }
461
- const base = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
462
- const body = {};
463
- if (options.sponsorEmail !== void 0) {
464
- body["sponsorEmail"] = options.sponsorEmail;
465
- }
466
- if (options.label !== void 0) {
467
- body["label"] = options.label;
468
- }
469
- let response;
470
- try {
471
- response = await fetchImpl(`${base}/agent/signup`, {
472
- method: "POST",
473
- headers: {
474
- "Content-Type": "application/json",
475
- Accept: "application/json"
476
- },
477
- body: JSON.stringify(body)
478
- });
479
- } catch (err) {
480
- throw new ConnectionError(
481
- err instanceof Error ? err.message : "connection failed",
482
- 0
483
- );
484
- }
485
- const requestId = requestIdOf(response.headers);
486
- const text = await response.text().catch(() => "");
487
- if (response.status !== 200) {
488
- let message = `request failed with status ${response.status}`;
489
- let code;
490
- try {
491
- const parsed2 = JSON.parse(text);
492
- if (typeof parsed2.error === "string" && parsed2.error !== "") {
493
- message = parsed2.error;
494
- }
495
- if (typeof parsed2.code === "string" && parsed2.code !== "") {
496
- code = parsed2.code;
497
- }
498
- } catch {
499
- }
500
- throw errorFromStatus(response.status, message, requestId, code);
501
- }
502
- const parsed = JSON.parse(text);
503
- return {
504
- secret: parsed.secret,
505
- capUsd: parsed.capUsd,
506
- claimToken: parsed.claimToken,
507
- claimUrl: parsed.claimUrl
508
- };
509
- }
510
568
 
511
569
  // src/core/client.ts
512
570
  var DEFAULT_BASE_URL2 = "https://api.getanyapi.com";