@pallasoft/ps-core-components 2.32.0 → 2.34.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/dist/analytics/index.d.ts +12 -0
- package/dist/analytics/index.js +28 -0
- package/dist/analytics/report-client-error.d.ts +1 -0
- package/dist/analytics/report-client-error.js +6 -0
- package/dist/components/ps-analytics-provider.d.ts +14 -0
- package/dist/components/ps-analytics-provider.js +72 -0
- package/dist/components/ps-client-error-reporter.d.ts +5 -0
- package/dist/components/ps-client-error-reporter.js +22 -0
- package/dist/components/ps-consent-banner.d.ts +11 -0
- package/dist/components/ps-consent-banner.js +21 -0
- package/dist/constants/ps-analytics-config.d.ts +19 -0
- package/dist/constants/ps-analytics-config.js +22 -0
- package/dist/constants/ps-client-error-config.d.ts +23 -0
- package/dist/constants/ps-client-error-config.js +17 -0
- package/dist/esm/analytics/index.js +12 -0
- package/dist/esm/analytics/report-client-error.js +2 -0
- package/dist/esm/components/ps-analytics-provider.js +68 -0
- package/dist/esm/components/ps-client-error-reporter.js +18 -0
- package/dist/esm/components/ps-consent-banner.js +17 -0
- package/dist/esm/constants/ps-analytics-config.js +19 -0
- package/dist/esm/constants/ps-client-error-config.js +14 -0
- package/dist/esm/hooks/use-consent.js +17 -0
- package/dist/esm/hooks/use-ps-debounced-revalidate.js +23 -0
- package/dist/esm/hooks/use-ps-live.js +34 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/interfaces/ps-live-handle.js +1 -0
- package/dist/esm/interfaces/ps-live-input.js +1 -0
- package/dist/esm/interfaces/ps-live-open-options.js +1 -0
- package/dist/esm/models/analytics-csp-input.js +1 -0
- package/dist/esm/models/analytics-csp-origins.js +1 -0
- package/dist/esm/models/ps-client-error-payload.js +1 -0
- package/dist/esm/models/ps-client-error-settings.js +1 -0
- package/dist/esm/models/ps-consent-api.js +1 -0
- package/dist/esm/models/ps-consent-choice.js +1 -0
- package/dist/esm/models/ps-consent-state.js +1 -0
- package/dist/esm/models/ps-gtag-window.js +1 -0
- package/dist/esm/services/analytics-csp-service.js +27 -0
- package/dist/esm/services/ps-analytics-key-service.js +8 -0
- package/dist/esm/services/ps-client-error-service.js +72 -0
- package/dist/esm/services/ps-consent-service.js +57 -0
- package/dist/esm/services/ps-google-analytics-service.js +41 -0
- package/dist/esm/services/ps-post-hog-service.js +18 -0
- package/dist/hooks/use-consent.d.ts +2 -0
- package/dist/hooks/use-consent.js +21 -0
- package/dist/hooks/use-ps-debounced-revalidate.d.ts +2 -0
- package/dist/hooks/use-ps-debounced-revalidate.js +27 -0
- package/dist/hooks/use-ps-live.d.ts +2 -0
- package/dist/hooks/use-ps-live.js +38 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/interfaces/ps-live-handle.d.ts +3 -0
- package/dist/interfaces/ps-live-handle.js +2 -0
- package/dist/interfaces/ps-live-input.d.ts +11 -0
- package/dist/interfaces/ps-live-input.js +2 -0
- package/dist/interfaces/ps-live-open-options.d.ts +7 -0
- package/dist/interfaces/ps-live-open-options.js +2 -0
- package/dist/models/analytics-csp-input.d.ts +7 -0
- package/dist/models/analytics-csp-input.js +2 -0
- package/dist/models/analytics-csp-origins.d.ts +5 -0
- package/dist/models/analytics-csp-origins.js +2 -0
- package/dist/models/ps-client-error-payload.d.ts +9 -0
- package/dist/models/ps-client-error-payload.js +2 -0
- package/dist/models/ps-client-error-settings.d.ts +4 -0
- package/dist/models/ps-client-error-settings.js +2 -0
- package/dist/models/ps-consent-api.d.ts +9 -0
- package/dist/models/ps-consent-api.js +2 -0
- package/dist/models/ps-consent-choice.d.ts +1 -0
- package/dist/models/ps-consent-choice.js +2 -0
- package/dist/models/ps-consent-state.d.ts +5 -0
- package/dist/models/ps-consent-state.js +2 -0
- package/dist/models/ps-gtag-window.d.ts +4 -0
- package/dist/models/ps-gtag-window.js +2 -0
- package/dist/services/analytics-csp-service.d.ts +7 -0
- package/dist/services/analytics-csp-service.js +31 -0
- package/dist/services/ps-analytics-key-service.d.ts +3 -0
- package/dist/services/ps-analytics-key-service.js +12 -0
- package/dist/services/ps-client-error-service.d.ts +14 -0
- package/dist/services/ps-client-error-service.js +76 -0
- package/dist/services/ps-consent-service.d.ts +15 -0
- package/dist/services/ps-consent-service.js +61 -0
- package/dist/services/ps-google-analytics-service.d.ts +6 -0
- package/dist/services/ps-google-analytics-service.js +45 -0
- package/dist/services/ps-post-hog-service.d.ts +4 -0
- package/dist/services/ps-post-hog-service.js +55 -0
- package/package.json +15 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from '../components/ps-analytics-provider';
|
|
2
|
+
export * from '../components/ps-client-error-reporter';
|
|
3
|
+
export * from '../components/ps-consent-banner';
|
|
4
|
+
export * from '../constants/ps-analytics-config';
|
|
5
|
+
export * from '../constants/ps-client-error-config';
|
|
6
|
+
export * from '../hooks/use-consent';
|
|
7
|
+
export * from '../models/analytics-csp-input';
|
|
8
|
+
export * from '../models/analytics-csp-origins';
|
|
9
|
+
export * from '../models/ps-consent-api';
|
|
10
|
+
export * from '../models/ps-consent-choice';
|
|
11
|
+
export * from '../services/analytics-csp-service';
|
|
12
|
+
export * from './report-client-error';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("../components/ps-analytics-provider"), exports);
|
|
18
|
+
__exportStar(require("../components/ps-client-error-reporter"), exports);
|
|
19
|
+
__exportStar(require("../components/ps-consent-banner"), exports);
|
|
20
|
+
__exportStar(require("../constants/ps-analytics-config"), exports);
|
|
21
|
+
__exportStar(require("../constants/ps-client-error-config"), exports);
|
|
22
|
+
__exportStar(require("../hooks/use-consent"), exports);
|
|
23
|
+
__exportStar(require("../models/analytics-csp-input"), exports);
|
|
24
|
+
__exportStar(require("../models/analytics-csp-origins"), exports);
|
|
25
|
+
__exportStar(require("../models/ps-consent-api"), exports);
|
|
26
|
+
__exportStar(require("../models/ps-consent-choice"), exports);
|
|
27
|
+
__exportStar(require("../services/analytics-csp-service"), exports);
|
|
28
|
+
__exportStar(require("./report-client-error"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const reportClientError: (error: unknown, context?: string) => void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reportClientError = void 0;
|
|
4
|
+
const ps_client_error_service_1 = require("../services/ps-client-error-service");
|
|
5
|
+
const reportClientError = (error, context) => ps_client_error_service_1.PsClientErrorService.report(error, context);
|
|
6
|
+
exports.reportClientError = reportClientError;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface PsAnalyticsProviderProps {
|
|
3
|
+
children?: ReactNode;
|
|
4
|
+
googleAnalyticsId?: string;
|
|
5
|
+
googleTagManagerUrl?: string;
|
|
6
|
+
postHogKey?: string;
|
|
7
|
+
postHogApiHost?: string;
|
|
8
|
+
userId?: string | null;
|
|
9
|
+
appVersion?: string;
|
|
10
|
+
nonce?: string;
|
|
11
|
+
consentCookieName?: string;
|
|
12
|
+
pathname?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const PsAnalyticsProvider: ({ children, googleAnalyticsId, googleTagManagerUrl, postHogKey, postHogApiHost, userId, appVersion, nonce, consentCookieName, pathname, }: PsAnalyticsProviderProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsAnalyticsProvider = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const ps_analytics_config_1 = require("../constants/ps-analytics-config");
|
|
7
|
+
const use_consent_1 = require("../hooks/use-consent");
|
|
8
|
+
const ps_analytics_key_service_1 = require("../services/ps-analytics-key-service");
|
|
9
|
+
const ps_google_analytics_service_1 = require("../services/ps-google-analytics-service");
|
|
10
|
+
const ps_post_hog_service_1 = require("../services/ps-post-hog-service");
|
|
11
|
+
const PsAnalyticsProvider = ({ children, googleAnalyticsId, googleTagManagerUrl = ps_analytics_config_1.PsAnalyticsConfig.defaultTagManagerUrl, postHogKey, postHogApiHost, userId, appVersion, nonce, consentCookieName = ps_analytics_config_1.PsAnalyticsConfig.defaultConsentCookieName, pathname, }) => {
|
|
12
|
+
const { isGranted } = (0, use_consent_1.useConsent)(consentCookieName);
|
|
13
|
+
const [postHog, setPostHog] = (0, react_1.useState)(null);
|
|
14
|
+
const identifiedRef = (0, react_1.useRef)(null);
|
|
15
|
+
const isGoogleOn = ps_analytics_key_service_1.PsAnalyticsKeyService.isEnabled(googleAnalyticsId);
|
|
16
|
+
const isPostHogOn = ps_analytics_key_service_1.PsAnalyticsKeyService.isEnabled(postHogKey);
|
|
17
|
+
const currentPath = pathname !== undefined ? pathname : '';
|
|
18
|
+
const normalizedUserId = userId ? userId : null;
|
|
19
|
+
(0, react_1.useEffect)(() => {
|
|
20
|
+
if (!isGoogleOn)
|
|
21
|
+
return;
|
|
22
|
+
if (!isGranted) {
|
|
23
|
+
ps_google_analytics_service_1.PsGoogleAnalyticsService.disable(googleAnalyticsId);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
ps_google_analytics_service_1.PsGoogleAnalyticsService.load(googleAnalyticsId, googleTagManagerUrl, nonce);
|
|
27
|
+
}, [isGoogleOn, isGranted, googleAnalyticsId, googleTagManagerUrl, nonce]);
|
|
28
|
+
(0, react_1.useEffect)(() => {
|
|
29
|
+
if (!isGoogleOn || !isGranted)
|
|
30
|
+
return;
|
|
31
|
+
ps_google_analytics_service_1.PsGoogleAnalyticsService.configure(googleAnalyticsId, currentPath, normalizedUserId);
|
|
32
|
+
}, [isGoogleOn, isGranted, googleAnalyticsId, currentPath, normalizedUserId]);
|
|
33
|
+
(0, react_1.useEffect)(() => {
|
|
34
|
+
if (!isPostHogOn)
|
|
35
|
+
return;
|
|
36
|
+
if (!isGranted) {
|
|
37
|
+
if (postHog)
|
|
38
|
+
postHog.opt_out_capturing();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let isCancelled = false;
|
|
42
|
+
ps_post_hog_service_1.PsPostHogService.load(postHogKey, postHogApiHost).then((client) => {
|
|
43
|
+
if (!isCancelled)
|
|
44
|
+
setPostHog(client);
|
|
45
|
+
});
|
|
46
|
+
return () => {
|
|
47
|
+
isCancelled = true;
|
|
48
|
+
};
|
|
49
|
+
}, [isPostHogOn, isGranted, postHogKey, postHogApiHost]);
|
|
50
|
+
(0, react_1.useEffect)(() => {
|
|
51
|
+
if (!postHog || !isGranted)
|
|
52
|
+
return;
|
|
53
|
+
if (normalizedUserId) {
|
|
54
|
+
postHog.identify(normalizedUserId, appVersion ? { app_version: appVersion } : undefined);
|
|
55
|
+
identifiedRef.current = normalizedUserId;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (identifiedRef.current) {
|
|
59
|
+
postHog.reset();
|
|
60
|
+
identifiedRef.current = null;
|
|
61
|
+
}
|
|
62
|
+
}, [postHog, isGranted, normalizedUserId, appVersion]);
|
|
63
|
+
(0, react_1.useEffect)(() => {
|
|
64
|
+
if (!postHog || !isGranted)
|
|
65
|
+
return;
|
|
66
|
+
postHog.capture(ps_analytics_config_1.PsAnalyticsConfig.postHog.pageviewEvent, {
|
|
67
|
+
$pathname: currentPath,
|
|
68
|
+
});
|
|
69
|
+
}, [postHog, isGranted, currentPath]);
|
|
70
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
|
|
71
|
+
};
|
|
72
|
+
exports.PsAnalyticsProvider = PsAnalyticsProvider;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsClientErrorReporter = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ps_client_error_config_1 = require("../constants/ps-client-error-config");
|
|
6
|
+
const ps_client_error_service_1 = require("../services/ps-client-error-service");
|
|
7
|
+
const PsClientErrorReporter = ({ endpoint, appVersion }) => {
|
|
8
|
+
(0, react_1.useEffect)(() => {
|
|
9
|
+
ps_client_error_service_1.PsClientErrorService.configure({ endpoint, appVersion });
|
|
10
|
+
const onError = (event) => ps_client_error_service_1.PsClientErrorService.report(event.error ? event.error : event.message, '', ps_client_error_config_1.PsClientErrorConfig.sources.error);
|
|
11
|
+
const onRejection = (event) => ps_client_error_service_1.PsClientErrorService.report(event.reason, '', ps_client_error_config_1.PsClientErrorConfig.sources.rejection);
|
|
12
|
+
window.addEventListener('error', onError);
|
|
13
|
+
window.addEventListener('unhandledrejection', onRejection);
|
|
14
|
+
return () => {
|
|
15
|
+
window.removeEventListener('error', onError);
|
|
16
|
+
window.removeEventListener('unhandledrejection', onRejection);
|
|
17
|
+
ps_client_error_service_1.PsClientErrorService.clear();
|
|
18
|
+
};
|
|
19
|
+
}, [endpoint, appVersion]);
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
exports.PsClientErrorReporter = PsClientErrorReporter;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface PsConsentBannerProps {
|
|
2
|
+
title: string;
|
|
3
|
+
message: string;
|
|
4
|
+
acceptLabel: string;
|
|
5
|
+
rejectLabel: string;
|
|
6
|
+
privacyPolicyHref: string;
|
|
7
|
+
privacyPolicyLabel: string;
|
|
8
|
+
consentCookieName?: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const PsConsentBanner: ({ title, message, acceptLabel, rejectLabel, privacyPolicyHref, privacyPolicyLabel, consentCookieName, className, }: PsConsentBannerProps) => import("react").JSX.Element | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsConsentBanner = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
|
+
const ps_analytics_config_1 = require("../constants/ps-analytics-config");
|
|
8
|
+
const use_consent_1 = require("../hooks/use-consent");
|
|
9
|
+
const ps_button_1 = require("./ps-button");
|
|
10
|
+
const PsConsentBanner = ({ title, message, acceptLabel, rejectLabel, privacyPolicyHref, privacyPolicyLabel, consentCookieName = ps_analytics_config_1.PsAnalyticsConfig.defaultConsentCookieName, className, }) => {
|
|
11
|
+
const { isBannerOpen, accept, reject } = (0, use_consent_1.useConsent)(consentCookieName);
|
|
12
|
+
const ref = (0, react_1.useRef)(null);
|
|
13
|
+
(0, react_1.useEffect)(() => {
|
|
14
|
+
if (isBannerOpen)
|
|
15
|
+
ref.current?.focus();
|
|
16
|
+
}, [isBannerOpen]);
|
|
17
|
+
if (!isBannerOpen)
|
|
18
|
+
return null;
|
|
19
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: ref, role: 'dialog', "aria-label": title, "aria-live": 'polite', tabIndex: -1, className: `ps-card fixed inset-x-3 bottom-3 flex flex-col gap-3 p-4 shadow-lg outline-none sm:left-auto sm:right-4 sm:max-w-md ${className ? className : ''}`, style: { zIndex: ps_design_system_1.PallasoftZIndex.Toast }, children: [(0, jsx_runtime_1.jsxs)("p", { className: 'text-sm', children: [message, ' ', (0, jsx_runtime_1.jsx)("a", { className: 'underline', href: privacyPolicyHref, children: privacyPolicyLabel })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex gap-2', children: [(0, jsx_runtime_1.jsx)(ps_button_1.PsButton, { className: 'flex-1', size: 'compact', variant: 'secondary', onClick: reject, children: rejectLabel }), (0, jsx_runtime_1.jsx)(ps_button_1.PsButton, { className: 'flex-1', size: 'compact', variant: 'secondary', onClick: accept, children: acceptLabel })] })] }));
|
|
20
|
+
};
|
|
21
|
+
exports.PsConsentBanner = PsConsentBanner;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const PsAnalyticsConfig: {
|
|
2
|
+
readonly offValues: readonly ["", "_"];
|
|
3
|
+
readonly defaultConsentCookieName: "ps_consent";
|
|
4
|
+
readonly consentGranted: "granted";
|
|
5
|
+
readonly consentDenied: "denied";
|
|
6
|
+
readonly consentMaxAgeSeconds: 31536000;
|
|
7
|
+
readonly defaultTagManagerUrl: "https://www.googletagmanager.com";
|
|
8
|
+
readonly tagManagerScriptPath: "/gtag/js?id=";
|
|
9
|
+
readonly scriptAttribute: "data-ps-analytics";
|
|
10
|
+
readonly postHog: {
|
|
11
|
+
readonly defaults: "2026-05-30";
|
|
12
|
+
readonly personProfiles: "identified_only";
|
|
13
|
+
readonly autocapture: true;
|
|
14
|
+
readonly capturePageleave: true;
|
|
15
|
+
readonly captureExceptions: true;
|
|
16
|
+
readonly pageviewEvent: "$pageview";
|
|
17
|
+
};
|
|
18
|
+
readonly listSeparator: ",";
|
|
19
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsAnalyticsConfig = void 0;
|
|
4
|
+
exports.PsAnalyticsConfig = {
|
|
5
|
+
offValues: ['', '_'],
|
|
6
|
+
defaultConsentCookieName: 'ps_consent',
|
|
7
|
+
consentGranted: 'granted',
|
|
8
|
+
consentDenied: 'denied',
|
|
9
|
+
consentMaxAgeSeconds: 31536000,
|
|
10
|
+
defaultTagManagerUrl: 'https://www.googletagmanager.com',
|
|
11
|
+
tagManagerScriptPath: '/gtag/js?id=',
|
|
12
|
+
scriptAttribute: 'data-ps-analytics',
|
|
13
|
+
postHog: {
|
|
14
|
+
defaults: '2026-05-30',
|
|
15
|
+
personProfiles: 'identified_only',
|
|
16
|
+
autocapture: true,
|
|
17
|
+
capturePageleave: true,
|
|
18
|
+
captureExceptions: true,
|
|
19
|
+
pageviewEvent: '$pageview',
|
|
20
|
+
},
|
|
21
|
+
listSeparator: ',',
|
|
22
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const PsClientErrorConfig: {
|
|
2
|
+
readonly maxReportsPerPage: 10;
|
|
3
|
+
readonly maxStackLength: 2048;
|
|
4
|
+
readonly maxMessageLength: 500;
|
|
5
|
+
readonly maxContextLength: 200;
|
|
6
|
+
readonly jsonContentType: "application/json";
|
|
7
|
+
readonly emailPattern: RegExp;
|
|
8
|
+
readonly redactedValue: "[redacted]";
|
|
9
|
+
readonly unknownMessage: "Unknown error";
|
|
10
|
+
readonly botPattern: RegExp;
|
|
11
|
+
readonly mobilePattern: RegExp;
|
|
12
|
+
readonly userAgentClasses: {
|
|
13
|
+
readonly bot: "bot";
|
|
14
|
+
readonly mobile: "mobile";
|
|
15
|
+
readonly desktop: "desktop";
|
|
16
|
+
readonly unknown: "unknown";
|
|
17
|
+
};
|
|
18
|
+
readonly sources: {
|
|
19
|
+
readonly error: "error";
|
|
20
|
+
readonly rejection: "unhandledrejection";
|
|
21
|
+
readonly boundary: "boundary";
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsClientErrorConfig = void 0;
|
|
4
|
+
exports.PsClientErrorConfig = {
|
|
5
|
+
maxReportsPerPage: 10,
|
|
6
|
+
maxStackLength: 2048,
|
|
7
|
+
maxMessageLength: 500,
|
|
8
|
+
maxContextLength: 200,
|
|
9
|
+
jsonContentType: 'application/json',
|
|
10
|
+
emailPattern: /[^\s@"'()<>]+@[^\s@"'()<>]+\.[^\s@"'()<>]+/g,
|
|
11
|
+
redactedValue: '[redacted]',
|
|
12
|
+
unknownMessage: 'Unknown error',
|
|
13
|
+
botPattern: /bot|crawl|spider|slurp|headless/i,
|
|
14
|
+
mobilePattern: /mobi|android|iphone|ipad/i,
|
|
15
|
+
userAgentClasses: { bot: 'bot', mobile: 'mobile', desktop: 'desktop', unknown: 'unknown' },
|
|
16
|
+
sources: { error: 'error', rejection: 'unhandledrejection', boundary: 'boundary' },
|
|
17
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from '../components/ps-analytics-provider';
|
|
2
|
+
export * from '../components/ps-client-error-reporter';
|
|
3
|
+
export * from '../components/ps-consent-banner';
|
|
4
|
+
export * from '../constants/ps-analytics-config';
|
|
5
|
+
export * from '../constants/ps-client-error-config';
|
|
6
|
+
export * from '../hooks/use-consent';
|
|
7
|
+
export * from '../models/analytics-csp-input';
|
|
8
|
+
export * from '../models/analytics-csp-origins';
|
|
9
|
+
export * from '../models/ps-consent-api';
|
|
10
|
+
export * from '../models/ps-consent-choice';
|
|
11
|
+
export * from '../services/analytics-csp-service';
|
|
12
|
+
export * from './report-client-error';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { PsAnalyticsConfig } from '../constants/ps-analytics-config';
|
|
4
|
+
import { useConsent } from '../hooks/use-consent';
|
|
5
|
+
import { PsAnalyticsKeyService } from '../services/ps-analytics-key-service';
|
|
6
|
+
import { PsGoogleAnalyticsService } from '../services/ps-google-analytics-service';
|
|
7
|
+
import { PsPostHogService } from '../services/ps-post-hog-service';
|
|
8
|
+
export const PsAnalyticsProvider = ({ children, googleAnalyticsId, googleTagManagerUrl = PsAnalyticsConfig.defaultTagManagerUrl, postHogKey, postHogApiHost, userId, appVersion, nonce, consentCookieName = PsAnalyticsConfig.defaultConsentCookieName, pathname, }) => {
|
|
9
|
+
const { isGranted } = useConsent(consentCookieName);
|
|
10
|
+
const [postHog, setPostHog] = useState(null);
|
|
11
|
+
const identifiedRef = useRef(null);
|
|
12
|
+
const isGoogleOn = PsAnalyticsKeyService.isEnabled(googleAnalyticsId);
|
|
13
|
+
const isPostHogOn = PsAnalyticsKeyService.isEnabled(postHogKey);
|
|
14
|
+
const currentPath = pathname !== undefined ? pathname : '';
|
|
15
|
+
const normalizedUserId = userId ? userId : null;
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!isGoogleOn)
|
|
18
|
+
return;
|
|
19
|
+
if (!isGranted) {
|
|
20
|
+
PsGoogleAnalyticsService.disable(googleAnalyticsId);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
PsGoogleAnalyticsService.load(googleAnalyticsId, googleTagManagerUrl, nonce);
|
|
24
|
+
}, [isGoogleOn, isGranted, googleAnalyticsId, googleTagManagerUrl, nonce]);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!isGoogleOn || !isGranted)
|
|
27
|
+
return;
|
|
28
|
+
PsGoogleAnalyticsService.configure(googleAnalyticsId, currentPath, normalizedUserId);
|
|
29
|
+
}, [isGoogleOn, isGranted, googleAnalyticsId, currentPath, normalizedUserId]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!isPostHogOn)
|
|
32
|
+
return;
|
|
33
|
+
if (!isGranted) {
|
|
34
|
+
if (postHog)
|
|
35
|
+
postHog.opt_out_capturing();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let isCancelled = false;
|
|
39
|
+
PsPostHogService.load(postHogKey, postHogApiHost).then((client) => {
|
|
40
|
+
if (!isCancelled)
|
|
41
|
+
setPostHog(client);
|
|
42
|
+
});
|
|
43
|
+
return () => {
|
|
44
|
+
isCancelled = true;
|
|
45
|
+
};
|
|
46
|
+
}, [isPostHogOn, isGranted, postHogKey, postHogApiHost]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!postHog || !isGranted)
|
|
49
|
+
return;
|
|
50
|
+
if (normalizedUserId) {
|
|
51
|
+
postHog.identify(normalizedUserId, appVersion ? { app_version: appVersion } : undefined);
|
|
52
|
+
identifiedRef.current = normalizedUserId;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (identifiedRef.current) {
|
|
56
|
+
postHog.reset();
|
|
57
|
+
identifiedRef.current = null;
|
|
58
|
+
}
|
|
59
|
+
}, [postHog, isGranted, normalizedUserId, appVersion]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!postHog || !isGranted)
|
|
62
|
+
return;
|
|
63
|
+
postHog.capture(PsAnalyticsConfig.postHog.pageviewEvent, {
|
|
64
|
+
$pathname: currentPath,
|
|
65
|
+
});
|
|
66
|
+
}, [postHog, isGranted, currentPath]);
|
|
67
|
+
return _jsx(_Fragment, { children: children });
|
|
68
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { PsClientErrorConfig } from '../constants/ps-client-error-config';
|
|
3
|
+
import { PsClientErrorService } from '../services/ps-client-error-service';
|
|
4
|
+
export const PsClientErrorReporter = ({ endpoint, appVersion }) => {
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
PsClientErrorService.configure({ endpoint, appVersion });
|
|
7
|
+
const onError = (event) => PsClientErrorService.report(event.error ? event.error : event.message, '', PsClientErrorConfig.sources.error);
|
|
8
|
+
const onRejection = (event) => PsClientErrorService.report(event.reason, '', PsClientErrorConfig.sources.rejection);
|
|
9
|
+
window.addEventListener('error', onError);
|
|
10
|
+
window.addEventListener('unhandledrejection', onRejection);
|
|
11
|
+
return () => {
|
|
12
|
+
window.removeEventListener('error', onError);
|
|
13
|
+
window.removeEventListener('unhandledrejection', onRejection);
|
|
14
|
+
PsClientErrorService.clear();
|
|
15
|
+
};
|
|
16
|
+
}, [endpoint, appVersion]);
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { PallasoftZIndex } from '@pallasoft/ps-design-system';
|
|
4
|
+
import { PsAnalyticsConfig } from '../constants/ps-analytics-config';
|
|
5
|
+
import { useConsent } from '../hooks/use-consent';
|
|
6
|
+
import { PsButton } from './ps-button';
|
|
7
|
+
export const PsConsentBanner = ({ title, message, acceptLabel, rejectLabel, privacyPolicyHref, privacyPolicyLabel, consentCookieName = PsAnalyticsConfig.defaultConsentCookieName, className, }) => {
|
|
8
|
+
const { isBannerOpen, accept, reject } = useConsent(consentCookieName);
|
|
9
|
+
const ref = useRef(null);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (isBannerOpen)
|
|
12
|
+
ref.current?.focus();
|
|
13
|
+
}, [isBannerOpen]);
|
|
14
|
+
if (!isBannerOpen)
|
|
15
|
+
return null;
|
|
16
|
+
return (_jsxs("div", { ref: ref, role: 'dialog', "aria-label": title, "aria-live": 'polite', tabIndex: -1, className: `ps-card fixed inset-x-3 bottom-3 flex flex-col gap-3 p-4 shadow-lg outline-none sm:left-auto sm:right-4 sm:max-w-md ${className ? className : ''}`, style: { zIndex: PallasoftZIndex.Toast }, children: [_jsxs("p", { className: 'text-sm', children: [message, ' ', _jsx("a", { className: 'underline', href: privacyPolicyHref, children: privacyPolicyLabel })] }), _jsxs("div", { className: 'flex gap-2', children: [_jsx(PsButton, { className: 'flex-1', size: 'compact', variant: 'secondary', onClick: reject, children: rejectLabel }), _jsx(PsButton, { className: 'flex-1', size: 'compact', variant: 'secondary', onClick: accept, children: acceptLabel })] })] }));
|
|
17
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const PsAnalyticsConfig = {
|
|
2
|
+
offValues: ['', '_'],
|
|
3
|
+
defaultConsentCookieName: 'ps_consent',
|
|
4
|
+
consentGranted: 'granted',
|
|
5
|
+
consentDenied: 'denied',
|
|
6
|
+
consentMaxAgeSeconds: 31536000,
|
|
7
|
+
defaultTagManagerUrl: 'https://www.googletagmanager.com',
|
|
8
|
+
tagManagerScriptPath: '/gtag/js?id=',
|
|
9
|
+
scriptAttribute: 'data-ps-analytics',
|
|
10
|
+
postHog: {
|
|
11
|
+
defaults: '2026-05-30',
|
|
12
|
+
personProfiles: 'identified_only',
|
|
13
|
+
autocapture: true,
|
|
14
|
+
capturePageleave: true,
|
|
15
|
+
captureExceptions: true,
|
|
16
|
+
pageviewEvent: '$pageview',
|
|
17
|
+
},
|
|
18
|
+
listSeparator: ',',
|
|
19
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const PsClientErrorConfig = {
|
|
2
|
+
maxReportsPerPage: 10,
|
|
3
|
+
maxStackLength: 2048,
|
|
4
|
+
maxMessageLength: 500,
|
|
5
|
+
maxContextLength: 200,
|
|
6
|
+
jsonContentType: 'application/json',
|
|
7
|
+
emailPattern: /[^\s@"'()<>]+@[^\s@"'()<>]+\.[^\s@"'()<>]+/g,
|
|
8
|
+
redactedValue: '[redacted]',
|
|
9
|
+
unknownMessage: 'Unknown error',
|
|
10
|
+
botPattern: /bot|crawl|spider|slurp|headless/i,
|
|
11
|
+
mobilePattern: /mobi|android|iphone|ipad/i,
|
|
12
|
+
userAgentClasses: { bot: 'bot', mobile: 'mobile', desktop: 'desktop', unknown: 'unknown' },
|
|
13
|
+
sources: { error: 'error', rejection: 'unhandledrejection', boundary: 'boundary' },
|
|
14
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
2
|
+
import { PsAnalyticsConfig } from '../constants/ps-analytics-config';
|
|
3
|
+
import { PsConsentService } from '../services/ps-consent-service';
|
|
4
|
+
export const useConsent = (consentCookieName = PsAnalyticsConfig.defaultConsentCookieName) => {
|
|
5
|
+
const state = useSyncExternalStore((listener) => PsConsentService.subscribe(listener), () => PsConsentService.snapshot(consentCookieName), () => PsConsentService.serverState);
|
|
6
|
+
const accept = useCallback(() => PsConsentService.choose(consentCookieName, PsAnalyticsConfig.consentGranted), [consentCookieName]);
|
|
7
|
+
const reject = useCallback(() => PsConsentService.choose(consentCookieName, PsAnalyticsConfig.consentDenied), [consentCookieName]);
|
|
8
|
+
const reopen = useCallback(() => PsConsentService.reopen(consentCookieName), [consentCookieName]);
|
|
9
|
+
return {
|
|
10
|
+
choice: state.choice,
|
|
11
|
+
isGranted: state.choice === PsAnalyticsConfig.consentGranted,
|
|
12
|
+
isBannerOpen: state === PsConsentService.serverState ? false : state.choice === null || state.isReopened,
|
|
13
|
+
accept,
|
|
14
|
+
reject,
|
|
15
|
+
reopen,
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
export const usePsDebouncedRevalidate = (revalidator, delayMilliseconds) => {
|
|
3
|
+
const latest = useRef(revalidator);
|
|
4
|
+
const timer = useRef(null);
|
|
5
|
+
latest.current = revalidator;
|
|
6
|
+
const fire = useCallback(() => {
|
|
7
|
+
timer.current = null;
|
|
8
|
+
if (latest.current.state !== 'idle') {
|
|
9
|
+
timer.current = setTimeout(fire, delayMilliseconds);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
void latest.current.revalidate();
|
|
13
|
+
}, [delayMilliseconds]);
|
|
14
|
+
useEffect(() => () => {
|
|
15
|
+
if (timer.current !== null)
|
|
16
|
+
clearTimeout(timer.current);
|
|
17
|
+
}, []);
|
|
18
|
+
return useCallback(() => {
|
|
19
|
+
if (timer.current !== null)
|
|
20
|
+
clearTimeout(timer.current);
|
|
21
|
+
timer.current = setTimeout(fire, delayMilliseconds);
|
|
22
|
+
}, [delayMilliseconds, fire]);
|
|
23
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
export const usePsLive = (input) => {
|
|
3
|
+
const latest = useRef(input);
|
|
4
|
+
const { enabled, open, path } = input;
|
|
5
|
+
const eventKey = input.events.join(',');
|
|
6
|
+
latest.current = input;
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!enabled)
|
|
9
|
+
return;
|
|
10
|
+
let isActive = true;
|
|
11
|
+
let close = null;
|
|
12
|
+
void open({
|
|
13
|
+
events: latest.current.events,
|
|
14
|
+
fetchToken: () => latest.current.fetchToken(),
|
|
15
|
+
onEvent: (name, payload) => latest.current.onEvent(name, payload),
|
|
16
|
+
onResume: () => {
|
|
17
|
+
const { onResume } = latest.current;
|
|
18
|
+
if (onResume !== undefined)
|
|
19
|
+
onResume();
|
|
20
|
+
},
|
|
21
|
+
path,
|
|
22
|
+
}).then((handle) => {
|
|
23
|
+
if (isActive)
|
|
24
|
+
close = () => handle.close();
|
|
25
|
+
else
|
|
26
|
+
handle.close();
|
|
27
|
+
});
|
|
28
|
+
return () => {
|
|
29
|
+
isActive = false;
|
|
30
|
+
if (close !== null)
|
|
31
|
+
close();
|
|
32
|
+
};
|
|
33
|
+
}, [enabled, eventKey, open, path]);
|
|
34
|
+
};
|
package/dist/esm/index.js
CHANGED
|
@@ -76,6 +76,8 @@ export * from './hooks/use-ps-debounced-value';
|
|
|
76
76
|
export * from './hooks/use-ps-change-queue';
|
|
77
77
|
export * from './interfaces/ps-change-queue-input';
|
|
78
78
|
export * from './interfaces/ps-change-queue-result';
|
|
79
|
+
export * from './hooks/use-ps-live';
|
|
80
|
+
export * from './hooks/use-ps-debounced-revalidate';
|
|
79
81
|
export * from './hooks/use-ps-optimistic-rows';
|
|
80
82
|
export * from './hooks/use-ps-optimistic-fetcher';
|
|
81
83
|
export * from './hooks/use-ps-optimistic-save';
|
|
@@ -88,6 +90,9 @@ export * from './services/ps-optimistic-save-service';
|
|
|
88
90
|
export * from './services/ps-client-param-revalidation-service';
|
|
89
91
|
export * from './interfaces/ps-fetcher-like';
|
|
90
92
|
export * from './interfaces/ps-fetcher-submit-options';
|
|
93
|
+
export * from './interfaces/ps-live-handle';
|
|
94
|
+
export * from './interfaces/ps-live-input';
|
|
95
|
+
export * from './interfaces/ps-live-open-options';
|
|
91
96
|
export * from './interfaces/ps-optimistic-fetcher-input';
|
|
92
97
|
export * from './interfaces/ps-optimistic-fetcher-result';
|
|
93
98
|
export * from './interfaces/ps-revalidator-like';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PsAnalyticsConfig } from '../constants/ps-analytics-config';
|
|
2
|
+
import { PsAnalyticsKeyService } from './ps-analytics-key-service';
|
|
3
|
+
export class AnalyticsCspService {
|
|
4
|
+
static origins(input) {
|
|
5
|
+
const isGoogleOn = PsAnalyticsKeyService.isEnabled(input.googleAnalyticsId);
|
|
6
|
+
const isPostHogOn = PsAnalyticsKeyService.isEnabled(input.postHogKey);
|
|
7
|
+
const googleUrls = isGoogleOn ? this.parse(input.googleAnalyticsUrls) : [];
|
|
8
|
+
const tagManagerUrls = isGoogleOn ? this.parse(input.googleTagManagerUrls) : [];
|
|
9
|
+
const postHogUrls = isPostHogOn ? this.parse(input.postHogUrls) : [];
|
|
10
|
+
return {
|
|
11
|
+
scriptSrc: this.unique([...tagManagerUrls, ...postHogUrls]),
|
|
12
|
+
connectSrc: this.unique([...googleUrls, ...tagManagerUrls, ...postHogUrls]),
|
|
13
|
+
imgSrc: this.unique([...googleUrls, ...tagManagerUrls]),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
static parse(value) {
|
|
17
|
+
if (!value)
|
|
18
|
+
return [];
|
|
19
|
+
return value
|
|
20
|
+
.split(PsAnalyticsConfig.listSeparator)
|
|
21
|
+
.map((item) => item.trim())
|
|
22
|
+
.filter((item) => PsAnalyticsKeyService.isEnabled(item));
|
|
23
|
+
}
|
|
24
|
+
static unique(values) {
|
|
25
|
+
return Array.from(new Set(values));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PsAnalyticsConfig } from '../constants/ps-analytics-config';
|
|
2
|
+
export class PsAnalyticsKeyService {
|
|
3
|
+
static isEnabled(value) {
|
|
4
|
+
if (value === null || value === undefined)
|
|
5
|
+
return false;
|
|
6
|
+
return !PsAnalyticsConfig.offValues.includes(value.trim());
|
|
7
|
+
}
|
|
8
|
+
}
|