@audd/sdk 1.4.9 → 1.5.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
@@ -240,40 +240,86 @@ await audd.streams.add({
240
240
  const streams = await audd.streams.list();
241
241
  ```
242
242
 
243
- Parse incoming callback POSTs into a typed payload:
243
+ ### Handling callback POSTs
244
+
245
+ Drop `handleCallback` into any HTTP handler — Express, Fastify, Hono,
246
+ or the bare `node:http` module. It duck-types the request: a Web
247
+ `Request`, a Node `IncomingMessage`, or a framework request whose body
248
+ has already been parsed all work without configuration.
244
249
 
245
250
  ```ts
246
- const payload = audd.streams.parseCallback(reqBodyJson);
247
- if (payload.isResult) {
248
- for (const r of payload.result!.results) {
249
- console.log(r.artist, r.title, r.score);
251
+ import express from "express";
252
+ import { handleCallback } from "@audd/sdk";
253
+
254
+ const app = express();
255
+ app.use(express.json());
256
+
257
+ app.post("/audd-callback", async (req, res) => {
258
+ const { match, notification } = await handleCallback(req);
259
+ if (match) {
260
+ console.log(`${match.song.artist} - ${match.song.title} score=${match.song.score}`);
261
+ for (const alt of match.alternatives) {
262
+ // alternatives are variant catalog releases — different artist/title is possible
263
+ console.log(` alt: ${alt.artist} - ${alt.title}`);
264
+ }
265
+ } else if (notification) {
266
+ console.log(`#${notification.notificationCode} ${notification.notificationMessage}`);
250
267
  }
251
- } else if (payload.isNotification) {
252
- console.log(payload.notification!.notificationCode,
253
- payload.notification!.notificationMessage);
254
- }
268
+ res.json({ ok: true });
269
+ });
255
270
  ```
256
271
 
272
+ If you already have the body bytes (queue consumer, replay tool), call
273
+ `parseCallback(body)` directly — it accepts a parsed JSON object or a
274
+ JSON string and returns the same `{ match, notification }` shape.
275
+
257
276
  ### Receiving events without a callback URL (longpoll)
258
277
 
259
- Useful when you can't expose a public HTTPS receiver. Before the first
260
- event, the SDK runs a one-time `getCallbackUrl` preflight — AudD
261
- silently discards events for accounts without any callback URL set, so
262
- this catches the trap early. Pass `skipCallbackCheck: true` to opt out.
278
+ Useful when you can't expose a public HTTPS receiver. The poll handle
279
+ exposes three async-iterables — `matches`, `notifications`, `errors` —
280
+ filled by a background loop. Iterate them independently, or in parallel
281
+ via `Promise.all`.
282
+
283
+ Before the first request the SDK runs a one-time `getCallbackUrl`
284
+ preflight: AudD silently discards events for accounts without any
285
+ callback URL set, and the preflight surfaces that as an actionable
286
+ error. Pass `skipCallbackCheck: true` to bypass.
263
287
 
264
288
  ```ts
265
- for await (const event of audd.streams.longpoll(category, { timeout: 30 })) {
266
- console.log(event);
289
+ const category = audd.streams.deriveLongpollCategory(12345); // pure, no network call
290
+ const poll = await audd.streams.longpoll(category, { timeout: 30 });
291
+
292
+ for await (const m of poll.matches) {
293
+ console.log(m.song.artist, m.song.title);
267
294
  }
268
295
  ```
269
296
 
270
- `category` is a 9-character string derived locally from your token and
271
- `radioId`:
297
+ Consume matches and notifications concurrently:
272
298
 
273
299
  ```ts
274
- const category = audd.streams.deriveLongpollCategory(12345);
300
+ await Promise.all([
301
+ (async () => {
302
+ for await (const m of poll.matches) {
303
+ console.log("match:", m.song.artist, m.song.title);
304
+ }
305
+ })(),
306
+ (async () => {
307
+ for await (const n of poll.notifications) {
308
+ console.log("notification:", n.notificationMessage);
309
+ }
310
+ })(),
311
+ (async () => {
312
+ for await (const err of poll.errors) {
313
+ console.error(err);
314
+ poll.close();
315
+ }
316
+ })(),
317
+ ]);
275
318
  ```
276
319
 
320
+ `poll.close()` (or the `await using` resource-management form) tears
321
+ down the background loop and completes all three iterables.
322
+
277
323
  ### Browser / widget consumers
278
324
 
279
325
  The `audd/longpoll` sub-entry exports a tokenless `LongpollConsumer` for
@@ -285,8 +331,9 @@ client out of the resulting bundle.
285
331
  import { LongpollConsumer } from "@audd/sdk/longpoll";
286
332
 
287
333
  const consumer = new LongpollConsumer("abc123def");
288
- for await (const event of consumer.iterate({ timeout: 30 })) {
289
- console.log(event);
334
+ const poll = consumer.iterate({ timeout: 30 });
335
+ for await (const m of poll.matches) {
336
+ console.log(m.song.artist, m.song.title);
290
337
  }
291
338
  ```
292
339
 
package/dist/index.cjs CHANGED
@@ -522,7 +522,7 @@ function parseStream(raw) {
522
522
  rawResponse: r
523
523
  };
524
524
  }
525
- var STREAM_CALLBACK_RESULT_ENTRY_KEYS = [
525
+ var STREAM_CALLBACK_SONG_KEYS = [
526
526
  "artist",
527
527
  "title",
528
528
  "score",
@@ -530,20 +530,22 @@ var STREAM_CALLBACK_RESULT_ENTRY_KEYS = [
530
530
  "release_date",
531
531
  "label",
532
532
  "song_link",
533
+ "isrc",
534
+ "upc",
533
535
  "apple_music",
534
536
  "spotify",
535
537
  "deezer",
536
538
  "napster",
537
539
  "musicbrainz"
538
540
  ];
539
- function parseStreamCallbackResultEntry(raw) {
540
- const r = requireObject(raw, "StreamCallbackResultEntry");
541
+ function parseStreamCallbackSong(raw) {
542
+ const r = requireObject(raw, "StreamCallbackSong");
541
543
  const artist = asString(r.artist);
542
544
  const title = asString(r.title);
543
545
  const score = asNumber(r.score);
544
- if (artist === void 0) throw new TypeError("StreamCallbackResultEntry: artist not string");
545
- if (title === void 0) throw new TypeError("StreamCallbackResultEntry: title not string");
546
- if (score === void 0) throw new TypeError("StreamCallbackResultEntry: score not number");
546
+ if (artist === void 0) throw new TypeError("StreamCallbackSong: artist not string");
547
+ if (title === void 0) throw new TypeError("StreamCallbackSong: title not string");
548
+ if (score === void 0) throw new TypeError("StreamCallbackSong: score not number");
547
549
  const musicbrainz = Array.isArray(r.musicbrainz) ? r.musicbrainz.filter((x) => asObject(x) !== void 0) : void 0;
548
550
  return {
549
551
  artist,
@@ -553,33 +555,39 @@ function parseStreamCallbackResultEntry(raw) {
553
555
  releaseDate: asString(r.release_date),
554
556
  label: asString(r.label),
555
557
  songLink: asString(r.song_link),
558
+ isrc: asString(r.isrc),
559
+ upc: asString(r.upc),
556
560
  appleMusic: asObject(r.apple_music),
557
561
  spotify: asObject(r.spotify),
558
562
  deezer: asObject(r.deezer),
559
563
  napster: asObject(r.napster),
560
564
  musicbrainz,
561
- extras: pickExtras(r, STREAM_CALLBACK_RESULT_ENTRY_KEYS),
562
- rawResponse: r
565
+ extras: pickExtras(r, STREAM_CALLBACK_SONG_KEYS)
563
566
  };
564
567
  }
565
- var STREAM_CALLBACK_RESULT_KEYS = [
568
+ var STREAM_CALLBACK_MATCH_KEYS = [
566
569
  "radio_id",
567
570
  "timestamp",
568
571
  "play_length",
569
572
  "results"
570
573
  ];
571
- function parseStreamCallbackResult(raw) {
572
- const r = requireObject(raw, "StreamCallbackResult");
574
+ function parseStreamCallbackMatch(raw) {
575
+ const r = requireObject(raw, "StreamCallbackMatch");
573
576
  const radioId = asNumber(r.radio_id);
574
- if (radioId === void 0) throw new TypeError("StreamCallbackResult: radio_id not number");
577
+ if (radioId === void 0) throw new TypeError("StreamCallbackMatch: radio_id not number");
575
578
  const resultsRaw = r.results;
576
- if (!Array.isArray(resultsRaw)) throw new TypeError("StreamCallbackResult: results not array");
579
+ if (!Array.isArray(resultsRaw)) throw new TypeError("StreamCallbackMatch: results not array");
580
+ if (resultsRaw.length === 0) throw new TypeError("StreamCallbackMatch: results is empty");
581
+ const songs = resultsRaw.map(parseStreamCallbackSong);
582
+ const [first, ...rest] = songs;
583
+ if (first === void 0) throw new TypeError("StreamCallbackMatch: results is empty");
577
584
  return {
578
585
  radioId,
579
586
  timestamp: asString(r.timestamp),
580
587
  playLength: asNumber(r.play_length),
581
- results: resultsRaw.map(parseStreamCallbackResultEntry),
582
- extras: pickExtras(r, STREAM_CALLBACK_RESULT_KEYS),
588
+ song: first,
589
+ alternatives: rest,
590
+ extras: pickExtras(r, STREAM_CALLBACK_MATCH_KEYS),
583
591
  rawResponse: r
584
592
  };
585
593
  }
@@ -609,39 +617,6 @@ function parseStreamCallbackNotification(raw) {
609
617
  rawResponse: r
610
618
  };
611
619
  }
612
- function parseStreamCallback(raw) {
613
- const r = requireObject(raw, "StreamCallbackPayload");
614
- if ("notification" in r) {
615
- const notification = parseStreamCallbackNotification(r.notification);
616
- const time = asNumber(r.time) ?? null;
617
- return {
618
- result: null,
619
- notification,
620
- time,
621
- rawPayload: r,
622
- get isResult() {
623
- return false;
624
- },
625
- get isNotification() {
626
- return true;
627
- }
628
- };
629
- }
630
- const inner = requireObject(r.result, "StreamCallbackPayload.result");
631
- const result = parseStreamCallbackResult(inner);
632
- return {
633
- result,
634
- notification: null,
635
- time: null,
636
- rawPayload: r,
637
- get isResult() {
638
- return true;
639
- },
640
- get isNotification() {
641
- return false;
642
- }
643
- };
644
- }
645
620
  var LYRICS_KEYS = [
646
621
  "artist",
647
622
  "title",
@@ -840,6 +815,246 @@ function addReturnToUrl(url, returnMetadata) {
840
815
  u.searchParams.set("return", value);
841
816
  return u.toString();
842
817
  }
818
+ function parseCallback(body) {
819
+ let raw = body;
820
+ if (typeof body === "string") {
821
+ try {
822
+ raw = JSON.parse(body);
823
+ } catch (err) {
824
+ throw new AudDSerializationError(
825
+ `callback body is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
826
+ body
827
+ );
828
+ }
829
+ }
830
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
831
+ throw new AudDSerializationError(
832
+ "callback body is not a JSON object",
833
+ typeof body === "string" ? body : ""
834
+ );
835
+ }
836
+ const r = raw;
837
+ if ("notification" in r) {
838
+ let notification;
839
+ try {
840
+ notification = parseStreamCallbackNotification(r.notification);
841
+ } catch (err) {
842
+ throw new AudDSerializationError(
843
+ `callback notification: ${err instanceof Error ? err.message : String(err)}`,
844
+ typeof body === "string" ? body : JSON.stringify(r)
845
+ );
846
+ }
847
+ const t = r.time;
848
+ if (typeof t === "number") notification.time = t;
849
+ return { match: null, notification };
850
+ }
851
+ if ("result" in r) {
852
+ let match;
853
+ try {
854
+ match = parseStreamCallbackMatch(r.result);
855
+ } catch (err) {
856
+ throw new AudDSerializationError(
857
+ `callback result: ${err instanceof Error ? err.message : String(err)}`,
858
+ typeof body === "string" ? body : JSON.stringify(r)
859
+ );
860
+ }
861
+ return { match, notification: null };
862
+ }
863
+ throw new AudDSerializationError(
864
+ "callback body has neither result nor notification",
865
+ typeof body === "string" ? body : JSON.stringify(r)
866
+ );
867
+ }
868
+ async function handleCallback(req) {
869
+ if (typeof req.text === "function") {
870
+ const text = await req.text();
871
+ return parseCallback(text);
872
+ }
873
+ const reqRecord = req;
874
+ if ("body" in reqRecord && reqRecord.body !== void 0 && reqRecord.body !== null) {
875
+ const body = reqRecord.body;
876
+ if (typeof body === "object" && body !== null && Symbol.asyncIterator in body && // Plain Buffer/Uint8Array also has Symbol.iterator — accept those by value.
877
+ !(body instanceof Uint8Array)) {
878
+ return parseCallback(await drainAsyncIterable(body));
879
+ }
880
+ if (body instanceof Uint8Array) {
881
+ return parseCallback(new TextDecoder().decode(body));
882
+ }
883
+ return parseCallback(body);
884
+ }
885
+ if (typeof req === "object" && req !== null && Symbol.asyncIterator in req) {
886
+ return parseCallback(await drainAsyncIterable(req));
887
+ }
888
+ throw new AudDSerializationError(
889
+ "handleCallback: request has no body, no text() method, and is not async-iterable"
890
+ );
891
+ }
892
+ async function drainAsyncIterable(it) {
893
+ const chunks = [];
894
+ let stringBuf = "";
895
+ for await (const chunk of it) {
896
+ if (typeof chunk === "string") {
897
+ stringBuf += chunk;
898
+ } else if (chunk instanceof Uint8Array) {
899
+ chunks.push(chunk);
900
+ } else if (chunk !== null && chunk !== void 0) {
901
+ stringBuf += String(chunk);
902
+ }
903
+ }
904
+ if (chunks.length > 0) {
905
+ let total = 0;
906
+ for (const c of chunks) total += c.byteLength;
907
+ const merged = new Uint8Array(total);
908
+ let offset = 0;
909
+ for (const c of chunks) {
910
+ merged.set(c, offset);
911
+ offset += c.byteLength;
912
+ }
913
+ stringBuf = new TextDecoder().decode(merged) + stringBuf;
914
+ }
915
+ return stringBuf;
916
+ }
917
+
918
+ // src/longpollCore.ts
919
+ var HTTP_CLIENT_ERROR_FLOOR = 400;
920
+ var AsyncQueue = class {
921
+ items = [];
922
+ waiters = [];
923
+ done = false;
924
+ push(value) {
925
+ if (this.done) return;
926
+ const w = this.waiters.shift();
927
+ if (w !== void 0) {
928
+ w({ value, done: false });
929
+ return;
930
+ }
931
+ this.items.push(value);
932
+ }
933
+ finish() {
934
+ if (this.done) return;
935
+ this.done = true;
936
+ while (this.waiters.length > 0) {
937
+ const w = this.waiters.shift();
938
+ if (w !== void 0) w({ value: void 0, done: true });
939
+ }
940
+ }
941
+ next() {
942
+ const item = this.items.shift();
943
+ if (item !== void 0) return Promise.resolve({ value: item, done: false });
944
+ if (this.done) return Promise.resolve({ value: void 0, done: true });
945
+ return new Promise((resolve) => {
946
+ this.waiters.push(resolve);
947
+ });
948
+ }
949
+ toIterable() {
950
+ const next = () => this.next();
951
+ return {
952
+ [Symbol.asyncIterator]() {
953
+ return { next };
954
+ }
955
+ };
956
+ }
957
+ };
958
+ function decodeOne(resp) {
959
+ if (resp.httpStatus >= HTTP_CLIENT_ERROR_FLOOR) {
960
+ throw new AudDServerError({
961
+ errorCode: 0,
962
+ message: `Longpoll endpoint returned HTTP ${String(resp.httpStatus)}`,
963
+ httpStatus: resp.httpStatus,
964
+ requestId: resp.requestId,
965
+ rawResponse: resp.jsonBody ?? resp.rawText
966
+ });
967
+ }
968
+ const body = resp.jsonBody;
969
+ if (typeof body !== "object" || body === null || Array.isArray(body)) {
970
+ throw new AudDSerializationError(
971
+ "Longpoll response was not a JSON object",
972
+ resp.rawText
973
+ );
974
+ }
975
+ const dict = body;
976
+ if ("notification" in dict) {
977
+ const notification = parseStreamCallbackNotification(dict.notification);
978
+ const t = dict["time"];
979
+ if (typeof t === "number") notification.time = t;
980
+ return { kind: "notification", notification };
981
+ }
982
+ if ("result" in dict) {
983
+ return { kind: "match", match: parseStreamCallbackMatch(dict.result) };
984
+ }
985
+ return { kind: "keepalive" };
986
+ }
987
+ function startLongpoll(cfg) {
988
+ const matches = new AsyncQueue();
989
+ const notifications = new AsyncQueue();
990
+ const errors = new AsyncQueue();
991
+ const controller = new AbortController();
992
+ let closed = false;
993
+ const finishAll = () => {
994
+ matches.finish();
995
+ notifications.finish();
996
+ errors.finish();
997
+ };
998
+ const close = () => {
999
+ if (closed) return;
1000
+ closed = true;
1001
+ controller.abort();
1002
+ finishAll();
1003
+ };
1004
+ const run = async () => {
1005
+ let curSince = cfg.sinceTime;
1006
+ while (!closed) {
1007
+ const params = {
1008
+ category: cfg.category,
1009
+ timeout: String(cfg.timeout)
1010
+ };
1011
+ if (curSince !== void 0) params["since_time"] = String(curSince);
1012
+ let resp;
1013
+ try {
1014
+ resp = await cfg.fetchOnce(params, controller.signal);
1015
+ } catch (err) {
1016
+ if (closed) return;
1017
+ if (err instanceof TypeError) {
1018
+ errors.push(new AudDConnectionError(`Network error: ${err.message}`, err));
1019
+ } else if (err !== null && typeof err === "object" && err.name === "AbortError") {
1020
+ if (closed) return;
1021
+ errors.push(new AudDConnectionError("Request was aborted (timeout)", err));
1022
+ } else if (err instanceof Error) {
1023
+ errors.push(err);
1024
+ } else {
1025
+ errors.push(new Error(String(err)));
1026
+ }
1027
+ finishAll();
1028
+ return;
1029
+ }
1030
+ try {
1031
+ const decoded = decodeOne(resp);
1032
+ if (decoded.kind === "match") matches.push(decoded.match);
1033
+ else if (decoded.kind === "notification") notifications.push(decoded.notification);
1034
+ } catch (err) {
1035
+ errors.push(err instanceof Error ? err : new Error(String(err)));
1036
+ finishAll();
1037
+ return;
1038
+ }
1039
+ const dict = resp.jsonBody;
1040
+ if (typeof dict === "object" && dict !== null && !Array.isArray(dict)) {
1041
+ const ts = dict["timestamp"];
1042
+ if (typeof ts === "number") curSince = ts;
1043
+ }
1044
+ }
1045
+ };
1046
+ void run();
1047
+ return {
1048
+ matches: matches.toIterable(),
1049
+ notifications: notifications.toIterable(),
1050
+ errors: errors.toIterable(),
1051
+ close,
1052
+ async [Symbol.asyncDispose]() {
1053
+ close();
1054
+ await Promise.resolve();
1055
+ }
1056
+ };
1057
+ }
843
1058
 
844
1059
  // src/streams.ts
845
1060
  var API_BASE = "https://api.audd.io";
@@ -946,69 +1161,58 @@ var Streams = class {
946
1161
  deriveLongpollCategory(radioId) {
947
1162
  return deriveLongpollCategory(this.apiToken, radioId);
948
1163
  }
949
- /** Parse a callback POST body into a typed `StreamCallbackPayload`. */
1164
+ /**
1165
+ * Parse a callback POST body into `{ match, notification }`. Pass an
1166
+ * already-parsed JSON value or a string. Exactly one field is non-null on
1167
+ * success. See {@link parseCallback} for details.
1168
+ */
950
1169
  parseCallback(body) {
951
- return parseStreamCallback(body);
1170
+ return parseCallback(body);
952
1171
  }
953
1172
  /**
954
- * Longpoll the AudD subscription endpoint, yielding parsed JSON dicts
955
- * (recognition events, notifications, or `{ timeout: ... }` markers).
1173
+ * Long-poll the AudD subscription endpoint.
1174
+ *
1175
+ * Returns a {@link LongpollPoll} handle with three async-iterables —
1176
+ * `matches`, `notifications`, `errors` — that are filled by a background
1177
+ * loop. Iterate them independently or in parallel via `Promise.all([...])`.
956
1178
  *
957
- * By default, performs a one-time `getCallbackUrl` preflight. If the
958
- * server returns code 19 (no callback URL configured), throws
959
- * `AudDInvalidRequestError` with an actionable hint. Pass
960
- * `skipCallbackCheck: true` to bypass.
1179
+ * Before the first request the SDK runs a one-time `getCallbackUrl`
1180
+ * preflight: AudD silently discards events for accounts that haven't set a
1181
+ * callback URL, and the preflight surfaces that misconfiguration as an
1182
+ * actionable {@link AudDInvalidRequestError}. Pass `skipCallbackCheck: true`
1183
+ * to bypass.
961
1184
  */
962
- longpoll(category, opts = {}) {
963
- const skipCheck = opts.skipCallbackCheck ?? false;
1185
+ async longpoll(category, opts = {}) {
1186
+ if (opts.skipCallbackCheck !== true) {
1187
+ await this.preflightCallbackUrl();
1188
+ }
964
1189
  const timeoutSec = opts.timeout ?? 50;
965
- const initialSince = opts.sinceTime;
966
- const getCallbackUrl = () => this.getCallbackUrl();
967
- const httpGet = (params) => this.http.get(`${API_BASE}/longpoll/`, params);
1190
+ const httpClient = this.http;
968
1191
  const readPolicy = this.readPolicy;
969
- return {
970
- [Symbol.asyncIterator]() {
971
- let preflighted = skipCheck;
972
- let curSince = initialSince;
973
- return {
974
- async next() {
975
- if (!preflighted) {
976
- try {
977
- await getCallbackUrl();
978
- } catch (err) {
979
- if (err instanceof AudDAPIError && err.errorCode === NO_CALLBACK_ERROR_CODE) {
980
- throw new AudDInvalidRequestError({
981
- errorCode: 0,
982
- message: PREFLIGHT_NO_CALLBACK_HINT,
983
- httpStatus: err.httpStatus,
984
- requestId: err.requestId
985
- });
986
- }
987
- throw err;
988
- }
989
- preflighted = true;
990
- }
991
- const params = {
992
- category,
993
- timeout: String(timeoutSec)
994
- };
995
- if (curSince !== void 0) params["since_time"] = String(curSince);
996
- const resp = await runRetried(() => httpGet(params), readPolicy);
997
- const body = resp.jsonBody;
998
- if (typeof body !== "object" || body === null || Array.isArray(body)) {
999
- throw new AudDSerializationError(
1000
- "Unparseable longpoll response",
1001
- resp.rawText
1002
- );
1003
- }
1004
- const dict = body;
1005
- const ts = dict["timestamp"];
1006
- if (typeof ts === "number") curSince = ts;
1007
- return { value: dict, done: false };
1008
- }
1009
- };
1192
+ return startLongpoll({
1193
+ category,
1194
+ timeout: timeoutSec,
1195
+ sinceTime: opts.sinceTime,
1196
+ fetchOnce: (params, signal) => runRetried(
1197
+ () => httpClient.get(`${API_BASE}/longpoll/`, params, { signal }),
1198
+ readPolicy
1199
+ )
1200
+ });
1201
+ }
1202
+ async preflightCallbackUrl() {
1203
+ try {
1204
+ await this.getCallbackUrl();
1205
+ } catch (err) {
1206
+ if (err instanceof AudDAPIError && err.errorCode === NO_CALLBACK_ERROR_CODE) {
1207
+ throw new AudDInvalidRequestError({
1208
+ errorCode: 0,
1209
+ message: PREFLIGHT_NO_CALLBACK_HINT,
1210
+ httpStatus: err.httpStatus,
1211
+ requestId: err.requestId
1212
+ });
1010
1213
  }
1011
- };
1214
+ throw err;
1215
+ }
1012
1216
  }
1013
1217
  };
1014
1218
 
@@ -1146,7 +1350,7 @@ var Advanced = class {
1146
1350
  var API_BASE3 = "https://api.audd.io";
1147
1351
  var ENTERPRISE_BASE = "https://enterprise.audd.io";
1148
1352
  var DEPRECATED_PARAMS_CODE = 51;
1149
- var HTTP_CLIENT_ERROR_FLOOR = 400;
1353
+ var HTTP_CLIENT_ERROR_FLOOR2 = 400;
1150
1354
  var TOKEN_ENV_VAR = "AUDD_API_TOKEN";
1151
1355
  function safeEmit(hook, event) {
1152
1356
  if (hook === void 0) return;
@@ -1194,7 +1398,7 @@ function maybeWarnAndStrip(body) {
1194
1398
  function decodeOrRaise(resp) {
1195
1399
  const body = resp.jsonBody;
1196
1400
  if (typeof body !== "object" || body === null || Array.isArray(body)) {
1197
- if (resp.httpStatus >= HTTP_CLIENT_ERROR_FLOOR) {
1401
+ if (resp.httpStatus >= HTTP_CLIENT_ERROR_FLOOR2) {
1198
1402
  throw new AudDServerError({
1199
1403
  errorCode: 0,
1200
1404
  message: `HTTP ${String(resp.httpStatus)} with non-JSON response body`,
@@ -1441,6 +1645,7 @@ exports.VERSION = VERSION;
1441
1645
  exports.addReturnToUrl = addReturnToUrl;
1442
1646
  exports.deriveLongpollCategory = deriveLongpollCategory;
1443
1647
  exports.errorClassForCode = errorClassForCode;
1444
- exports.parseCallback = parseStreamCallback;
1648
+ exports.handleCallback = handleCallback;
1649
+ exports.parseCallback = parseCallback;
1445
1650
  //# sourceMappingURL=index.cjs.map
1446
1651
  //# sourceMappingURL=index.cjs.map