@optifye/dashboard-core 6.12.2 → 6.12.3
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 +5 -1
- package/dist/index.d.mts +35 -4
- package/dist/index.d.ts +35 -4
- package/dist/index.js +560 -75
- package/dist/index.mjs +556 -76
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2628,19 +2628,206 @@ function getSentry() {
|
|
|
2628
2628
|
return null;
|
|
2629
2629
|
}
|
|
2630
2630
|
}
|
|
2631
|
+
var SENTRY_HANDLED_EVENT_WINDOW_MS = 10 * 60 * 1e3;
|
|
2632
|
+
var SENTRY_HANDLED_EVENT_SESSION_LIMIT = 20;
|
|
2633
|
+
var SENTRY_QUOTA_STORAGE_KEY = "optifye:sentry-handled-quota:v1";
|
|
2634
|
+
var sentryFingerprintSentAt = /* @__PURE__ */ new Map();
|
|
2635
|
+
var handledSentryEventCount = 0;
|
|
2636
|
+
var UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;
|
|
2637
|
+
var LONG_HEX_PATTERN = /\b[0-9a-f]{24,}\b/gi;
|
|
2638
|
+
var EMAIL_PATTERN = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
|
|
2639
|
+
var BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi;
|
|
2640
|
+
var URI_PATTERN = /\b[a-z][a-z0-9+.-]{1,31}:\/\/[^\s)]+/gi;
|
|
2641
|
+
var MEDIA_PATH_PATTERN = /\b[^\s)]+\.m3u8(?:\?[^\s)]*)?/gi;
|
|
2642
|
+
var SENSITIVE_ASSIGNMENT_PATTERN = /\b([A-Za-z0-9_.-]*(?:authorization|cookie|password|secret|token|session|email|api[_-]?key)[A-Za-z0-9_.-]*)\b\s*[:=]\s*["']?[^"'\s,;)}\]]+/gi;
|
|
2643
|
+
var SENSITIVE_KEY_PATTERN = /(authorization|cookie|password|secret|token|session|email|api[_-]?key|url|stream|playlist|media)/i;
|
|
2644
|
+
function resetSentryQuotaForTests() {
|
|
2645
|
+
sentryFingerprintSentAt.clear();
|
|
2646
|
+
handledSentryEventCount = 0;
|
|
2647
|
+
const storage = getBrowserSessionStorage();
|
|
2648
|
+
storage?.removeItem(SENTRY_QUOTA_STORAGE_KEY);
|
|
2649
|
+
}
|
|
2650
|
+
var sanitizeString = (value) => {
|
|
2651
|
+
return value.replace(BEARER_PATTERN, "Bearer [redacted]").replace(SENSITIVE_ASSIGNMENT_PATTERN, (_match, key) => `${key}=[redacted]`).replace(URI_PATTERN, "[url]").replace(MEDIA_PATH_PATTERN, "[media]").replace(EMAIL_PATTERN, "[email]").replace(UUID_PATTERN, ":uuid").replace(LONG_HEX_PATTERN, ":id").replace(/\?.*$/, "").replace(/\s+/g, " ").trim();
|
|
2652
|
+
};
|
|
2653
|
+
var sanitizeRoute = (value) => {
|
|
2654
|
+
if (typeof value !== "string" || value.trim().length === 0) return void 0;
|
|
2655
|
+
try {
|
|
2656
|
+
const parsed = new URL(value, "https://optifye.local");
|
|
2657
|
+
return sanitizeString(parsed.pathname);
|
|
2658
|
+
} catch {
|
|
2659
|
+
return sanitizeString(value.split("?")[0] || value);
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
var sanitizeValue = (value, depth = 0) => {
|
|
2663
|
+
if (depth > 3) return "[truncated]";
|
|
2664
|
+
if (typeof value === "string") return sanitizeString(value);
|
|
2665
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null || value === void 0) return value;
|
|
2666
|
+
if (Array.isArray(value)) {
|
|
2667
|
+
return value.slice(0, 20).map((item) => sanitizeValue(item, depth + 1));
|
|
2668
|
+
}
|
|
2669
|
+
if (typeof value === "object") {
|
|
2670
|
+
const sanitized = {};
|
|
2671
|
+
Object.entries(value).forEach(([key, item]) => {
|
|
2672
|
+
sanitized[key] = SENSITIVE_KEY_PATTERN.test(key) ? "[redacted]" : sanitizeValue(item, depth + 1);
|
|
2673
|
+
});
|
|
2674
|
+
return sanitized;
|
|
2675
|
+
}
|
|
2676
|
+
return String(value);
|
|
2677
|
+
};
|
|
2678
|
+
var sanitizeStack = (stack) => {
|
|
2679
|
+
return stack.split("\n").map((line) => sanitizeString(line)).join("\n");
|
|
2680
|
+
};
|
|
2681
|
+
var sanitizeExtras = (extras) => {
|
|
2682
|
+
if (!extras) return void 0;
|
|
2683
|
+
return sanitizeValue(extras);
|
|
2684
|
+
};
|
|
2685
|
+
var getBrowserSessionStorage = () => {
|
|
2686
|
+
if (typeof window === "undefined") return null;
|
|
2687
|
+
try {
|
|
2688
|
+
return window.sessionStorage;
|
|
2689
|
+
} catch {
|
|
2690
|
+
return null;
|
|
2691
|
+
}
|
|
2692
|
+
};
|
|
2693
|
+
var loadSentryQuotaFromStorage = (now4) => {
|
|
2694
|
+
const storage = getBrowserSessionStorage();
|
|
2695
|
+
if (!storage) return;
|
|
2696
|
+
const rawState = storage.getItem(SENTRY_QUOTA_STORAGE_KEY);
|
|
2697
|
+
if (!rawState) return;
|
|
2698
|
+
try {
|
|
2699
|
+
const parsed = JSON.parse(rawState);
|
|
2700
|
+
const persistedCount = Number(parsed.handledEventCount);
|
|
2701
|
+
handledSentryEventCount = Number.isFinite(persistedCount) && persistedCount > 0 ? Math.floor(persistedCount) : 0;
|
|
2702
|
+
sentryFingerprintSentAt.clear();
|
|
2703
|
+
if (parsed.fingerprintSentAt && typeof parsed.fingerprintSentAt === "object") {
|
|
2704
|
+
Object.entries(parsed.fingerprintSentAt).forEach(([fingerprint, sentAt]) => {
|
|
2705
|
+
const sentAtMs = Number(sentAt);
|
|
2706
|
+
if (Number.isFinite(sentAtMs) && now4 - sentAtMs < SENTRY_HANDLED_EVENT_WINDOW_MS) {
|
|
2707
|
+
sentryFingerprintSentAt.set(fingerprint, sentAtMs);
|
|
2708
|
+
}
|
|
2709
|
+
});
|
|
2710
|
+
}
|
|
2711
|
+
} catch {
|
|
2712
|
+
storage.removeItem(SENTRY_QUOTA_STORAGE_KEY);
|
|
2713
|
+
}
|
|
2714
|
+
};
|
|
2715
|
+
var persistSentryQuotaToStorage = () => {
|
|
2716
|
+
const storage = getBrowserSessionStorage();
|
|
2717
|
+
if (!storage) return;
|
|
2718
|
+
try {
|
|
2719
|
+
storage.setItem(
|
|
2720
|
+
SENTRY_QUOTA_STORAGE_KEY,
|
|
2721
|
+
JSON.stringify({
|
|
2722
|
+
handledEventCount: handledSentryEventCount,
|
|
2723
|
+
fingerprintSentAt: Object.fromEntries(sentryFingerprintSentAt)
|
|
2724
|
+
})
|
|
2725
|
+
);
|
|
2726
|
+
} catch {
|
|
2727
|
+
}
|
|
2728
|
+
};
|
|
2729
|
+
var normalizeMessage = (error) => {
|
|
2730
|
+
const message = error instanceof Error ? error.message : String(error ?? "unknown");
|
|
2731
|
+
return sanitizeString(message.toLowerCase()).slice(0, 180) || "unknown";
|
|
2732
|
+
};
|
|
2733
|
+
var getErrorName = (error) => {
|
|
2734
|
+
if (error && typeof error === "object" && "name" in error) {
|
|
2735
|
+
const name = error.name;
|
|
2736
|
+
if (typeof name === "string" && name.trim()) return sanitizeString(name);
|
|
2737
|
+
}
|
|
2738
|
+
return error instanceof Error ? error.constructor.name : typeof error;
|
|
2739
|
+
};
|
|
2740
|
+
var getStatusFromError = (error) => {
|
|
2741
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
2742
|
+
const statusMatch = message.match(/\((\d{3})\)/) || message.match(/http\s+(\d{3})/i) || message.match(/status:\s*(\d{3})/i);
|
|
2743
|
+
if (!statusMatch) return null;
|
|
2744
|
+
const status = Number.parseInt(statusMatch[1], 10);
|
|
2745
|
+
return Number.isFinite(status) ? status : null;
|
|
2746
|
+
};
|
|
2747
|
+
var hasCaptureOptionShape = (value) => {
|
|
2748
|
+
return "surface" in value || "route" in value || "status" in value || "severity" in value || "quotaKey" in value || "extras" in value;
|
|
2749
|
+
};
|
|
2750
|
+
var normalizeCaptureOptions = (options) => {
|
|
2751
|
+
if (!options) return {};
|
|
2752
|
+
const optionRecord = options;
|
|
2753
|
+
if (!hasCaptureOptionShape(optionRecord)) {
|
|
2754
|
+
return { extras: optionRecord };
|
|
2755
|
+
}
|
|
2756
|
+
const {
|
|
2757
|
+
surface,
|
|
2758
|
+
route,
|
|
2759
|
+
status,
|
|
2760
|
+
severity,
|
|
2761
|
+
quotaKey,
|
|
2762
|
+
extras,
|
|
2763
|
+
...rest
|
|
2764
|
+
} = options;
|
|
2765
|
+
return {
|
|
2766
|
+
surface: typeof surface === "string" ? surface : void 0,
|
|
2767
|
+
route: typeof route === "string" ? route : void 0,
|
|
2768
|
+
status: typeof status === "number" ? status : null,
|
|
2769
|
+
severity: severity === "info" || severity === "warning" || severity === "error" ? severity : void 0,
|
|
2770
|
+
quotaKey: typeof quotaKey === "string" ? quotaKey : void 0,
|
|
2771
|
+
extras: {
|
|
2772
|
+
...rest,
|
|
2773
|
+
...extras && typeof extras === "object" && !Array.isArray(extras) ? extras : {}
|
|
2774
|
+
}
|
|
2775
|
+
};
|
|
2776
|
+
};
|
|
2777
|
+
var buildSentryFingerprint = (error, options) => {
|
|
2778
|
+
if (options.quotaKey) return sanitizeString(options.quotaKey);
|
|
2779
|
+
const surface = sanitizeString(options.surface || "frontend");
|
|
2780
|
+
const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url) || "unknown-route";
|
|
2781
|
+
const status = options.status ?? getStatusFromError(error) ?? "unknown-status";
|
|
2782
|
+
return [
|
|
2783
|
+
surface,
|
|
2784
|
+
route,
|
|
2785
|
+
String(status),
|
|
2786
|
+
getErrorName(error),
|
|
2787
|
+
normalizeMessage(error)
|
|
2788
|
+
].join("|");
|
|
2789
|
+
};
|
|
2790
|
+
var shouldSendHandledSentryEvent = (fingerprint) => {
|
|
2791
|
+
const now4 = Date.now();
|
|
2792
|
+
loadSentryQuotaFromStorage(now4);
|
|
2793
|
+
const lastSentAt = sentryFingerprintSentAt.get(fingerprint);
|
|
2794
|
+
if (lastSentAt !== void 0 && now4 - lastSentAt < SENTRY_HANDLED_EVENT_WINDOW_MS) {
|
|
2795
|
+
return false;
|
|
2796
|
+
}
|
|
2797
|
+
if (handledSentryEventCount >= SENTRY_HANDLED_EVENT_SESSION_LIMIT) {
|
|
2798
|
+
return false;
|
|
2799
|
+
}
|
|
2800
|
+
sentryFingerprintSentAt.set(fingerprint, now4);
|
|
2801
|
+
handledSentryEventCount += 1;
|
|
2802
|
+
persistSentryQuotaToStorage();
|
|
2803
|
+
return true;
|
|
2804
|
+
};
|
|
2805
|
+
var createSanitizedSentryException = (error) => {
|
|
2806
|
+
if (error instanceof Error) {
|
|
2807
|
+
const sanitizedError = new Error(sanitizeString(error.message) || getErrorName(error) || "Error");
|
|
2808
|
+
sanitizedError.name = getErrorName(error) || "Error";
|
|
2809
|
+
if (typeof error.stack === "string") {
|
|
2810
|
+
sanitizedError.stack = sanitizeStack(error.stack);
|
|
2811
|
+
}
|
|
2812
|
+
return sanitizedError;
|
|
2813
|
+
}
|
|
2814
|
+
if (typeof error === "string") {
|
|
2815
|
+
return sanitizeString(error) || "unknown";
|
|
2816
|
+
}
|
|
2817
|
+
return sanitizeValue(error);
|
|
2818
|
+
};
|
|
2631
2819
|
function isIgnorableFrontendError(error) {
|
|
2632
2820
|
const name = error && typeof error === "object" ? error.name || "" : "";
|
|
2633
2821
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
2634
2822
|
const lowerMessage = message.toLowerCase();
|
|
2635
|
-
return name === "AbortError" || lowerMessage.includes("the operation was aborted") || lowerMessage.includes("signal is aborted") || lowerMessage.includes("resizeobserver loop");
|
|
2823
|
+
return name === "AbortError" || name === "NotAllowedError" || lowerMessage.includes("the operation was aborted") || lowerMessage.includes("signal is aborted") || lowerMessage.includes("resizeobserver loop") || lowerMessage.includes("play() failed") || lowerMessage.includes("play request was interrupted") || lowerMessage.includes("autoplay") || lowerMessage.includes("not allowed by the user agent") || lowerMessage.includes("recoverable hls") || lowerMessage.includes("non-fatal hls") || lowerMessage.includes("fragloaderror") || lowerMessage.includes("frag load error") || lowerMessage.includes("fragloadtimeout") || lowerMessage.includes("bufferstallederror") || lowerMessage.includes("buffer stalled") || lowerMessage.includes("media error recovered") || lowerMessage.includes("recovermediaerror");
|
|
2636
2824
|
}
|
|
2637
2825
|
function setSentryUserContext(user) {
|
|
2638
2826
|
const sentry = getSentry();
|
|
2639
2827
|
if (!sentry) return;
|
|
2640
2828
|
if (user) {
|
|
2641
2829
|
sentry.setUser({
|
|
2642
|
-
id: user.id
|
|
2643
|
-
email: user.email
|
|
2830
|
+
id: user.id
|
|
2644
2831
|
});
|
|
2645
2832
|
sentry.setTags({
|
|
2646
2833
|
company_id: user.company_id || "unknown",
|
|
@@ -2691,28 +2878,60 @@ function applyScopeExtras(scope, extras) {
|
|
|
2691
2878
|
function captureSentryMessage(message, level = "warning", extras) {
|
|
2692
2879
|
const sentry = getSentry();
|
|
2693
2880
|
if (!sentry || !sentry.captureMessage) return;
|
|
2881
|
+
const options = normalizeCaptureOptions({ ...normalizeCaptureOptions(extras), severity: level });
|
|
2882
|
+
const fingerprint = buildSentryFingerprint(new Error(message), options);
|
|
2883
|
+
if (!shouldSendHandledSentryEvent(fingerprint)) return;
|
|
2884
|
+
const sanitizedExtras = sanitizeExtras({
|
|
2885
|
+
...options.extras,
|
|
2886
|
+
sentry_quota_key: fingerprint,
|
|
2887
|
+
sentry_capture_policy: "quota_controlled"
|
|
2888
|
+
});
|
|
2889
|
+
const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);
|
|
2890
|
+
const sanitizedMessage = sanitizeString(message) || "Sentry message";
|
|
2694
2891
|
if (sentry.withScope) {
|
|
2695
2892
|
sentry.withScope((scope) => {
|
|
2696
2893
|
scope.setLevel?.(level);
|
|
2697
|
-
|
|
2698
|
-
|
|
2894
|
+
scope.setFingerprint?.([fingerprint]);
|
|
2895
|
+
if (options.surface) scope.setTag?.("surface", sanitizeString(options.surface));
|
|
2896
|
+
if (route) scope.setTag?.("route", route);
|
|
2897
|
+
if (options.status !== void 0 && options.status !== null) scope.setTag?.("status", options.status);
|
|
2898
|
+
applyScopeExtras(scope, sanitizedExtras);
|
|
2899
|
+
sentry.captureMessage?.(sanitizedMessage);
|
|
2699
2900
|
});
|
|
2700
2901
|
return;
|
|
2701
2902
|
}
|
|
2702
|
-
sentry.captureMessage(
|
|
2903
|
+
sentry.captureMessage(sanitizedMessage, level);
|
|
2703
2904
|
}
|
|
2704
2905
|
function captureSentryException(error, extras) {
|
|
2906
|
+
if (isIgnorableFrontendError(error)) {
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2705
2909
|
const sentry = getSentry();
|
|
2706
2910
|
if (!sentry || !sentry.captureException) return;
|
|
2911
|
+
const options = normalizeCaptureOptions(extras);
|
|
2912
|
+
const fingerprint = buildSentryFingerprint(error, options);
|
|
2913
|
+
if (!shouldSendHandledSentryEvent(fingerprint)) return;
|
|
2914
|
+
const sanitizedExtras = sanitizeExtras({
|
|
2915
|
+
...options.extras,
|
|
2916
|
+
sentry_quota_key: fingerprint,
|
|
2917
|
+
sentry_capture_policy: "quota_controlled"
|
|
2918
|
+
});
|
|
2919
|
+
const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);
|
|
2920
|
+
const status = options.status ?? getStatusFromError(error);
|
|
2921
|
+
const sanitizedError = createSanitizedSentryException(error);
|
|
2707
2922
|
if (sentry.withScope) {
|
|
2708
2923
|
sentry.withScope((scope) => {
|
|
2709
|
-
scope.setLevel?.("error");
|
|
2710
|
-
|
|
2711
|
-
|
|
2924
|
+
scope.setLevel?.(options.severity || "error");
|
|
2925
|
+
scope.setFingerprint?.([fingerprint]);
|
|
2926
|
+
if (options.surface) scope.setTag?.("surface", sanitizeString(options.surface));
|
|
2927
|
+
if (route) scope.setTag?.("route", route);
|
|
2928
|
+
if (status !== null && status !== void 0) scope.setTag?.("status", status);
|
|
2929
|
+
applyScopeExtras(scope, sanitizedExtras);
|
|
2930
|
+
sentry.captureException?.(sanitizedError);
|
|
2712
2931
|
});
|
|
2713
2932
|
return;
|
|
2714
2933
|
}
|
|
2715
|
-
sentry.captureException(
|
|
2934
|
+
sentry.captureException(sanitizedError);
|
|
2716
2935
|
}
|
|
2717
2936
|
function captureHandledFrontendException(error, extras) {
|
|
2718
2937
|
if (isIgnorableFrontendError(error)) {
|
|
@@ -2720,6 +2939,23 @@ function captureHandledFrontendException(error, extras) {
|
|
|
2720
2939
|
}
|
|
2721
2940
|
captureSentryException(error, extras);
|
|
2722
2941
|
}
|
|
2942
|
+
function addSentryBreadcrumb(message, options = {}) {
|
|
2943
|
+
const sentry = getSentry();
|
|
2944
|
+
if (!sentry?.addBreadcrumb) return;
|
|
2945
|
+
const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);
|
|
2946
|
+
const data = sanitizeExtras({
|
|
2947
|
+
surface: options.surface,
|
|
2948
|
+
route,
|
|
2949
|
+
status: options.status,
|
|
2950
|
+
...options.extras
|
|
2951
|
+
});
|
|
2952
|
+
sentry.addBreadcrumb({
|
|
2953
|
+
category: options.category || options.surface || "frontend",
|
|
2954
|
+
message: sanitizeString(message),
|
|
2955
|
+
level: options.severity || "info",
|
|
2956
|
+
data
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2723
2959
|
|
|
2724
2960
|
// src/lib/services/backendClient.ts
|
|
2725
2961
|
var ACCESS_TOKEN_REFRESH_BUFFER_MS = 6e4;
|
|
@@ -2787,7 +3023,7 @@ var createAbortError = () => {
|
|
|
2787
3023
|
return error;
|
|
2788
3024
|
}
|
|
2789
3025
|
};
|
|
2790
|
-
var
|
|
3026
|
+
var getStatusFromError2 = (error) => {
|
|
2791
3027
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
2792
3028
|
const statusMatch = message.match(/\((\d{3})\)/) || message.match(/http\s+(\d{3})/i);
|
|
2793
3029
|
if (!statusMatch) {
|
|
@@ -2803,7 +3039,7 @@ var isRetryableError = (error) => {
|
|
|
2803
3039
|
if (isAbortError(error)) {
|
|
2804
3040
|
return false;
|
|
2805
3041
|
}
|
|
2806
|
-
const status =
|
|
3042
|
+
const status = getStatusFromError2(error);
|
|
2807
3043
|
if (status !== null) {
|
|
2808
3044
|
return status >= 500 || status === 408 || status === 425 || status === 429;
|
|
2809
3045
|
}
|
|
@@ -2827,6 +3063,7 @@ var fetchBackendJson = async (supabase, endpoint, options = {}) => {
|
|
|
2827
3063
|
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
2828
3064
|
retries = 0,
|
|
2829
3065
|
retryDelayMs = DEFAULT_RETRY_DELAY_MS,
|
|
3066
|
+
sentry,
|
|
2830
3067
|
signal: externalSignal,
|
|
2831
3068
|
...fetchOptions
|
|
2832
3069
|
} = options;
|
|
@@ -2864,14 +3101,22 @@ var fetchBackendJson = async (supabase, endpoint, options = {}) => {
|
|
|
2864
3101
|
} catch (error) {
|
|
2865
3102
|
const wrappedError = externalSignal?.aborted || controller.signal.aborted && !isAbortError(error) ? createAbortError() : error;
|
|
2866
3103
|
if (attempt >= retries || !isRetryableError(wrappedError)) {
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
3104
|
+
if (sentry && sentry.capture !== false) {
|
|
3105
|
+
captureHandledFrontendException(wrappedError, {
|
|
3106
|
+
surface: sentry.surface || "backend_client",
|
|
3107
|
+
route: sentry.route || endpoint,
|
|
3108
|
+
status: sentry.status ?? getStatusFromError2(wrappedError),
|
|
3109
|
+
severity: sentry.severity || "error",
|
|
3110
|
+
quotaKey: sentry.quotaKey,
|
|
3111
|
+
extras: {
|
|
3112
|
+
method,
|
|
3113
|
+
retry_attempt: attempt,
|
|
3114
|
+
retries,
|
|
3115
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
3116
|
+
...sentry.extras
|
|
3117
|
+
}
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
2875
3120
|
throw wrappedError;
|
|
2876
3121
|
}
|
|
2877
3122
|
await new Promise((resolve) => globalThis.setTimeout(resolve, retryDelayMs));
|
|
@@ -2957,6 +3202,7 @@ var ApiClient = class {
|
|
|
2957
3202
|
retryDelay = 1e3,
|
|
2958
3203
|
silentErrors = true,
|
|
2959
3204
|
fallbackData = null,
|
|
3205
|
+
sentry,
|
|
2960
3206
|
...fetchOptions
|
|
2961
3207
|
} = options;
|
|
2962
3208
|
let lastError = null;
|
|
@@ -2990,24 +3236,36 @@ var ApiClient = class {
|
|
|
2990
3236
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
2991
3237
|
continue;
|
|
2992
3238
|
}
|
|
2993
|
-
if (
|
|
3239
|
+
if (sentry && sentry.capture !== false) {
|
|
2994
3240
|
captureHandledFrontendException(lastError, {
|
|
3241
|
+
surface: sentry.surface || "api_client",
|
|
3242
|
+
route: sentry.route || url,
|
|
3243
|
+
status: sentry.status,
|
|
3244
|
+
severity: sentry.severity || "error",
|
|
3245
|
+
quotaKey: sentry.quotaKey,
|
|
3246
|
+
extras: {
|
|
3247
|
+
retries,
|
|
3248
|
+
silent_errors: silentErrors,
|
|
3249
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
3250
|
+
...sentry.extras
|
|
3251
|
+
}
|
|
3252
|
+
});
|
|
3253
|
+
} else {
|
|
3254
|
+
addSentryBreadcrumb("API client request failed", {
|
|
2995
3255
|
surface: "api_client",
|
|
2996
|
-
url,
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3256
|
+
route: url,
|
|
3257
|
+
severity: silentErrors ? "info" : "warning",
|
|
3258
|
+
extras: {
|
|
3259
|
+
retries,
|
|
3260
|
+
silent_errors: silentErrors,
|
|
3261
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
3262
|
+
}
|
|
3000
3263
|
});
|
|
3264
|
+
}
|
|
3265
|
+
if (silentErrors) {
|
|
3001
3266
|
console.warn("[ApiClient] All retries exhausted, returning fallback data");
|
|
3002
3267
|
return fallbackData;
|
|
3003
3268
|
} else {
|
|
3004
|
-
captureHandledFrontendException(lastError, {
|
|
3005
|
-
surface: "api_client",
|
|
3006
|
-
url,
|
|
3007
|
-
retries,
|
|
3008
|
-
silent_errors: false,
|
|
3009
|
-
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
3010
|
-
});
|
|
3011
3269
|
throw lastError;
|
|
3012
3270
|
}
|
|
3013
3271
|
}
|
|
@@ -3259,8 +3517,12 @@ var AuthService = class {
|
|
|
3259
3517
|
timeout: 1e4,
|
|
3260
3518
|
// 10 seconds
|
|
3261
3519
|
retries: 1,
|
|
3262
|
-
silentErrors: false
|
|
3520
|
+
silentErrors: false,
|
|
3263
3521
|
// We want to know about auth errors
|
|
3522
|
+
sentry: {
|
|
3523
|
+
surface: "auth_session_request",
|
|
3524
|
+
route: "/api/auth/session"
|
|
3525
|
+
}
|
|
3264
3526
|
}
|
|
3265
3527
|
);
|
|
3266
3528
|
console.log("[AuthService] Session loaded:", {
|
|
@@ -3330,7 +3592,11 @@ var AuthService = class {
|
|
|
3330
3592
|
timeout: 1e4,
|
|
3331
3593
|
// 10 seconds
|
|
3332
3594
|
retries: 1,
|
|
3333
|
-
silentErrors: false
|
|
3595
|
+
silentErrors: false,
|
|
3596
|
+
sentry: {
|
|
3597
|
+
surface: "auth_permissions_request",
|
|
3598
|
+
route: "/api/auth/permissions"
|
|
3599
|
+
}
|
|
3334
3600
|
}
|
|
3335
3601
|
);
|
|
3336
3602
|
console.log("[AuthService] Permissions loaded for role:", data.role);
|
|
@@ -5972,12 +6238,16 @@ var workspaceService = {
|
|
|
5972
6238
|
return displayNamesMap;
|
|
5973
6239
|
} catch (error) {
|
|
5974
6240
|
console.error("Error fetching workspace display names:", error);
|
|
5975
|
-
|
|
6241
|
+
addSentryBreadcrumb("Workspace display-name fallback failed", {
|
|
5976
6242
|
surface: "workspace_display_names",
|
|
5977
6243
|
route: "/api/workspaces/display-names",
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
6244
|
+
severity: "warning",
|
|
6245
|
+
extras: {
|
|
6246
|
+
company_id: companyId || null,
|
|
6247
|
+
line_id: lineId || null,
|
|
6248
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
6249
|
+
error_message: error instanceof Error ? error.message : String(error)
|
|
6250
|
+
}
|
|
5981
6251
|
});
|
|
5982
6252
|
throw error;
|
|
5983
6253
|
}
|
|
@@ -7581,11 +7851,28 @@ var shouldReportMixpanel = (key, isError) => {
|
|
|
7581
7851
|
};
|
|
7582
7852
|
var reportMixpanelWarning = (key, message, extras) => {
|
|
7583
7853
|
if (!shouldReportMixpanel(key, false)) return;
|
|
7584
|
-
|
|
7854
|
+
addSentryBreadcrumb(message, {
|
|
7855
|
+
surface: "mixpanel",
|
|
7856
|
+
severity: "warning",
|
|
7857
|
+
extras: {
|
|
7858
|
+
key,
|
|
7859
|
+
...baseMixpanelExtras(),
|
|
7860
|
+
...extras
|
|
7861
|
+
}
|
|
7862
|
+
});
|
|
7585
7863
|
};
|
|
7586
7864
|
var reportMixpanelError = (key, error, extras) => {
|
|
7587
7865
|
if (!shouldReportMixpanel(key, true)) return;
|
|
7588
|
-
|
|
7866
|
+
addSentryBreadcrumb("Mixpanel operation failed", {
|
|
7867
|
+
surface: "mixpanel",
|
|
7868
|
+
severity: "warning",
|
|
7869
|
+
extras: {
|
|
7870
|
+
key,
|
|
7871
|
+
error_message: error instanceof Error ? error.message : String(error ?? "unknown"),
|
|
7872
|
+
...baseMixpanelExtras(),
|
|
7873
|
+
...extras
|
|
7874
|
+
}
|
|
7875
|
+
});
|
|
7589
7876
|
};
|
|
7590
7877
|
var normalizeCoreEventName = (eventName) => {
|
|
7591
7878
|
const canonicalName = MIXPANEL_EVENT_NAME_ALIASES[eventName] || eventName;
|
|
@@ -9602,11 +9889,15 @@ var LinesService = class {
|
|
|
9602
9889
|
}));
|
|
9603
9890
|
} catch (error) {
|
|
9604
9891
|
console.error("Error fetching lines:", error);
|
|
9605
|
-
|
|
9892
|
+
addSentryBreadcrumb("Lines service request failed", {
|
|
9606
9893
|
surface: "lines_service",
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9894
|
+
route: "/api/lines",
|
|
9895
|
+
severity: "warning",
|
|
9896
|
+
extras: {
|
|
9897
|
+
operation: "getLinesByCompanyId",
|
|
9898
|
+
company_id: companyId,
|
|
9899
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
9900
|
+
}
|
|
9610
9901
|
});
|
|
9611
9902
|
throw new Error(`Failed to fetch lines: ${error.message}`);
|
|
9612
9903
|
}
|
|
@@ -9657,10 +9948,14 @@ var LinesService = class {
|
|
|
9657
9948
|
}));
|
|
9658
9949
|
} catch (error) {
|
|
9659
9950
|
console.error("Error fetching all lines:", error);
|
|
9660
|
-
|
|
9951
|
+
addSentryBreadcrumb("Lines service request failed", {
|
|
9661
9952
|
surface: "lines_service",
|
|
9662
|
-
|
|
9663
|
-
|
|
9953
|
+
route: "/api/lines",
|
|
9954
|
+
severity: "warning",
|
|
9955
|
+
extras: {
|
|
9956
|
+
operation: "getAllLines",
|
|
9957
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
9958
|
+
}
|
|
9664
9959
|
});
|
|
9665
9960
|
throw new Error(`Failed to fetch lines: ${error.message}`);
|
|
9666
9961
|
}
|
|
@@ -9720,11 +10015,15 @@ var LinesService = class {
|
|
|
9720
10015
|
};
|
|
9721
10016
|
} catch (error) {
|
|
9722
10017
|
console.error("Error fetching line:", error);
|
|
9723
|
-
|
|
10018
|
+
addSentryBreadcrumb("Lines service request failed", {
|
|
9724
10019
|
surface: "lines_service",
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
10020
|
+
route: "/api/lines/:id",
|
|
10021
|
+
severity: "warning",
|
|
10022
|
+
extras: {
|
|
10023
|
+
operation: "getLineById",
|
|
10024
|
+
line_id: lineId,
|
|
10025
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
10026
|
+
}
|
|
9728
10027
|
});
|
|
9729
10028
|
throw new Error(`Failed to fetch line: ${error.message}`);
|
|
9730
10029
|
}
|
|
@@ -13199,6 +13498,17 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId, options) => {
|
|
|
13199
13498
|
workspaceMetricsStore.setDetailed(transformedData);
|
|
13200
13499
|
} catch (err) {
|
|
13201
13500
|
console.error("Error fetching workspace metrics:", err);
|
|
13501
|
+
captureHandledFrontendException(err, {
|
|
13502
|
+
surface: "workspace_detail_metrics",
|
|
13503
|
+
route: "/api/dashboard/workspace/:workspaceId/metrics",
|
|
13504
|
+
extras: {
|
|
13505
|
+
workspace_id: workspaceId,
|
|
13506
|
+
company_id: companyId,
|
|
13507
|
+
date,
|
|
13508
|
+
shift_id: shiftId,
|
|
13509
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
13510
|
+
}
|
|
13511
|
+
});
|
|
13202
13512
|
setError({ message: err.message, code: err.code });
|
|
13203
13513
|
} finally {
|
|
13204
13514
|
isFetchingRef.current = false;
|
|
@@ -14339,6 +14649,7 @@ var transformMonitorWorkspaceMetrics = ({
|
|
|
14339
14649
|
recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
|
|
14340
14650
|
recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
|
|
14341
14651
|
recent_flow_computed_at: item.recent_flow_computed_at ?? null,
|
|
14652
|
+
recent_flow_forced_zero_after_shift: item.recent_flow_forced_zero_after_shift ?? null,
|
|
14342
14653
|
scheduled_break_active: item.scheduled_break_active ?? false,
|
|
14343
14654
|
incoming_wip_current: item.incoming_wip_current ?? null,
|
|
14344
14655
|
incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
|
|
@@ -21465,20 +21776,26 @@ var apiUtils = {
|
|
|
21465
21776
|
const token = sessionResponse.data.session?.access_token;
|
|
21466
21777
|
if (!token) {
|
|
21467
21778
|
console.error("API Util: No authentication token available.");
|
|
21468
|
-
|
|
21779
|
+
addSentryBreadcrumb("API utility request skipped without auth token", {
|
|
21469
21780
|
surface: "api_utils",
|
|
21470
|
-
|
|
21471
|
-
|
|
21781
|
+
route: relativeEndpoint,
|
|
21782
|
+
severity: "warning",
|
|
21783
|
+
extras: {
|
|
21784
|
+
reason: "missing_auth_token"
|
|
21785
|
+
}
|
|
21472
21786
|
});
|
|
21473
21787
|
throw new Error("Authentication required.");
|
|
21474
21788
|
}
|
|
21475
21789
|
const baseUrl = config.apiBaseUrl;
|
|
21476
21790
|
if (!baseUrl) {
|
|
21477
21791
|
console.error("API Util: apiBaseUrl is not configured.");
|
|
21478
|
-
|
|
21792
|
+
addSentryBreadcrumb("API utility request skipped without base URL", {
|
|
21479
21793
|
surface: "api_utils",
|
|
21480
|
-
|
|
21481
|
-
|
|
21794
|
+
route: relativeEndpoint,
|
|
21795
|
+
severity: "warning",
|
|
21796
|
+
extras: {
|
|
21797
|
+
reason: "missing_api_base_url"
|
|
21798
|
+
}
|
|
21482
21799
|
});
|
|
21483
21800
|
throw new Error("API base URL is not configured.");
|
|
21484
21801
|
}
|
|
@@ -21508,11 +21825,14 @@ var apiUtils = {
|
|
|
21508
21825
|
return await response.json();
|
|
21509
21826
|
} catch (error) {
|
|
21510
21827
|
console.error(`Network or fetch error calling ${endpoint}:`, error);
|
|
21511
|
-
|
|
21828
|
+
addSentryBreadcrumb("API utility request failed", {
|
|
21512
21829
|
surface: "api_utils",
|
|
21513
|
-
endpoint,
|
|
21514
|
-
|
|
21515
|
-
|
|
21830
|
+
route: endpoint,
|
|
21831
|
+
severity: "warning",
|
|
21832
|
+
extras: {
|
|
21833
|
+
method: options.method || "GET",
|
|
21834
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
21835
|
+
}
|
|
21516
21836
|
});
|
|
21517
21837
|
if (error instanceof Error) {
|
|
21518
21838
|
throw error;
|
|
@@ -36996,6 +37316,9 @@ var isLowWipGreenOverride = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
|
|
|
36996
37316
|
if (workspace.scheduled_break_active === true) {
|
|
36997
37317
|
return false;
|
|
36998
37318
|
}
|
|
37319
|
+
if (workspace.recent_flow_forced_zero_after_shift === true) {
|
|
37320
|
+
return false;
|
|
37321
|
+
}
|
|
36999
37322
|
if (!hasVideoGridRecentFlow(workspace) || !isVideoGridWipGated(workspace)) {
|
|
37000
37323
|
return false;
|
|
37001
37324
|
}
|
|
@@ -37241,7 +37564,7 @@ var VideoCard = React144__namespace.default.memo(({
|
|
|
37241
37564
|
}
|
|
37242
37565
|
);
|
|
37243
37566
|
}, (prevProps, nextProps) => {
|
|
37244
|
-
if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
|
|
37567
|
+
if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.recent_flow_forced_zero_after_shift !== nextProps.workspace.recent_flow_forced_zero_after_shift || prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
|
|
37245
37568
|
return false;
|
|
37246
37569
|
}
|
|
37247
37570
|
if (prevProps.workspace.workspace_uuid !== nextProps.workspace.workspace_uuid || prevProps.workspace.workspace_name !== nextProps.workspace.workspace_name || prevProps.workspace.line_id !== nextProps.workspace.line_id) {
|
|
@@ -37560,6 +37883,15 @@ var VideoGridView = React144__namespace.default.memo(({
|
|
|
37560
37883
|
}
|
|
37561
37884
|
console.error(`[VideoGridView] Stream failed for workspace: ${workspaceId}`);
|
|
37562
37885
|
setFailedStreams((prev) => new Set(prev).add(workspaceId));
|
|
37886
|
+
captureHandledFrontendException(new Error("Video stream failed after recovery attempts"), {
|
|
37887
|
+
surface: "video_grid_stream",
|
|
37888
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
37889
|
+
extras: {
|
|
37890
|
+
workspace_id: workspaceId,
|
|
37891
|
+
stream_source: isR2Stream ? "r2" : "media_config",
|
|
37892
|
+
has_fallback: hasFallback
|
|
37893
|
+
}
|
|
37894
|
+
});
|
|
37563
37895
|
trackCoreEvent("Video Stream Error", {
|
|
37564
37896
|
workspace_id: workspaceId,
|
|
37565
37897
|
view_type: "video_grid",
|
|
@@ -42227,11 +42559,14 @@ var SilentErrorBoundary = class extends React144__namespace.default.Component {
|
|
|
42227
42559
|
componentStack: errorInfo.componentStack,
|
|
42228
42560
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
42229
42561
|
});
|
|
42230
|
-
|
|
42562
|
+
captureHandledFrontendException(error, {
|
|
42231
42563
|
surface: "react_error_boundary",
|
|
42232
|
-
|
|
42233
|
-
|
|
42234
|
-
|
|
42564
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
42565
|
+
severity: "error",
|
|
42566
|
+
extras: {
|
|
42567
|
+
component_stack: errorInfo.componentStack,
|
|
42568
|
+
error_count: this.state.errorCount + 1
|
|
42569
|
+
}
|
|
42235
42570
|
});
|
|
42236
42571
|
this.setState((prev) => ({
|
|
42237
42572
|
errorCount: prev.errorCount + 1,
|
|
@@ -44768,6 +45103,11 @@ var OVERLAY_ICON_COLOR_BY_PALETTE = {
|
|
|
44768
45103
|
cyan: "text-cyan-300",
|
|
44769
45104
|
slate: "text-slate-200"
|
|
44770
45105
|
};
|
|
45106
|
+
var buildPlaybackCaptureError = (recoverable) => {
|
|
45107
|
+
return new Error(
|
|
45108
|
+
recoverable ? "Clip playback failed after retry exhaustion" : "Clip playback failed with non-recoverable media error"
|
|
45109
|
+
);
|
|
45110
|
+
};
|
|
44771
45111
|
var BottlenecksContent = ({
|
|
44772
45112
|
workspaceId,
|
|
44773
45113
|
workspaceName,
|
|
@@ -45222,6 +45562,16 @@ var BottlenecksContent = ({
|
|
|
45222
45562
|
}
|
|
45223
45563
|
} catch (err) {
|
|
45224
45564
|
console.error("Error loading first video for category:", err);
|
|
45565
|
+
captureHandledFrontendException(err, {
|
|
45566
|
+
surface: "clips_initial_load",
|
|
45567
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
45568
|
+
extras: {
|
|
45569
|
+
workspace_id: workspaceId,
|
|
45570
|
+
category: targetCategory,
|
|
45571
|
+
date: effectiveDateString,
|
|
45572
|
+
shift_id: effectiveShiftId
|
|
45573
|
+
}
|
|
45574
|
+
});
|
|
45225
45575
|
if (isMountedRef.current) {
|
|
45226
45576
|
setError({
|
|
45227
45577
|
type: "fatal",
|
|
@@ -45759,6 +46109,15 @@ var BottlenecksContent = ({
|
|
|
45759
46109
|
console.log(`[BottlenecksContent] Loaded clip ${clipId} (${clickedClipIndex + 1}/${metadataArray.length})`);
|
|
45760
46110
|
} catch (error2) {
|
|
45761
46111
|
console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
|
|
46112
|
+
captureHandledFrontendException(error2, {
|
|
46113
|
+
surface: "clips_selected_load",
|
|
46114
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
46115
|
+
extras: {
|
|
46116
|
+
workspace_id: workspaceId,
|
|
46117
|
+
clip_id: clipId,
|
|
46118
|
+
category: categoryId
|
|
46119
|
+
}
|
|
46120
|
+
});
|
|
45762
46121
|
if (isMountedRef.current) {
|
|
45763
46122
|
setError({
|
|
45764
46123
|
type: "fatal",
|
|
@@ -46358,6 +46717,18 @@ var BottlenecksContent = ({
|
|
|
46358
46717
|
errorCode,
|
|
46359
46718
|
errorMessage
|
|
46360
46719
|
});
|
|
46720
|
+
captureHandledFrontendException(buildPlaybackCaptureError(false), {
|
|
46721
|
+
surface: "clips_video_playback",
|
|
46722
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
46723
|
+
extras: {
|
|
46724
|
+
workspace_id: workspaceId,
|
|
46725
|
+
category: activeFilterRef.current,
|
|
46726
|
+
video_id: currentVideo?.id,
|
|
46727
|
+
error_code: errorCode,
|
|
46728
|
+
player_error_message: errorMessage,
|
|
46729
|
+
recoverable: false
|
|
46730
|
+
}
|
|
46731
|
+
});
|
|
46361
46732
|
return;
|
|
46362
46733
|
}
|
|
46363
46734
|
if (videoRetryCountRef.current < 3 && currentVideo) {
|
|
@@ -46401,6 +46772,19 @@ var BottlenecksContent = ({
|
|
|
46401
46772
|
errorMessage,
|
|
46402
46773
|
attempts: 3
|
|
46403
46774
|
});
|
|
46775
|
+
captureHandledFrontendException(buildPlaybackCaptureError(true), {
|
|
46776
|
+
surface: "clips_video_playback",
|
|
46777
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
46778
|
+
extras: {
|
|
46779
|
+
workspace_id: workspaceId,
|
|
46780
|
+
category: activeFilterRef.current,
|
|
46781
|
+
video_id: currentVideo?.id,
|
|
46782
|
+
error_code: errorCode,
|
|
46783
|
+
player_error_message: errorMessage,
|
|
46784
|
+
recoverable: true,
|
|
46785
|
+
attempts: 3
|
|
46786
|
+
}
|
|
46787
|
+
});
|
|
46404
46788
|
}
|
|
46405
46789
|
}, [currentVideo, workspaceId, clearLoadingState, clearRetryTimeout, restartCurrentClipPlayback]);
|
|
46406
46790
|
React144.useEffect(() => {
|
|
@@ -48732,6 +49116,14 @@ var LinePdfExportButton = ({
|
|
|
48732
49116
|
pdf.save(`${fileName}.pdf`);
|
|
48733
49117
|
} catch (error) {
|
|
48734
49118
|
console.error("PDF Export Error:", error);
|
|
49119
|
+
captureHandledFrontendException(error, {
|
|
49120
|
+
surface: "line_pdf_export",
|
|
49121
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
49122
|
+
extras: {
|
|
49123
|
+
file_name: fileName,
|
|
49124
|
+
target_type: typeof targetElement === "string" ? "selector" : "element"
|
|
49125
|
+
}
|
|
49126
|
+
});
|
|
48735
49127
|
alert("An error occurred while exporting to PDF. Please try again.");
|
|
48736
49128
|
} finally {
|
|
48737
49129
|
setIsExporting(false);
|
|
@@ -50554,6 +50946,16 @@ var LineMonthlyPdfGenerator = ({
|
|
|
50554
50946
|
doc.save(fileName);
|
|
50555
50947
|
} catch (error) {
|
|
50556
50948
|
console.error("Line Monthly PDF generation failed:", error);
|
|
50949
|
+
captureHandledFrontendException(error, {
|
|
50950
|
+
surface: "line_monthly_pdf_generation",
|
|
50951
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
50952
|
+
extras: {
|
|
50953
|
+
line_name: lineName || null,
|
|
50954
|
+
selected_month: selectedMonth,
|
|
50955
|
+
selected_year: selectedYear,
|
|
50956
|
+
selected_shift_id: selectedShiftId
|
|
50957
|
+
}
|
|
50958
|
+
});
|
|
50557
50959
|
} finally {
|
|
50558
50960
|
setIsGenerating(false);
|
|
50559
50961
|
}
|
|
@@ -51336,6 +51738,15 @@ var LinePdfGenerator = ({
|
|
|
51336
51738
|
doc.save(fileName);
|
|
51337
51739
|
} catch (error) {
|
|
51338
51740
|
console.error("PDF generation failed:", error);
|
|
51741
|
+
captureHandledFrontendException(error, {
|
|
51742
|
+
surface: "line_pdf_generation",
|
|
51743
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
51744
|
+
extras: {
|
|
51745
|
+
line_id: lineInfo.line_id,
|
|
51746
|
+
date: lineInfo.date,
|
|
51747
|
+
shift_id: lineInfo.shift_id
|
|
51748
|
+
}
|
|
51749
|
+
});
|
|
51339
51750
|
} finally {
|
|
51340
51751
|
setIsGenerating(false);
|
|
51341
51752
|
}
|
|
@@ -51408,6 +51819,14 @@ var WorkspacePdfExportButton = ({
|
|
|
51408
51819
|
pdf.save(`${fileName}.pdf`);
|
|
51409
51820
|
} catch (error) {
|
|
51410
51821
|
console.error("PDF Export Error:", error);
|
|
51822
|
+
captureHandledFrontendException(error, {
|
|
51823
|
+
surface: "workspace_pdf_export",
|
|
51824
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
51825
|
+
extras: {
|
|
51826
|
+
file_name: fileName,
|
|
51827
|
+
target_type: typeof targetElement === "string" ? "selector" : "element"
|
|
51828
|
+
}
|
|
51829
|
+
});
|
|
51411
51830
|
alert("An error occurred while exporting to PDF. Please try again.");
|
|
51412
51831
|
} finally {
|
|
51413
51832
|
setIsExporting(false);
|
|
@@ -53257,6 +53676,16 @@ var WorkspacePdfGenerator = ({
|
|
|
53257
53676
|
doc.save(fileName);
|
|
53258
53677
|
} catch (error) {
|
|
53259
53678
|
console.error("PDF generation failed:", error);
|
|
53679
|
+
captureHandledFrontendException(error, {
|
|
53680
|
+
surface: "workspace_pdf_generation",
|
|
53681
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
53682
|
+
extras: {
|
|
53683
|
+
workspace_id: workspace.workspace_id,
|
|
53684
|
+
line_id: workspace.line_id,
|
|
53685
|
+
date: workspace.date,
|
|
53686
|
+
shift_id: workspace.shift_id
|
|
53687
|
+
}
|
|
53688
|
+
});
|
|
53260
53689
|
} finally {
|
|
53261
53690
|
setIsGenerating(false);
|
|
53262
53691
|
}
|
|
@@ -53664,6 +54093,18 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
53664
54093
|
doc.save(fileName);
|
|
53665
54094
|
} catch (error) {
|
|
53666
54095
|
console.error("Monthly PDF generation failed:", error);
|
|
54096
|
+
captureHandledFrontendException(error, {
|
|
54097
|
+
surface: "workspace_monthly_pdf_generation",
|
|
54098
|
+
route: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
54099
|
+
extras: {
|
|
54100
|
+
workspace_id: workspaceId,
|
|
54101
|
+
workspace_name: workspaceName,
|
|
54102
|
+
line_name: lineName || null,
|
|
54103
|
+
selected_month: selectedMonth,
|
|
54104
|
+
selected_year: selectedYear,
|
|
54105
|
+
selected_shift_id: selectedShiftId
|
|
54106
|
+
}
|
|
54107
|
+
});
|
|
53667
54108
|
} finally {
|
|
53668
54109
|
setIsGenerating(false);
|
|
53669
54110
|
}
|
|
@@ -62936,6 +63377,16 @@ var useLiveMonitorBootstrap = ({
|
|
|
62936
63377
|
if (requestId !== activeRequestIdRef.current) {
|
|
62937
63378
|
return;
|
|
62938
63379
|
}
|
|
63380
|
+
captureHandledFrontendException(fetchError, {
|
|
63381
|
+
surface: "live_monitor_bootstrap",
|
|
63382
|
+
route: "/api/dashboard/monitor-bootstrap",
|
|
63383
|
+
extras: {
|
|
63384
|
+
company_id: resolvedCompanyId,
|
|
63385
|
+
line_ids: normalizedLineIds,
|
|
63386
|
+
force_refresh: force,
|
|
63387
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
63388
|
+
}
|
|
63389
|
+
});
|
|
62939
63390
|
setError(fetchError instanceof Error ? fetchError : new Error("Failed to load live monitor bootstrap"));
|
|
62940
63391
|
} finally {
|
|
62941
63392
|
if (requestId === activeRequestIdRef.current) {
|
|
@@ -63248,11 +63699,14 @@ var NotificationService = class {
|
|
|
63248
63699
|
}
|
|
63249
63700
|
return response;
|
|
63250
63701
|
} catch (error) {
|
|
63251
|
-
|
|
63702
|
+
addSentryBreadcrumb("Notification service request failed", {
|
|
63252
63703
|
surface: "notification_service",
|
|
63253
|
-
url,
|
|
63254
|
-
|
|
63255
|
-
|
|
63704
|
+
route: url,
|
|
63705
|
+
severity: "warning",
|
|
63706
|
+
extras: {
|
|
63707
|
+
method: options.method || "GET",
|
|
63708
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
63709
|
+
}
|
|
63256
63710
|
});
|
|
63257
63711
|
throw error;
|
|
63258
63712
|
}
|
|
@@ -77407,11 +77861,14 @@ var TicketService = class {
|
|
|
77407
77861
|
}
|
|
77408
77862
|
return response;
|
|
77409
77863
|
} catch (error) {
|
|
77410
|
-
|
|
77864
|
+
addSentryBreadcrumb("Ticket service request failed", {
|
|
77411
77865
|
surface: "ticket_service",
|
|
77412
|
-
url,
|
|
77413
|
-
|
|
77414
|
-
|
|
77866
|
+
route: url,
|
|
77867
|
+
severity: "warning",
|
|
77868
|
+
extras: {
|
|
77869
|
+
method: options.method || "GET",
|
|
77870
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
77871
|
+
}
|
|
77415
77872
|
});
|
|
77416
77873
|
throw error;
|
|
77417
77874
|
}
|
|
@@ -83191,10 +83648,33 @@ var useOperationsOverviewRefresh = ({
|
|
|
83191
83648
|
if (controller.signal.aborted || requestIdsRef.current[section] !== requestId || isAbortError2(error)) {
|
|
83192
83649
|
return;
|
|
83193
83650
|
}
|
|
83651
|
+
const sentryContext = {
|
|
83652
|
+
surface: "operations_overview_refresh",
|
|
83653
|
+
route: `/api/dashboard/operations-overview/${section}`,
|
|
83654
|
+
extras: {
|
|
83655
|
+
section,
|
|
83656
|
+
reason,
|
|
83657
|
+
company_id: companyId,
|
|
83658
|
+
line_ids: lineIds,
|
|
83659
|
+
start_date: startKey,
|
|
83660
|
+
end_date: endKey,
|
|
83661
|
+
trend_mode: trendMode,
|
|
83662
|
+
comparison_strategy: comparisonStrategy || null
|
|
83663
|
+
}
|
|
83664
|
+
};
|
|
83665
|
+
if (section === "improvements") {
|
|
83666
|
+
addSentryBreadcrumb("Operations overview optional improvements refresh failed", {
|
|
83667
|
+
...sentryContext,
|
|
83668
|
+
severity: "warning",
|
|
83669
|
+
category: "operations_overview"
|
|
83670
|
+
});
|
|
83671
|
+
} else {
|
|
83672
|
+
captureHandledFrontendException(error, sentryContext);
|
|
83673
|
+
}
|
|
83194
83674
|
onError(error instanceof Error ? error.message : `Failed to refresh ${section}`);
|
|
83195
83675
|
}
|
|
83196
83676
|
},
|
|
83197
|
-
[companyId, enabled, lineIds
|
|
83677
|
+
[companyId, comparisonStrategy, enabled, endKey, lineIds, startKey, supabase, trendMode]
|
|
83198
83678
|
);
|
|
83199
83679
|
const refreshSnapshot = React144__namespace.default.useCallback(
|
|
83200
83680
|
async (reason) => {
|
|
@@ -84796,6 +85276,9 @@ exports.RegistryProvider = RegistryProvider;
|
|
|
84796
85276
|
exports.RoleBadge = RoleBadge;
|
|
84797
85277
|
exports.S3ClipsService = S3ClipsSupabaseService;
|
|
84798
85278
|
exports.S3Service = S3Service;
|
|
85279
|
+
exports.SENTRY_HANDLED_EVENT_SESSION_LIMIT = SENTRY_HANDLED_EVENT_SESSION_LIMIT;
|
|
85280
|
+
exports.SENTRY_HANDLED_EVENT_WINDOW_MS = SENTRY_HANDLED_EVENT_WINDOW_MS;
|
|
85281
|
+
exports.SENTRY_QUOTA_STORAGE_KEY = SENTRY_QUOTA_STORAGE_KEY;
|
|
84799
85282
|
exports.SKUManagementView = SKUManagementView;
|
|
84800
85283
|
exports.SOPComplianceChart = SOPComplianceChart;
|
|
84801
85284
|
exports.SSEChatClient = SSEChatClient;
|
|
@@ -84872,6 +85355,7 @@ exports.WorkspacePdfExportButton = WorkspacePdfExportButton;
|
|
|
84872
85355
|
exports.WorkspacePdfGenerator = WorkspacePdfGenerator;
|
|
84873
85356
|
exports.WorkspaceWhatsAppShareButton = WorkspaceWhatsAppShareButton;
|
|
84874
85357
|
exports.actionService = actionService;
|
|
85358
|
+
exports.addSentryBreadcrumb = addSentryBreadcrumb;
|
|
84875
85359
|
exports.aggregateKPIsFromLineMetricsRows = aggregateKPIsFromLineMetricsRows;
|
|
84876
85360
|
exports.alertsService = alertsService;
|
|
84877
85361
|
exports.apiUtils = apiUtils;
|
|
@@ -85057,6 +85541,7 @@ exports.realtimeService = realtimeService;
|
|
|
85057
85541
|
exports.refreshWorkspaceDisplayNames = refreshWorkspaceDisplayNames;
|
|
85058
85542
|
exports.resetCoreMixpanel = resetCoreMixpanel;
|
|
85059
85543
|
exports.resetFailedUrl = resetFailedUrl;
|
|
85544
|
+
exports.resetSentryQuotaForTests = resetSentryQuotaForTests;
|
|
85060
85545
|
exports.resetSubscriptionManager = resetSubscriptionManager;
|
|
85061
85546
|
exports.resolveDefaultSkuId = resolveDefaultSkuId;
|
|
85062
85547
|
exports.resolveLiveSkuId = resolveLiveSkuId;
|