@audd/sdk 1.5.11 → 1.5.13

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
@@ -3,7 +3,7 @@ import * as path from 'path';
3
3
  import { createHash } from 'crypto';
4
4
 
5
5
  // src/version.ts
6
- var VERSION = "1.5.8";
6
+ var VERSION = "1.5.13";
7
7
 
8
8
  // src/errors.ts
9
9
  var AudDError = class extends Error {
@@ -76,6 +76,16 @@ var AudDConnectionError = class extends AudDError {
76
76
  this.cause = cause;
77
77
  }
78
78
  };
79
+ var USER_ABORT_ERROR_NAME = "UserAbortError";
80
+ function userAbortError(cause) {
81
+ const err = new Error("Request was cancelled by the caller's AbortSignal");
82
+ err.name = USER_ABORT_ERROR_NAME;
83
+ err.cause = cause;
84
+ return err;
85
+ }
86
+ function isUserAbortError(err) {
87
+ return err !== null && typeof err === "object" && err.name === USER_ABORT_ERROR_NAME;
88
+ }
79
89
  var AudDSerializationError = class extends AudDError {
80
90
  name = "AudDSerializationError";
81
91
  rawText;
@@ -148,6 +158,55 @@ function raiseFromErrorResponse(body, opts) {
148
158
  throw new Cls(fields);
149
159
  }
150
160
 
161
+ // src/events.ts
162
+ function safeEmit(hook, event) {
163
+ if (hook === void 0) return;
164
+ try {
165
+ hook(event);
166
+ } catch {
167
+ }
168
+ }
169
+ async function emitAround(hook, method, url, fn) {
170
+ const startedAt = Date.now();
171
+ safeEmit(hook, {
172
+ kind: "request",
173
+ method,
174
+ url,
175
+ requestId: null,
176
+ httpStatus: null,
177
+ elapsedMs: null,
178
+ errorCode: null,
179
+ extras: {}
180
+ });
181
+ let resp;
182
+ try {
183
+ resp = await fn();
184
+ } catch (err) {
185
+ safeEmit(hook, {
186
+ kind: "exception",
187
+ method,
188
+ url,
189
+ requestId: null,
190
+ httpStatus: null,
191
+ elapsedMs: Date.now() - startedAt,
192
+ errorCode: null,
193
+ extras: { name: err instanceof Error ? err.name : String(err) }
194
+ });
195
+ throw err;
196
+ }
197
+ safeEmit(hook, {
198
+ kind: "response",
199
+ method,
200
+ url,
201
+ requestId: resp.requestId,
202
+ httpStatus: resp.httpStatus,
203
+ elapsedMs: Date.now() - startedAt,
204
+ errorCode: null,
205
+ extras: {}
206
+ });
207
+ return resp;
208
+ }
209
+
151
210
  // src/userAgent.ts
152
211
  function userAgent() {
153
212
  const node = typeof process !== "undefined" && process.version ? process.version.replace(/^v/, "") : "unknown";
@@ -225,6 +284,11 @@ var HttpClient = class {
225
284
  requestId: response.headers.get("x-request-id"),
226
285
  rawText: text
227
286
  };
287
+ } catch (err) {
288
+ if (userSignal?.aborted === true && err !== null && typeof err === "object" && err.name === "AbortError") {
289
+ throw userAbortError(err);
290
+ }
291
+ throw err;
228
292
  } finally {
229
293
  clearTimeout(timer);
230
294
  if (userSignal !== void 0) {
@@ -249,19 +313,42 @@ function asObject(raw) {
249
313
  }
250
314
  return void 0;
251
315
  }
252
- function requireObject(raw, what) {
253
- const o = asObject(raw);
254
- if (o === void 0) throw new TypeError(`${what}: expected object`);
255
- return o;
316
+ function asObjectLenient(raw) {
317
+ return asObject(raw) ?? {};
256
318
  }
257
319
  function asString(v) {
258
- return typeof v === "string" ? v : void 0;
320
+ if (typeof v === "string") return v;
321
+ if (typeof v === "number" || typeof v === "boolean") return String(v);
322
+ return void 0;
323
+ }
324
+ function toFiniteNumber(v) {
325
+ if (typeof v === "number") return Number.isFinite(v) ? v : void 0;
326
+ if (typeof v === "boolean") return v ? 1 : 0;
327
+ if (typeof v === "string") {
328
+ const t = v.trim();
329
+ if (t === "") return void 0;
330
+ const n = Number(t);
331
+ return Number.isFinite(n) ? n : void 0;
332
+ }
333
+ return void 0;
259
334
  }
260
335
  function asNumber(v) {
261
- return typeof v === "number" ? v : void 0;
336
+ return toFiniteNumber(v);
337
+ }
338
+ function asInteger(v) {
339
+ const n = toFiniteNumber(v);
340
+ return n === void 0 ? void 0 : Math.trunc(n);
262
341
  }
263
342
  function asBoolean(v) {
264
- return typeof v === "boolean" ? v : void 0;
343
+ if (typeof v === "boolean") return v;
344
+ if (typeof v === "number") return v !== 0;
345
+ if (typeof v === "string") {
346
+ const t = v.trim().toLowerCase();
347
+ if (t === "true" || t === "1" || t === "yes" || t === "on") return true;
348
+ if (t === "false" || t === "0" || t === "no" || t === "off" || t === "") return false;
349
+ return void 0;
350
+ }
351
+ return void 0;
265
352
  }
266
353
  function parseOffsetToSeconds(o) {
267
354
  if (o === void 0 || o === null) return void 0;
@@ -356,9 +443,9 @@ var RECOGNITION_KEYS = [
356
443
  "musicbrainz"
357
444
  ];
358
445
  function parseRecognitionResult(raw) {
359
- const r = requireObject(raw, "RecognitionResult");
446
+ const r = asObjectLenient(raw);
360
447
  const timecode = asString(r.timecode);
361
- const audioId = asNumber(r.audio_id);
448
+ const audioId = asInteger(r.audio_id);
362
449
  const artist = asString(r.artist);
363
450
  const title = asString(r.title);
364
451
  const songLink = asString(r.song_link);
@@ -431,7 +518,7 @@ var ENTERPRISE_MATCH_KEYS = [
431
518
  "end_seconds"
432
519
  ];
433
520
  function parseEnterpriseMatch(raw) {
434
- const r = requireObject(raw, "EnterpriseMatch");
521
+ const r = asObjectLenient(raw);
435
522
  const score = asNumber(r.score);
436
523
  const timecode = asString(r.timecode);
437
524
  const songLink = asString(r.song_link);
@@ -446,8 +533,8 @@ function parseEnterpriseMatch(raw) {
446
533
  isrc: asString(r.isrc),
447
534
  upc: asString(r.upc),
448
535
  songLink,
449
- startOffset: asNumber(r.start_offset),
450
- endOffset: asNumber(r.end_offset),
536
+ startOffset: asInteger(r.start_offset),
537
+ endOffset: asInteger(r.end_offset),
451
538
  startSeconds: void 0,
452
539
  endSeconds: void 0,
453
540
  extras: pickExtras(r, ENTERPRISE_MATCH_KEYS),
@@ -481,8 +568,8 @@ function parseEnterpriseMatch(raw) {
481
568
  }
482
569
  var ENTERPRISE_CHUNK_KEYS = ["songs", "offset"];
483
570
  function parseEnterpriseChunkResult(raw) {
484
- const r = requireObject(raw, "EnterpriseChunkResult");
485
- const songsRaw = Array.isArray(r.songs) ? r.songs : [];
571
+ const r = asObjectLenient(raw);
572
+ const songsRaw = Array.isArray(r.songs) ? r.songs.filter((s) => asObject(s) !== void 0) : [];
486
573
  const offset = asString(r.offset);
487
574
  const base = parseOffsetToSeconds(r.offset);
488
575
  const songs = songsRaw.map((s) => {
@@ -502,8 +589,8 @@ function parseEnterpriseChunkResult(raw) {
502
589
  }
503
590
  var STREAM_KEYS = ["radio_id", "url", "stream_running", "longpoll_category"];
504
591
  function parseStream(raw) {
505
- const r = requireObject(raw, "Stream");
506
- const radioId = asNumber(r.radio_id);
592
+ const r = asObjectLenient(raw);
593
+ const radioId = asInteger(r.radio_id);
507
594
  const url = asString(r.url);
508
595
  const streamRunning = asBoolean(r.stream_running);
509
596
  return {
@@ -532,7 +619,7 @@ var STREAM_CALLBACK_SONG_KEYS = [
532
619
  "musicbrainz"
533
620
  ];
534
621
  function parseStreamCallbackSong(raw) {
535
- const r = requireObject(raw, "StreamCallbackSong");
622
+ const r = asObjectLenient(raw);
536
623
  const artist = asString(r.artist);
537
624
  const title = asString(r.title);
538
625
  const score = asNumber(r.score);
@@ -562,15 +649,15 @@ var STREAM_CALLBACK_MATCH_KEYS = [
562
649
  "results"
563
650
  ];
564
651
  function parseStreamCallbackMatch(raw) {
565
- const r = requireObject(raw, "StreamCallbackMatch");
566
- const radioId = asNumber(r.radio_id);
652
+ const r = asObjectLenient(raw);
653
+ const radioId = asInteger(r.radio_id);
567
654
  const resultsRaw = Array.isArray(r.results) ? r.results : [];
568
655
  const songs = resultsRaw.filter((x) => asObject(x) !== void 0).map(parseStreamCallbackSong);
569
656
  const [first, ...rest] = songs;
570
657
  return {
571
658
  radioId,
572
659
  timestamp: asString(r.timestamp),
573
- playLength: asNumber(r.play_length),
660
+ playLength: asInteger(r.play_length),
574
661
  song: first,
575
662
  alternatives: rest,
576
663
  extras: pickExtras(r, STREAM_CALLBACK_MATCH_KEYS),
@@ -584,9 +671,9 @@ var STREAM_CALLBACK_NOTIFICATION_KEYS = [
584
671
  "notification_message"
585
672
  ];
586
673
  function parseStreamCallbackNotification(raw) {
587
- const r = requireObject(raw, "StreamCallbackNotification");
588
- const radioId = asNumber(r.radio_id);
589
- const code = asNumber(r.notification_code);
674
+ const r = asObjectLenient(raw);
675
+ const radioId = asInteger(r.radio_id);
676
+ const code = asInteger(r.notification_code);
590
677
  const message = asString(r.notification_message);
591
678
  return {
592
679
  radioId,
@@ -608,16 +695,16 @@ var LYRICS_KEYS = [
608
695
  "media"
609
696
  ];
610
697
  function parseLyricsResult(raw) {
611
- const r = requireObject(raw, "LyricsResult");
698
+ const r = asObjectLenient(raw);
612
699
  const artist = asString(r.artist);
613
700
  const title = asString(r.title);
614
701
  return {
615
702
  artist,
616
703
  title,
617
704
  lyrics: asString(r.lyrics),
618
- songId: asNumber(r.song_id),
705
+ songId: asInteger(r.song_id),
619
706
  fullTitle: asString(r.full_title),
620
- artistId: asNumber(r.artist_id),
707
+ artistId: asInteger(r.artist_id),
621
708
  songLink: asString(r.song_link),
622
709
  media: asString(r.media),
623
710
  extras: pickExtras(r, LYRICS_KEYS),
@@ -704,6 +791,22 @@ async function retry(fn, policy) {
704
791
  if (haveResp && lastResp !== void 0) return lastResp;
705
792
  throw lastError;
706
793
  }
794
+ async function runRetried(fn, policy) {
795
+ try {
796
+ return await retry(fn, policy);
797
+ } catch (err) {
798
+ if (isUserAbortError(err)) {
799
+ throw new AudDConnectionError("Request was cancelled by the caller's AbortSignal", err);
800
+ }
801
+ if (err instanceof TypeError) {
802
+ throw new AudDConnectionError(`Network error: ${err.message}`, err);
803
+ }
804
+ if (err !== null && typeof err === "object" && err.name === "AbortError") {
805
+ throw new AudDConnectionError("Request was aborted (timeout)", err);
806
+ }
807
+ throw err;
808
+ }
809
+ }
707
810
  function looksLikeUrl(s) {
708
811
  return s.startsWith("http://") || s.startsWith("https://");
709
812
  }
@@ -955,13 +1058,13 @@ function decodeOne(resp) {
955
1058
  );
956
1059
  }
957
1060
  const dict = body;
958
- if ("notification" in dict) {
1061
+ if (asObject(dict.notification) !== void 0) {
959
1062
  const notification = parseStreamCallbackNotification(dict.notification);
960
1063
  const t = dict["time"];
961
1064
  if (typeof t === "number") notification.time = t;
962
1065
  return { kind: "notification", notification };
963
1066
  }
964
- if ("result" in dict) {
1067
+ if (asObject(dict.result) !== void 0) {
965
1068
  return { kind: "match", match: parseStreamCallbackMatch(dict.result) };
966
1069
  }
967
1070
  return { kind: "keepalive" };
@@ -1040,6 +1143,7 @@ function startLongpoll(cfg) {
1040
1143
 
1041
1144
  // src/streams.ts
1042
1145
  var API_BASE = "https://api.audd.io";
1146
+ var LONGPOLL_TIMEOUT_MARGIN_SEC = 10;
1043
1147
  var NO_CALLBACK_ERROR_CODE = 19;
1044
1148
  var PREFLIGHT_NO_CALLBACK_HINT = "Longpoll won't deliver events because no callback URL is configured for this account. Set one first via streams.setCallbackUrl(...) \u2014 `https://audd.tech/empty/` is fine if you only want longpolling and don't need a real receiver. To skip this check, pass skipCallbackCheck: true.";
1045
1149
  function decodeSuccess(body, httpStatus, requestId) {
@@ -1064,34 +1168,26 @@ function decodeSuccess(body, httpStatus, requestId) {
1064
1168
  rawResponse: obj
1065
1169
  });
1066
1170
  }
1067
- async function runRetried(fn, policy) {
1068
- try {
1069
- return await retry(fn, policy);
1070
- } catch (err) {
1071
- if (err instanceof TypeError) {
1072
- throw new AudDConnectionError(`Network error: ${err.message}`, err);
1073
- }
1074
- if (err !== null && typeof err === "object" && err.name === "AbortError") {
1075
- throw new AudDConnectionError("Request was aborted (timeout)", err);
1076
- }
1077
- throw err;
1078
- }
1079
- }
1080
1171
  var Streams = class {
1081
- constructor(http, readPolicy, mutatingPolicy, apiToken) {
1172
+ constructor(http, readPolicy, mutatingPolicy, apiToken, onEvent) {
1082
1173
  this.http = http;
1083
1174
  this.readPolicy = readPolicy;
1084
1175
  this.mutatingPolicy = mutatingPolicy;
1085
1176
  this.apiToken = apiToken;
1177
+ this.onEvent = onEvent;
1086
1178
  }
1087
1179
  http;
1088
1180
  readPolicy;
1089
1181
  mutatingPolicy;
1090
1182
  apiToken;
1183
+ onEvent;
1091
1184
  async post(path2, fields, policy) {
1092
- const resp = await runRetried(
1093
- () => this.http.postForm(`${API_BASE}/${path2}/`, fields),
1094
- policy
1185
+ const url = `${API_BASE}/${path2}/`;
1186
+ const resp = await emitAround(
1187
+ this.onEvent,
1188
+ path2,
1189
+ url,
1190
+ () => runRetried(() => this.http.postForm(url, fields), policy)
1095
1191
  );
1096
1192
  return decodeSuccess(resp.jsonBody, resp.httpStatus, resp.requestId);
1097
1193
  }
@@ -1138,11 +1234,11 @@ var Streams = class {
1138
1234
  async list() {
1139
1235
  const result = await this.post("getStreams", {}, this.readPolicy);
1140
1236
  if (!Array.isArray(result)) return [];
1141
- return result.map(parseStream);
1237
+ return result.filter((x) => asObject(x) !== void 0).map(parseStream);
1142
1238
  }
1143
1239
  /** Compute the 9-char longpoll category locally — pure, no network. */
1144
1240
  deriveLongpollCategory(radioId) {
1145
- return deriveLongpollCategory(this.apiToken, radioId);
1241
+ return deriveLongpollCategory(this.apiToken(), radioId);
1146
1242
  }
1147
1243
  /**
1148
1244
  * Parse a callback POST body into `{ match, notification }`. Pass an
@@ -1184,13 +1280,21 @@ var Streams = class {
1184
1280
  const timeoutSec = effectiveOpts.timeout ?? 50;
1185
1281
  const httpClient = this.http;
1186
1282
  const readPolicy = this.readPolicy;
1283
+ const onEvent = this.onEvent;
1284
+ const longpollUrl = `${API_BASE}/longpoll/`;
1285
+ const timeoutMs = (timeoutSec + LONGPOLL_TIMEOUT_MARGIN_SEC) * 1e3;
1187
1286
  return startLongpoll({
1188
1287
  category,
1189
1288
  timeout: timeoutSec,
1190
1289
  sinceTime: effectiveOpts.sinceTime,
1191
- fetchOnce: (params, signal) => runRetried(
1192
- () => httpClient.get(`${API_BASE}/longpoll/`, params, { signal }),
1193
- readPolicy
1290
+ fetchOnce: (params, signal) => emitAround(
1291
+ onEvent,
1292
+ "longpoll",
1293
+ longpollUrl,
1294
+ () => runRetried(
1295
+ () => httpClient.get(longpollUrl, params, { signal, timeoutMs }),
1296
+ readPolicy
1297
+ )
1194
1298
  )
1195
1299
  });
1196
1300
  }
@@ -1235,26 +1339,15 @@ function decodeSuccess2(body, httpStatus, requestId) {
1235
1339
  });
1236
1340
  }
1237
1341
  }
1238
- async function runRetried2(fn, policy) {
1239
- try {
1240
- return await retry(fn, policy);
1241
- } catch (err) {
1242
- if (err instanceof TypeError) {
1243
- throw new AudDConnectionError(`Network error: ${err.message}`, err);
1244
- }
1245
- if (err !== null && typeof err === "object" && err.name === "AbortError") {
1246
- throw new AudDConnectionError("Request was aborted (timeout)", err);
1247
- }
1248
- throw err;
1249
- }
1250
- }
1251
1342
  var CustomCatalog = class {
1252
- constructor(http, noRetryPolicy) {
1343
+ constructor(http, noRetryPolicy, onEvent) {
1253
1344
  this.http = http;
1254
1345
  this.noRetryPolicy = noRetryPolicy;
1346
+ this.onEvent = onEvent;
1255
1347
  }
1256
1348
  http;
1257
1349
  noRetryPolicy;
1350
+ onEvent;
1258
1351
  /**
1259
1352
  * **This is NOT how you submit audio for music recognition.** For
1260
1353
  * recognition, use `audd.recognize()` (or `audd.recognizeEnterprise()` for
@@ -1275,42 +1368,36 @@ var CustomCatalog = class {
1275
1368
  async add(opts) {
1276
1369
  const reopen = prepareSource(opts.source);
1277
1370
  const audioId = String(opts.audioId);
1278
- const resp = await runRetried2(async () => {
1279
- const prepared = await reopen();
1280
- const fields = { ...prepared.fields, audio_id: audioId };
1281
- return this.http.postForm(UPLOAD_URL, fields);
1282
- }, this.noRetryPolicy);
1371
+ const resp = await emitAround(
1372
+ this.onEvent,
1373
+ "customCatalogAdd",
1374
+ UPLOAD_URL,
1375
+ () => runRetried(async () => {
1376
+ const prepared = await reopen();
1377
+ const fields = { ...prepared.fields, audio_id: audioId };
1378
+ return this.http.postForm(UPLOAD_URL, fields);
1379
+ }, this.noRetryPolicy)
1380
+ );
1283
1381
  decodeSuccess2(resp.jsonBody, resp.httpStatus, resp.requestId);
1284
1382
  }
1285
1383
  };
1286
1384
 
1287
1385
  // src/advanced.ts
1288
1386
  var API_BASE2 = "https://api.audd.io";
1289
- async function runRetried3(fn, policy) {
1290
- try {
1291
- return await retry(fn, policy);
1292
- } catch (err) {
1293
- if (err instanceof TypeError) {
1294
- throw new AudDConnectionError(`Network error: ${err.message}`, err);
1295
- }
1296
- if (err !== null && typeof err === "object" && err.name === "AbortError") {
1297
- throw new AudDConnectionError("Request was aborted (timeout)", err);
1298
- }
1299
- throw err;
1300
- }
1301
- }
1302
1387
  var Advanced = class {
1303
1388
  /**
1304
1389
  * @param recognitionPolicy The findLyrics endpoint is metered and shouldn't
1305
1390
  * double-bill on post-upload read timeout: Advanced uses RECOGNITION retry
1306
1391
  * policy, not READ.
1307
1392
  */
1308
- constructor(http, recognitionPolicy) {
1393
+ constructor(http, recognitionPolicy, onEvent) {
1309
1394
  this.http = http;
1310
1395
  this.recognitionPolicy = recognitionPolicy;
1396
+ this.onEvent = onEvent;
1311
1397
  }
1312
1398
  http;
1313
1399
  recognitionPolicy;
1400
+ onEvent;
1314
1401
  /** Find lyrics by free-text query. Returns a list of matches (possibly empty). */
1315
1402
  async findLyrics(query) {
1316
1403
  const body = await this.rawRequest("findLyrics", { q: query });
@@ -1334,9 +1421,12 @@ var Advanced = class {
1334
1421
  if (v === void 0) continue;
1335
1422
  fields[k] = String(v);
1336
1423
  }
1337
- const resp = await runRetried3(
1338
- () => this.http.postForm(`${API_BASE2}/${method}/`, fields),
1339
- this.recognitionPolicy
1424
+ const url = `${API_BASE2}/${method}/`;
1425
+ const resp = await emitAround(
1426
+ this.onEvent,
1427
+ method,
1428
+ url,
1429
+ () => runRetried(() => this.http.postForm(url, fields), this.recognitionPolicy)
1340
1430
  );
1341
1431
  const body = resp.jsonBody;
1342
1432
  if (typeof body !== "object" || body === null || Array.isArray(body)) {
@@ -1352,13 +1442,6 @@ var ENTERPRISE_BASE = "https://enterprise.audd.io";
1352
1442
  var DEPRECATED_PARAMS_CODE = 51;
1353
1443
  var HTTP_CLIENT_ERROR_FLOOR2 = 400;
1354
1444
  var TOKEN_ENV_VAR = "AUDD_API_TOKEN";
1355
- function safeEmit(hook, event) {
1356
- if (hook === void 0) return;
1357
- try {
1358
- hook(event);
1359
- } catch {
1360
- }
1361
- }
1362
1445
  function resolveToken(apiToken) {
1363
1446
  if (apiToken !== void 0 && apiToken !== "") return apiToken;
1364
1447
  const env = (typeof process !== "undefined" ? process.env[TOKEN_ENV_VAR] : void 0) ?? "";
@@ -1431,7 +1514,7 @@ function decodeOrRaise(resp) {
1431
1514
  function decodeRecognize(resp) {
1432
1515
  const body = decodeOrRaise(resp);
1433
1516
  const result = body["result"];
1434
- if (result == null) return null;
1517
+ if (asObject(result) === void 0) return null;
1435
1518
  return parseRecognitionResult(result);
1436
1519
  }
1437
1520
  function decodeEnterprise(resp) {
@@ -1440,24 +1523,12 @@ function decodeEnterprise(resp) {
1440
1523
  if (!Array.isArray(chunks)) return [];
1441
1524
  const out = [];
1442
1525
  for (const c of chunks) {
1526
+ if (asObject(c) === void 0) continue;
1443
1527
  const chunk = parseEnterpriseChunkResult(c);
1444
1528
  out.push(...chunk.songs);
1445
1529
  }
1446
1530
  return out;
1447
1531
  }
1448
- async function runRetried4(fn, policy) {
1449
- try {
1450
- return await retry(fn, policy);
1451
- } catch (err) {
1452
- if (err instanceof TypeError) {
1453
- throw new AudDConnectionError(`Network error: ${err.message}`, err);
1454
- }
1455
- if (err !== null && typeof err === "object" && err.name === "AbortError") {
1456
- throw new AudDConnectionError("Request was aborted (timeout)", err);
1457
- }
1458
- throw err;
1459
- }
1460
- }
1461
1532
  var AudD = class {
1462
1533
  _http;
1463
1534
  _enterpriseHttp;
@@ -1515,7 +1586,8 @@ var AudD = class {
1515
1586
  this._http,
1516
1587
  this.policyFor("read"),
1517
1588
  this.policyFor("mutating"),
1518
- this._apiToken
1589
+ () => this._apiToken,
1590
+ this._onEvent
1519
1591
  );
1520
1592
  }
1521
1593
  return this._streams;
@@ -1523,14 +1595,14 @@ var AudD = class {
1523
1595
  /** Sub-namespace for the private fingerprint catalog. NOT for recognition. */
1524
1596
  get customCatalog() {
1525
1597
  if (this._customCatalog === void 0) {
1526
- this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"));
1598
+ this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"), this._onEvent);
1527
1599
  }
1528
1600
  return this._customCatalog;
1529
1601
  }
1530
1602
  /** Lyrics search + raw-request escape hatch. */
1531
1603
  get advanced() {
1532
1604
  if (this._advanced === void 0) {
1533
- this._advanced = new Advanced(this._http, this.policyFor("recognition"));
1605
+ this._advanced = new Advanced(this._http, this.policyFor("recognition"), this._onEvent);
1534
1606
  }
1535
1607
  return this._advanced;
1536
1608
  }
@@ -1546,20 +1618,11 @@ var AudD = class {
1546
1618
  const market = opts.market;
1547
1619
  const policy = this.policyFor("recognition");
1548
1620
  const url = `${API_BASE3}/`;
1549
- const startedAt = Date.now();
1550
- safeEmit(this._onEvent, {
1551
- kind: "request",
1552
- method: "recognize",
1621
+ const resp = await emitAround(
1622
+ this._onEvent,
1623
+ "recognize",
1553
1624
  url,
1554
- requestId: null,
1555
- httpStatus: null,
1556
- elapsedMs: null,
1557
- errorCode: null,
1558
- extras: {}
1559
- });
1560
- let resp;
1561
- try {
1562
- resp = await runRetried4(async () => {
1625
+ () => runRetried(async () => {
1563
1626
  const prepared = await reopen();
1564
1627
  const fields = { ...prepared.fields };
1565
1628
  if (opts.extraParameters) Object.assign(fields, opts.extraParameters);
@@ -1569,30 +1632,8 @@ var AudD = class {
1569
1632
  ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
1570
1633
  ...opts.signal !== void 0 ? { signal: opts.signal } : {}
1571
1634
  });
1572
- }, policy);
1573
- } catch (err) {
1574
- safeEmit(this._onEvent, {
1575
- kind: "exception",
1576
- method: "recognize",
1577
- url,
1578
- requestId: null,
1579
- httpStatus: null,
1580
- elapsedMs: Date.now() - startedAt,
1581
- errorCode: null,
1582
- extras: { name: err instanceof Error ? err.name : String(err) }
1583
- });
1584
- throw err;
1585
- }
1586
- safeEmit(this._onEvent, {
1587
- kind: "response",
1588
- method: "recognize",
1589
- url,
1590
- requestId: resp.requestId,
1591
- httpStatus: resp.httpStatus,
1592
- elapsedMs: Date.now() - startedAt,
1593
- errorCode: null,
1594
- extras: {}
1595
- });
1635
+ }, policy)
1636
+ );
1596
1637
  return decodeRecognize(resp);
1597
1638
  }
1598
1639
  /**
@@ -1605,14 +1646,20 @@ var AudD = class {
1605
1646
  const reopen = prepareSource(source);
1606
1647
  const extra = buildEnterpriseFields(opts);
1607
1648
  const policy = this.policyFor("recognition");
1608
- const resp = await runRetried4(async () => {
1609
- const prepared = await reopen();
1610
- const fields = { ...prepared.fields, ...extra };
1611
- return this._enterpriseHttp.postForm(`${ENTERPRISE_BASE}/`, fields, {
1612
- ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
1613
- ...opts.signal !== void 0 ? { signal: opts.signal } : {}
1614
- });
1615
- }, policy);
1649
+ const url = `${ENTERPRISE_BASE}/`;
1650
+ const resp = await emitAround(
1651
+ this._onEvent,
1652
+ "recognizeEnterprise",
1653
+ url,
1654
+ () => runRetried(async () => {
1655
+ const prepared = await reopen();
1656
+ const fields = { ...prepared.fields, ...extra };
1657
+ return this._enterpriseHttp.postForm(url, fields, {
1658
+ ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
1659
+ ...opts.signal !== void 0 ? { signal: opts.signal } : {}
1660
+ });
1661
+ }, policy)
1662
+ );
1616
1663
  return decodeEnterprise(resp);
1617
1664
  }
1618
1665
  /** Release any underlying resources. (Currently a no-op for `fetch`-based transport.) */