@microsoft/webiq 0.1.7 → 1.0.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 +180 -62
- package/dist/index.d.mts +230 -14
- package/dist/index.d.ts +230 -14
- package/dist/index.js +404 -10
- package/dist/index.mjs +387 -11
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -332,9 +332,31 @@ var APIStatusError = class _APIStatusError extends WebIQError {
|
|
|
332
332
|
this.name = "APIStatusError";
|
|
333
333
|
this.statusCode = statusCode;
|
|
334
334
|
this.body = options?.body;
|
|
335
|
-
this.requestId = options?.requestId;
|
|
336
|
-
this.traceId = options?.traceId;
|
|
337
335
|
Object.setPrototypeOf(this, _APIStatusError.prototype);
|
|
336
|
+
this.requestId = options?.requestId ?? this.stringField("requestId");
|
|
337
|
+
this.traceId = options?.traceId ?? this.stringField("traceId");
|
|
338
|
+
}
|
|
339
|
+
stringField(name) {
|
|
340
|
+
if (this.body && typeof this.body === "object" && name in this.body) {
|
|
341
|
+
const value = this.body[name];
|
|
342
|
+
return typeof value === "string" ? value : void 0;
|
|
343
|
+
}
|
|
344
|
+
return void 0;
|
|
345
|
+
}
|
|
346
|
+
get errorCode() {
|
|
347
|
+
return this.stringField("errorCode");
|
|
348
|
+
}
|
|
349
|
+
get errorCategory() {
|
|
350
|
+
return this.stringField("errorCategory");
|
|
351
|
+
}
|
|
352
|
+
get userMessage() {
|
|
353
|
+
return this.stringField("userMessage");
|
|
354
|
+
}
|
|
355
|
+
get technicalDetails() {
|
|
356
|
+
return this.stringField("technicalDetails");
|
|
357
|
+
}
|
|
358
|
+
get retryAfter() {
|
|
359
|
+
return this.stringField("retryAfter");
|
|
338
360
|
}
|
|
339
361
|
};
|
|
340
362
|
var RateLimitError = class _RateLimitError extends APIStatusError {
|
|
@@ -351,6 +373,20 @@ var RateLimitError = class _RateLimitError extends APIStatusError {
|
|
|
351
373
|
return void 0;
|
|
352
374
|
}
|
|
353
375
|
};
|
|
376
|
+
var BadRequestError = class _BadRequestError extends APIStatusError {
|
|
377
|
+
constructor(statusCode, message, options) {
|
|
378
|
+
super(statusCode, message, options);
|
|
379
|
+
this.name = "BadRequestError";
|
|
380
|
+
Object.setPrototypeOf(this, _BadRequestError.prototype);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
var NotFoundError = class _NotFoundError extends APIStatusError {
|
|
384
|
+
constructor(statusCode, message, options) {
|
|
385
|
+
super(statusCode, message, options);
|
|
386
|
+
this.name = "NotFoundError";
|
|
387
|
+
Object.setPrototypeOf(this, _NotFoundError.prototype);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
354
390
|
var AuthenticationError = class _AuthenticationError extends APIStatusError {
|
|
355
391
|
constructor(statusCode, message, options) {
|
|
356
392
|
super(statusCode, message, options);
|
|
@@ -398,7 +434,7 @@ var RetryPolicy = class {
|
|
|
398
434
|
this.maxRetries = options?.maxRetries ?? 2;
|
|
399
435
|
this.baseDelaySeconds = options?.baseDelaySeconds ?? 0.25;
|
|
400
436
|
this.maxDelaySeconds = options?.maxDelaySeconds ?? 4;
|
|
401
|
-
this.retryOnStatus = new Set(options?.retryOnStatus ?? [408, 500, 502,
|
|
437
|
+
this.retryOnStatus = new Set(options?.retryOnStatus ?? [408, 500, 502, 520, 523, 524]);
|
|
402
438
|
}
|
|
403
439
|
/**
|
|
404
440
|
* Compute the delay before the next retry attempt.
|
|
@@ -409,7 +445,7 @@ var RetryPolicy = class {
|
|
|
409
445
|
*/
|
|
410
446
|
computeDelay(attempt, retryAfter) {
|
|
411
447
|
if (retryAfter !== void 0) {
|
|
412
|
-
return
|
|
448
|
+
return retryAfter;
|
|
413
449
|
}
|
|
414
450
|
const delay = Math.random() * this.baseDelaySeconds * Math.pow(2, attempt - 1);
|
|
415
451
|
return Math.min(delay, this.maxDelaySeconds);
|
|
@@ -421,8 +457,24 @@ var RetryPolicy = class {
|
|
|
421
457
|
return this.retryOnStatus.has(statusCode);
|
|
422
458
|
}
|
|
423
459
|
};
|
|
424
|
-
function sleep(seconds) {
|
|
425
|
-
|
|
460
|
+
async function sleep(seconds, signal) {
|
|
461
|
+
let remainingMs = seconds * 1e3;
|
|
462
|
+
do {
|
|
463
|
+
signal?.throwIfAborted();
|
|
464
|
+
const delayMs = Math.min(remainingMs, 2147483647);
|
|
465
|
+
await new Promise((resolve, reject) => {
|
|
466
|
+
const onAbort = () => {
|
|
467
|
+
clearTimeout(timer);
|
|
468
|
+
reject(signal?.reason ?? new DOMException("Request aborted", "AbortError"));
|
|
469
|
+
};
|
|
470
|
+
const timer = setTimeout(() => {
|
|
471
|
+
signal?.removeEventListener("abort", onAbort);
|
|
472
|
+
resolve();
|
|
473
|
+
}, delayMs);
|
|
474
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
475
|
+
});
|
|
476
|
+
remainingMs -= delayMs;
|
|
477
|
+
} while (remainingMs > 0);
|
|
426
478
|
}
|
|
427
479
|
|
|
428
480
|
// src/telemetry.ts
|
|
@@ -467,15 +519,18 @@ function extractMetadata(body, headers) {
|
|
|
467
519
|
}
|
|
468
520
|
var HttpTransport = class {
|
|
469
521
|
baseUrl;
|
|
522
|
+
appId;
|
|
470
523
|
auth;
|
|
471
524
|
telemetry;
|
|
472
525
|
retry;
|
|
473
526
|
timeout;
|
|
474
527
|
fetchFn;
|
|
475
528
|
activeControllers = /* @__PURE__ */ new Set();
|
|
529
|
+
closeController = new AbortController();
|
|
476
530
|
closed = false;
|
|
477
531
|
constructor(options) {
|
|
478
532
|
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
533
|
+
this.appId = options.appId;
|
|
479
534
|
this.auth = options.auth;
|
|
480
535
|
this.telemetry = options.telemetryHook;
|
|
481
536
|
this.retry = options.retry ?? new RetryPolicy();
|
|
@@ -484,7 +539,19 @@ var HttpTransport = class {
|
|
|
484
539
|
}
|
|
485
540
|
async request(options) {
|
|
486
541
|
const { method, path, json, signal: callerSignal } = options;
|
|
542
|
+
const retrySignal = callerSignal ? AbortSignal.any([callerSignal, this.closeController.signal]) : this.closeController.signal;
|
|
487
543
|
const effectiveTimeout = options.timeout ?? this.timeout;
|
|
544
|
+
let url = `${this.baseUrl}${path}`;
|
|
545
|
+
if (this.appId) {
|
|
546
|
+
const fragmentIndex = url.indexOf("#");
|
|
547
|
+
const fragment = fragmentIndex < 0 ? "" : url.slice(fragmentIndex);
|
|
548
|
+
const target = fragmentIndex < 0 ? url : url.slice(0, fragmentIndex);
|
|
549
|
+
const queryIndex = target.indexOf("?");
|
|
550
|
+
const pathname = queryIndex < 0 ? target : target.slice(0, queryIndex);
|
|
551
|
+
const params = new URLSearchParams(queryIndex < 0 ? "" : target.slice(queryIndex + 1));
|
|
552
|
+
params.set("appid", this.appId);
|
|
553
|
+
url = `${pathname}?${params.toString()}${fragment}`;
|
|
554
|
+
}
|
|
488
555
|
let attempt = 0;
|
|
489
556
|
while (true) {
|
|
490
557
|
if (this.closed) {
|
|
@@ -503,7 +570,7 @@ var HttpTransport = class {
|
|
|
503
570
|
const fetchSignal = callerSignal ? AbortSignal.any([callerSignal, abortController.signal]) : abortController.signal;
|
|
504
571
|
let response;
|
|
505
572
|
try {
|
|
506
|
-
response = await this.fetchFn(
|
|
573
|
+
response = await this.fetchFn(url, {
|
|
507
574
|
method,
|
|
508
575
|
headers: {
|
|
509
576
|
"Content-Type": "application/json",
|
|
@@ -538,11 +605,13 @@ var HttpTransport = class {
|
|
|
538
605
|
traceId,
|
|
539
606
|
retryAfter,
|
|
540
607
|
elapsedMs: elapsed,
|
|
608
|
+
responseSizeBytes: responseText ? new TextEncoder().encode(responseText).length : 0,
|
|
541
609
|
errorBody: response.status >= 400 ? body : void 0
|
|
542
610
|
})
|
|
543
611
|
);
|
|
544
612
|
if (response.status === 401) {
|
|
545
|
-
|
|
613
|
+
const message = body && typeof body === "object" && "userMessage" in body ? String(body.userMessage) : "Unauthorized";
|
|
614
|
+
throw new AuthenticationError(response.status, message, {
|
|
546
615
|
body,
|
|
547
616
|
requestId,
|
|
548
617
|
traceId
|
|
@@ -563,9 +632,17 @@ var HttpTransport = class {
|
|
|
563
632
|
}
|
|
564
633
|
if (this.retry.shouldRetry(response.status) && attempt <= this.retry.maxRetries) {
|
|
565
634
|
const delay = this.retry.computeDelay(attempt, parseRetryAfter(retryAfter));
|
|
566
|
-
await sleep(delay);
|
|
635
|
+
await sleep(delay, retrySignal);
|
|
567
636
|
continue;
|
|
568
637
|
}
|
|
638
|
+
if (response.status === 400) {
|
|
639
|
+
const message = body && typeof body === "object" && "userMessage" in body ? String(body.userMessage) : "Bad request";
|
|
640
|
+
throw new BadRequestError(response.status, message, { body, requestId, traceId });
|
|
641
|
+
}
|
|
642
|
+
if (response.status === 404) {
|
|
643
|
+
const message = body && typeof body === "object" && "userMessage" in body ? String(body.userMessage) : "Not found";
|
|
644
|
+
throw new NotFoundError(response.status, message, { body, requestId, traceId });
|
|
645
|
+
}
|
|
569
646
|
if (response.status >= 400) {
|
|
570
647
|
const message = body && typeof body === "object" && "userMessage" in body ? String(body.userMessage) : `HTTP ${response.status}`;
|
|
571
648
|
throw new APIStatusError(response.status, message, { body, requestId, traceId });
|
|
@@ -595,7 +672,7 @@ var HttpTransport = class {
|
|
|
595
672
|
});
|
|
596
673
|
}
|
|
597
674
|
if (attempt <= this.retry.maxRetries) {
|
|
598
|
-
await sleep(this.retry.computeDelay(attempt));
|
|
675
|
+
await sleep(this.retry.computeDelay(attempt), retrySignal);
|
|
599
676
|
continue;
|
|
600
677
|
}
|
|
601
678
|
throw new APIConnectionError(
|
|
@@ -607,13 +684,293 @@ var HttpTransport = class {
|
|
|
607
684
|
}
|
|
608
685
|
async close() {
|
|
609
686
|
this.closed = true;
|
|
687
|
+
this.closeController.abort(new APIConnectionError("Transport is closed"));
|
|
610
688
|
Array.from(this.activeControllers).forEach((controller) => controller.abort());
|
|
611
689
|
this.activeControllers.clear();
|
|
612
690
|
}
|
|
613
691
|
};
|
|
614
692
|
|
|
693
|
+
// src/feedback.ts
|
|
694
|
+
var FeedbackError = class extends Error {
|
|
695
|
+
constructor(code, message, statusCode) {
|
|
696
|
+
super(message);
|
|
697
|
+
this.code = code;
|
|
698
|
+
this.statusCode = statusCode;
|
|
699
|
+
this.name = new.target.name;
|
|
700
|
+
}
|
|
701
|
+
code;
|
|
702
|
+
statusCode;
|
|
703
|
+
};
|
|
704
|
+
var InvalidFeedbackInputError = class extends FeedbackError {
|
|
705
|
+
constructor(message = "Invalid feedback input.", code = "invalid_feedback_input") {
|
|
706
|
+
super(code, message);
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
var MissingInstrumentationError = class extends FeedbackError {
|
|
710
|
+
constructor() {
|
|
711
|
+
super("missing_citation_base", "Citation instrumentation is unavailable.");
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
var InvalidInstrumentationSuffixError = class extends FeedbackError {
|
|
715
|
+
constructor(code = "malformed_instrumentation_suffix") {
|
|
716
|
+
super(code, "Every cited result requires a valid instrumentation suffix.");
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
var FeedbackConnectionError = class extends FeedbackError {
|
|
720
|
+
constructor() {
|
|
721
|
+
super("feedback_connection", "Feedback request failed.");
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
var FeedbackTimeoutError = class extends FeedbackError {
|
|
725
|
+
constructor() {
|
|
726
|
+
super("feedback_timeout", "Feedback request timed out.");
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
var FeedbackCancelledError = class extends FeedbackError {
|
|
730
|
+
constructor() {
|
|
731
|
+
super("feedback_cancelled", "Feedback request was cancelled.");
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
var FeedbackHttpError = class extends FeedbackError {
|
|
735
|
+
constructor(statusCode) {
|
|
736
|
+
super("feedback_http", "Feedback endpoint returned an unsuccessful response.", statusCode);
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
var FeedbackNavigationError = class extends FeedbackError {
|
|
740
|
+
constructor() {
|
|
741
|
+
super("feedback_navigation", "Feedback navigation failed.");
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
function isPresent(value) {
|
|
745
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
746
|
+
}
|
|
747
|
+
function buildClickPingUrl(response, result) {
|
|
748
|
+
if (isPresent(result.clickUrl) || !isPresent(response.instrumentationClickBase) || !isPresent(result.instrumentationSuffix)) {
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
return response.instrumentationClickBase + result.instrumentationSuffix;
|
|
752
|
+
}
|
|
753
|
+
function prepareClick(response, result) {
|
|
754
|
+
if (isPresent(result.clickUrl)) {
|
|
755
|
+
return { kind: "redirect", navigationUrl: result.clickUrl, pingUrl: null };
|
|
756
|
+
}
|
|
757
|
+
if (!isPresent(result.url)) {
|
|
758
|
+
throw new InvalidFeedbackInputError(
|
|
759
|
+
"A non-empty url or clickUrl is required to prepare a click.",
|
|
760
|
+
"missing_navigation_url"
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
const pingUrl = buildClickPingUrl(response, result);
|
|
764
|
+
return {
|
|
765
|
+
kind: pingUrl === null ? "untracked" : "ping",
|
|
766
|
+
navigationUrl: result.url,
|
|
767
|
+
pingUrl
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
function buildCitationPingUrl(response, results) {
|
|
771
|
+
const base = response.instrumentationCitationBase;
|
|
772
|
+
if (!isPresent(base)) {
|
|
773
|
+
throw new MissingInstrumentationError();
|
|
774
|
+
}
|
|
775
|
+
if (results.length === 0) {
|
|
776
|
+
throw new InvalidFeedbackInputError(
|
|
777
|
+
"At least one cited result is required.",
|
|
778
|
+
"empty_citation_results"
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
const payload = results.map((result) => {
|
|
782
|
+
const suffix = result.instrumentationSuffix;
|
|
783
|
+
if (!isPresent(suffix)) {
|
|
784
|
+
throw new InvalidInstrumentationSuffixError("missing_instrumentation_suffix");
|
|
785
|
+
}
|
|
786
|
+
const comma = suffix.indexOf(",");
|
|
787
|
+
const appNamespace = comma < 0 ? "" : suffix.slice(0, comma);
|
|
788
|
+
const key = comma < 0 ? "" : suffix.slice(comma + 1);
|
|
789
|
+
if (!isPresent(appNamespace) || !isPresent(key)) {
|
|
790
|
+
throw new InvalidInstrumentationSuffixError();
|
|
791
|
+
}
|
|
792
|
+
return { T: "CI.Info", FID: "LLM", Name: "citation", AppNS: appNamespace, K: key };
|
|
793
|
+
});
|
|
794
|
+
return base + encodeURIComponent(JSON.stringify(payload)).replace(
|
|
795
|
+
/[!'()*]/g,
|
|
796
|
+
(character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// src/feedback-client.ts
|
|
801
|
+
var MAX_FEEDBACK_TIMEOUT = 2147483647;
|
|
802
|
+
var DefaultFeedbackSender = class {
|
|
803
|
+
async send(url, options) {
|
|
804
|
+
const init = {
|
|
805
|
+
method: "GET",
|
|
806
|
+
credentials: "omit",
|
|
807
|
+
redirect: "error",
|
|
808
|
+
referrerPolicy: "no-referrer",
|
|
809
|
+
cache: "no-store",
|
|
810
|
+
signal: options?.signal
|
|
811
|
+
};
|
|
812
|
+
const response = await globalThis.fetch(url, init);
|
|
813
|
+
await response.body?.cancel();
|
|
814
|
+
return { status: response.status };
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
function record(value) {
|
|
818
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
819
|
+
}
|
|
820
|
+
function validateFields(value, fields) {
|
|
821
|
+
if (!record(value) || fields.some((key) => value[key] != null && typeof value[key] !== "string")) {
|
|
822
|
+
throw new InvalidFeedbackInputError();
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
function validate(options) {
|
|
826
|
+
if (!record(options)) throw new InvalidFeedbackInputError();
|
|
827
|
+
validateFields(options.response, ["instrumentationClickBase", "instrumentationCitationBase"]);
|
|
828
|
+
if (!["citation", "click"].includes(options.type) || !Array.isArray(options.events) || options.events.length === 0 || options.type === "click" && options.events.length !== 1 || options.navigation !== void 0 && !["auto", "manual"].includes(options.navigation) || options.navigationPolicy !== void 0 && !["always", "on-feedback-success"].includes(options.navigationPolicy) || options.failureMode !== void 0 && !["collect", "throw"].includes(options.failureMode) || options.signal !== void 0 && (!record(options.signal) || typeof options.signal.aborted !== "boolean" || typeof options.signal.addEventListener !== "function" || typeof options.signal.removeEventListener !== "function") || options.timeout !== void 0 && (!Number.isFinite(options.timeout) || options.timeout <= 0 || options.timeout > MAX_FEEDBACK_TIMEOUT)) {
|
|
829
|
+
throw new InvalidFeedbackInputError();
|
|
830
|
+
}
|
|
831
|
+
for (const event of options.events) {
|
|
832
|
+
if (!record(event) || "type" in event) throw new InvalidFeedbackInputError();
|
|
833
|
+
if (options.type === "citation") {
|
|
834
|
+
if ("result" in event || !Array.isArray(event.results) || event.results.length === 0) {
|
|
835
|
+
throw new InvalidFeedbackInputError();
|
|
836
|
+
}
|
|
837
|
+
for (const result of event.results) {
|
|
838
|
+
validateFields(result, ["url", "clickUrl", "instrumentationSuffix"]);
|
|
839
|
+
}
|
|
840
|
+
} else {
|
|
841
|
+
if ("results" in event) throw new InvalidFeedbackInputError();
|
|
842
|
+
validateFields(event.result, ["url", "clickUrl", "instrumentationSuffix"]);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function validateHttpUrl(url) {
|
|
847
|
+
let parsed;
|
|
848
|
+
try {
|
|
849
|
+
parsed = new URL(url);
|
|
850
|
+
} catch {
|
|
851
|
+
throw new InvalidFeedbackInputError("Feedback requires an absolute HTTP(S) URL.");
|
|
852
|
+
}
|
|
853
|
+
if (!["https:", "http:"].includes(parsed.protocol) || parsed.username || parsed.password) {
|
|
854
|
+
throw new InvalidFeedbackInputError(
|
|
855
|
+
"Feedback requires an absolute HTTP(S) URL without credentials."
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function browserNavigation() {
|
|
860
|
+
const runtime = globalThis;
|
|
861
|
+
const location = record(runtime) ? runtime.location : void 0;
|
|
862
|
+
if (record(location) && typeof location.assign === "function") {
|
|
863
|
+
const assign = location.assign.bind(location);
|
|
864
|
+
return {
|
|
865
|
+
navigate: (url) => {
|
|
866
|
+
assign(url);
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
return void 0;
|
|
871
|
+
}
|
|
872
|
+
var FeedbackClient = class {
|
|
873
|
+
sender;
|
|
874
|
+
navigator;
|
|
875
|
+
constructor(options = {}) {
|
|
876
|
+
this.sender = options.sender ?? new DefaultFeedbackSender();
|
|
877
|
+
this.navigator = options.navigationHandler ?? browserNavigation();
|
|
878
|
+
}
|
|
879
|
+
async pushEvents(options) {
|
|
880
|
+
validate(options);
|
|
881
|
+
let outcome;
|
|
882
|
+
let requestUrl;
|
|
883
|
+
if (options.type === "citation") {
|
|
884
|
+
const results = options.events.flatMap((event) => [...event.results]);
|
|
885
|
+
requestUrl = buildCitationPingUrl(options.response, results);
|
|
886
|
+
outcome = {
|
|
887
|
+
type: "citation",
|
|
888
|
+
feedbackStatus: "submitted",
|
|
889
|
+
navigationStatus: "not-applicable",
|
|
890
|
+
resultCount: results.length
|
|
891
|
+
};
|
|
892
|
+
} else {
|
|
893
|
+
const prepared = prepareClick(options.response, options.events[0].result);
|
|
894
|
+
validateHttpUrl(prepared.navigationUrl);
|
|
895
|
+
requestUrl = prepared.pingUrl ?? void 0;
|
|
896
|
+
outcome = {
|
|
897
|
+
type: "click",
|
|
898
|
+
clickMode: prepared.kind,
|
|
899
|
+
feedbackStatus: prepared.kind === "redirect" ? "pending-navigation" : prepared.kind === "ping" ? "submitted" : "not-instrumented",
|
|
900
|
+
navigationStatus: "required",
|
|
901
|
+
navigationUrl: prepared.navigationUrl
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
if (requestUrl !== void 0) validateHttpUrl(requestUrl);
|
|
905
|
+
if (options.signal?.aborted) throw new FeedbackCancelledError();
|
|
906
|
+
if (requestUrl !== void 0) {
|
|
907
|
+
try {
|
|
908
|
+
await this.send(requestUrl, options);
|
|
909
|
+
} catch (error) {
|
|
910
|
+
outcome.feedbackStatus = "failed";
|
|
911
|
+
outcome.feedbackError = error instanceof FeedbackError ? error : new FeedbackConnectionError();
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (outcome.type === "click") {
|
|
915
|
+
if (outcome.feedbackError && options.navigationPolicy === "on-feedback-success") {
|
|
916
|
+
outcome.navigationStatus = "blocked";
|
|
917
|
+
} else if (options.navigation !== "manual" && this.navigator) {
|
|
918
|
+
try {
|
|
919
|
+
await this.navigator.navigate(outcome.navigationUrl);
|
|
920
|
+
outcome.navigationStatus = "navigated";
|
|
921
|
+
if (outcome.clickMode === "redirect") outcome.feedbackStatus = "dispatched";
|
|
922
|
+
} catch {
|
|
923
|
+
outcome.navigationStatus = "failed";
|
|
924
|
+
outcome.navigationError = new FeedbackNavigationError();
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if (options.failureMode === "throw") {
|
|
929
|
+
const error = outcome.feedbackError ?? (outcome.type === "click" ? outcome.navigationError : void 0);
|
|
930
|
+
if (error) throw error;
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
outcomes: [outcome],
|
|
934
|
+
submittedEventCount: outcome.feedbackStatus === "submitted" ? outcome.type === "citation" ? outcome.resultCount : 1 : 0,
|
|
935
|
+
dispatchedEventCount: outcome.feedbackStatus === "dispatched" ? 1 : 0,
|
|
936
|
+
requestCount: requestUrl === void 0 ? 0 : 1,
|
|
937
|
+
navigated: outcome.navigationStatus === "navigated"
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
async send(url, options) {
|
|
941
|
+
const controller = new AbortController();
|
|
942
|
+
const timeout = options.timeout ?? 1e4;
|
|
943
|
+
let timer;
|
|
944
|
+
let onAbort;
|
|
945
|
+
const stopped = new Promise((_, reject) => {
|
|
946
|
+
onAbort = () => {
|
|
947
|
+
reject(new FeedbackCancelledError());
|
|
948
|
+
controller.abort();
|
|
949
|
+
};
|
|
950
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
951
|
+
timer = setTimeout(() => {
|
|
952
|
+
reject(new FeedbackTimeoutError());
|
|
953
|
+
controller.abort();
|
|
954
|
+
}, timeout);
|
|
955
|
+
});
|
|
956
|
+
try {
|
|
957
|
+
const response = await Promise.race([
|
|
958
|
+
this.sender.send(url, { signal: controller.signal, timeout }),
|
|
959
|
+
stopped
|
|
960
|
+
]);
|
|
961
|
+
if (!Number.isInteger(response.status)) throw new FeedbackConnectionError();
|
|
962
|
+
if (response.status < 200 || response.status >= 300)
|
|
963
|
+
throw new FeedbackHttpError(response.status);
|
|
964
|
+
} finally {
|
|
965
|
+
clearTimeout(timer);
|
|
966
|
+
if (onAbort) options.signal?.removeEventListener("abort", onAbort);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
|
|
615
971
|
// src/client.ts
|
|
616
972
|
var WebIQClient = class {
|
|
973
|
+
feedback;
|
|
617
974
|
/** Web search resource */
|
|
618
975
|
web;
|
|
619
976
|
/** News search resource */
|
|
@@ -639,6 +996,7 @@ var WebIQClient = class {
|
|
|
639
996
|
const retry = options.retry instanceof RetryPolicy ? options.retry : options.retry ? new RetryPolicy(options.retry) : new RetryPolicy();
|
|
640
997
|
this._transport = new HttpTransport({
|
|
641
998
|
baseUrl: options.baseUrl ?? "https://api.microsoft.ai/v3",
|
|
999
|
+
appId: options.appId,
|
|
642
1000
|
auth: this._auth,
|
|
643
1001
|
telemetryHook: options.telemetryHook,
|
|
644
1002
|
retry,
|
|
@@ -650,6 +1008,7 @@ var WebIQClient = class {
|
|
|
650
1008
|
this.videos = new VideosResource(this._transport);
|
|
651
1009
|
this.images = new ImagesResource(this._transport);
|
|
652
1010
|
this.browse = new BrowseResource(this._transport);
|
|
1011
|
+
this.feedback = new FeedbackClient(options.feedback);
|
|
653
1012
|
}
|
|
654
1013
|
/**
|
|
655
1014
|
* Close the client and release resources.
|
|
@@ -719,6 +1078,15 @@ var LiveCrawlMode = /* @__PURE__ */ ((LiveCrawlMode2) => {
|
|
|
719
1078
|
return LiveCrawlMode2;
|
|
720
1079
|
})(LiveCrawlMode || {});
|
|
721
1080
|
|
|
1081
|
+
// src/generated-otc/models/RiskLevel.ts
|
|
1082
|
+
var RiskLevel = /* @__PURE__ */ ((RiskLevel2) => {
|
|
1083
|
+
RiskLevel2["LOW"] = "low";
|
|
1084
|
+
RiskLevel2["MODERATE"] = "moderate";
|
|
1085
|
+
RiskLevel2["HIGH"] = "high";
|
|
1086
|
+
RiskLevel2["VERY_HIGH"] = "veryHigh";
|
|
1087
|
+
return RiskLevel2;
|
|
1088
|
+
})(RiskLevel || {});
|
|
1089
|
+
|
|
722
1090
|
// src/generated-otc/models/SafeSearch.ts
|
|
723
1091
|
var SafeSearch = /* @__PURE__ */ ((SafeSearch2) => {
|
|
724
1092
|
SafeSearch2["OFF"] = "off";
|
|
@@ -726,6 +1094,14 @@ var SafeSearch = /* @__PURE__ */ ((SafeSearch2) => {
|
|
|
726
1094
|
return SafeSearch2;
|
|
727
1095
|
})(SafeSearch || {});
|
|
728
1096
|
|
|
1097
|
+
// src/generated-otc/models/SourceQuality.ts
|
|
1098
|
+
var SourceQuality = /* @__PURE__ */ ((SourceQuality2) => {
|
|
1099
|
+
SourceQuality2["INSUFFICIENT"] = "insufficient";
|
|
1100
|
+
SourceQuality2["ADEQUATE"] = "adequate";
|
|
1101
|
+
SourceQuality2["PREFERRED"] = "preferred";
|
|
1102
|
+
return SourceQuality2;
|
|
1103
|
+
})(SourceQuality || {});
|
|
1104
|
+
|
|
729
1105
|
// src/generated-otc/models/VideoDuration.ts
|
|
730
1106
|
var VideoDuration = /* @__PURE__ */ ((VideoDuration2) => {
|
|
731
1107
|
VideoDuration2["SHORT"] = "short";
|
|
@@ -747,31 +1123,49 @@ exports.APIConnectionError = APIConnectionError;
|
|
|
747
1123
|
exports.APIStatusError = APIStatusError;
|
|
748
1124
|
exports.ApiKeyAuth = ApiKeyAuth;
|
|
749
1125
|
exports.AuthenticationError = AuthenticationError;
|
|
1126
|
+
exports.BadRequestError = BadRequestError;
|
|
750
1127
|
exports.BrowseContentFormat = BrowseContentFormat;
|
|
751
1128
|
exports.BrowseResource = BrowseResource;
|
|
752
1129
|
exports.ContentFormat = ContentFormat;
|
|
753
1130
|
exports.ContentTier = ContentTier;
|
|
754
1131
|
exports.DEFAULT_SCOPE = DEFAULT_SCOPE;
|
|
755
1132
|
exports.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
|
|
1133
|
+
exports.DefaultFeedbackSender = DefaultFeedbackSender;
|
|
756
1134
|
exports.EntraIDAuth = EntraIDAuth;
|
|
1135
|
+
exports.FeedbackCancelledError = FeedbackCancelledError;
|
|
1136
|
+
exports.FeedbackClient = FeedbackClient;
|
|
1137
|
+
exports.FeedbackConnectionError = FeedbackConnectionError;
|
|
1138
|
+
exports.FeedbackError = FeedbackError;
|
|
1139
|
+
exports.FeedbackHttpError = FeedbackHttpError;
|
|
1140
|
+
exports.FeedbackNavigationError = FeedbackNavigationError;
|
|
1141
|
+
exports.FeedbackTimeoutError = FeedbackTimeoutError;
|
|
757
1142
|
exports.HttpTransport = HttpTransport;
|
|
758
1143
|
exports.ImageAspectRatio = ImageAspectRatio;
|
|
759
1144
|
exports.ImageColor = ImageColor;
|
|
760
1145
|
exports.ImageSize = ImageSize;
|
|
761
1146
|
exports.ImagesResource = ImagesResource;
|
|
1147
|
+
exports.InvalidFeedbackInputError = InvalidFeedbackInputError;
|
|
1148
|
+
exports.InvalidInstrumentationSuffixError = InvalidInstrumentationSuffixError;
|
|
762
1149
|
exports.LiveCrawlMode = LiveCrawlMode;
|
|
1150
|
+
exports.MissingInstrumentationError = MissingInstrumentationError;
|
|
763
1151
|
exports.NewsResource = NewsResource;
|
|
1152
|
+
exports.NotFoundError = NotFoundError;
|
|
764
1153
|
exports.PermissionDeniedError = PermissionDeniedError;
|
|
765
1154
|
exports.RateLimitError = RateLimitError;
|
|
766
1155
|
exports.RetryPolicy = RetryPolicy;
|
|
1156
|
+
exports.RiskLevel = RiskLevel;
|
|
767
1157
|
exports.SafeSearch = SafeSearch;
|
|
1158
|
+
exports.SourceQuality = SourceQuality;
|
|
768
1159
|
exports.VideoDuration = VideoDuration;
|
|
769
1160
|
exports.VideoResolution = VideoResolution;
|
|
770
1161
|
exports.VideosResource = VideosResource;
|
|
771
1162
|
exports.WebIQClient = WebIQClient;
|
|
772
1163
|
exports.WebIQError = WebIQError;
|
|
773
1164
|
exports.WebResource = WebResource;
|
|
1165
|
+
exports.buildCitationPingUrl = buildCitationPingUrl;
|
|
1166
|
+
exports.buildClickPingUrl = buildClickPingUrl;
|
|
774
1167
|
exports.createAuth = createAuth;
|
|
775
1168
|
exports.createClient = createClient;
|
|
776
1169
|
exports.createTelemetryEvent = createTelemetryEvent;
|
|
1170
|
+
exports.prepareClick = prepareClick;
|
|
777
1171
|
exports.safeCall = safeCall;
|