@lib-acr/react 0.0.0-stage → 0.0.2
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/LICENSE +21 -0
- package/README.md +87 -2
- package/dist/cjs/context.js +44 -0
- package/dist/cjs/context.js.map +1 -0
- package/dist/cjs/control.js +71 -0
- package/dist/cjs/control.js.map +1 -0
- package/dist/cjs/error-boundary.js +46 -0
- package/dist/cjs/error-boundary.js.map +1 -0
- package/dist/cjs/hooks.js +60 -0
- package/dist/cjs/hooks.js.map +1 -0
- package/dist/cjs/index.js +38 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/router.js +24 -0
- package/dist/cjs/router.js.map +1 -0
- package/dist/cjs/sdk-info.js +6 -0
- package/dist/cjs/sdk-info.js.map +1 -0
- package/dist/esm/context.js +39 -0
- package/dist/esm/context.js.map +1 -0
- package/dist/esm/control.js +64 -0
- package/dist/esm/control.js.map +1 -0
- package/dist/esm/error-boundary.js +43 -0
- package/dist/esm/error-boundary.js.map +1 -0
- package/dist/esm/hooks.js +54 -0
- package/dist/esm/hooks.js.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/router.js +20 -0
- package/dist/esm/router.js.map +1 -0
- package/dist/esm/sdk-info.js +3 -0
- package/dist/esm/sdk-info.js.map +1 -0
- package/dist/types/context.d.ts +29 -0
- package/dist/types/control.d.ts +59 -0
- package/dist/types/error-boundary.d.ts +13 -0
- package/dist/types/hooks.d.ts +36 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/router.d.ts +7 -0
- package/dist/types/sdk-info.d.ts +4 -0
- package/package.json +64 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Application Control Room
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,88 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lib-acr/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React SDK: ACRProvider, hooks (useACR, useACREvent, useACRUser, useACRMetrics), an error boundary, and route tracking from `@lib-acr/react/router` for React Router.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @lib-acr/react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { ACRProvider, ACRErrorBoundary, useACREvent } from '@lib-acr/react';
|
|
15
|
+
import { ACRRouteTracker } from '@lib-acr/react/router'; // React Router v6.4+ only
|
|
16
|
+
|
|
17
|
+
<ACRProvider config={{ apiKey: process.env.REACT_APP_ACR_API_KEY!, endpoint: 'https://acr.example.com/v1' }}>
|
|
18
|
+
<BrowserRouter>
|
|
19
|
+
<ACRRouteTracker />
|
|
20
|
+
<ACRErrorBoundary fallback={<p>Something went wrong.</p>}>
|
|
21
|
+
<App />
|
|
22
|
+
</ACRErrorBoundary>
|
|
23
|
+
</BrowserRouter>
|
|
24
|
+
</ACRProvider>
|
|
25
|
+
|
|
26
|
+
function AddToCart() {
|
|
27
|
+
const track = useACREvent();
|
|
28
|
+
return <button onClick={() => track('product_added_to_cart', { productId: '123', price: 500 })}>Add</button>;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Notes
|
|
33
|
+
|
|
34
|
+
Never pass a secret key to the provider; it is rejected. User traits are sent only with `<ACRProvider collectUserTraits>`. The client is created once, so changes to `config` after mount are ignored.
|
|
35
|
+
|
|
36
|
+
- **Logout:** `useACR().reset()` (or `useACRUser().reset()`) clears the user id, creates a new anonymous id and
|
|
37
|
+
starts a new session.
|
|
38
|
+
- **Server-side rendering:** no client is created on the server, so no timers start there. Hooks are no-ops until the
|
|
39
|
+
provider renders in the browser, and `useACR().client` is `null` meanwhile.
|
|
40
|
+
- **Options:** all `@lib-acr/browser` options apply, including `minLogLevel` (default `warn`) and
|
|
41
|
+
`activePingMinutes` (default 5, `0` turns the live-visitor ping off).
|
|
42
|
+
- The entry point does not re-export `AcrClient`, `HttpTransport` or `resolveConfig` from `@lib-acr/core`.
|
|
43
|
+
|
|
44
|
+
## Remote control
|
|
45
|
+
|
|
46
|
+
`config.ui` controls the built-in maintenance screen, announcement banner and update prompt (default: all on). Turn
|
|
47
|
+
parts off to render your own:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { ACRAnnouncement, ACRMaintenanceGate, useACRControls } from '@lib-acr/react';
|
|
51
|
+
|
|
52
|
+
<ACRProvider config={{ apiKey, endpoint, appVersion: '2.3.0', ui: { maintenance: false, announcement: false } }}>
|
|
53
|
+
<ACRAnnouncement render={({ announcement, dismiss }) => <Banner text={announcement.message} onClose={dismiss} />} />
|
|
54
|
+
<ACRMaintenanceGate fallback={<MaintenancePage />}>
|
|
55
|
+
<App />
|
|
56
|
+
</ACRMaintenanceGate>
|
|
57
|
+
</ACRProvider>;
|
|
58
|
+
|
|
59
|
+
function UpdateNotice() {
|
|
60
|
+
const { release, reloadRequested } = useACRControls(); // also maintenance, announcement, flags, revision, refresh()
|
|
61
|
+
return release?.updateAvailable || reloadRequested ? <button onClick={() => location.reload()}>Update</button> : null;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`ACRMaintenanceGate` renders its children unless maintenance applies to the current user. Its `fallback` defaults to
|
|
66
|
+
nothing, so the built-in screen is what visitors see. Users in `allowUserIds` always see the children.
|
|
67
|
+
|
|
68
|
+
## Feature flags
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
const newCheckout = useFlag('new-checkout', false);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`useFlag` re-renders when the snapshot or the identified user changes. During server-side rendering and before the
|
|
75
|
+
first snapshot, it and `useACRControls` return fallbacks and nothing is fetched.
|
|
76
|
+
|
|
77
|
+
## Wire protocol
|
|
78
|
+
|
|
79
|
+
Every SDK sends the same protocol: `POST {endpoint}/ingest/{events|logs|errors|heartbeat}` with the `X-ACR-Api-Key`
|
|
80
|
+
header (and `X-ACR-Secret-Key` for server keys). The credential determines the app and environment. The body is
|
|
81
|
+
`{ schemaVersion: 1, sdk, items[1..500] }`.
|
|
82
|
+
Each item has a client `id`, which lets the server drop duplicates when a batch is retried.
|
|
83
|
+
|
|
84
|
+
Retries use exponential backoff with jitter for network errors, 429 and 5xx responses. Other 4xx responses are not retried.
|
|
85
|
+
Telemetry is buffered in a bounded in-memory queue. When it is full, the oldest items are dropped and counted in `stats()`.
|
|
86
|
+
SDK code never throws into the host application.
|
|
87
|
+
|
|
88
|
+
Metrics (`increment`, `gauge`, `histogram`) and traces are not yet available.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACRContext = void 0;
|
|
4
|
+
exports.ACRProvider = ACRProvider;
|
|
5
|
+
exports.useACRContext = useACRContext;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const browser_1 = require("@lib-acr/browser");
|
|
9
|
+
const sdk_info_js_1 = require("./sdk-info.js");
|
|
10
|
+
exports.ACRContext = (0, react_1.createContext)(null);
|
|
11
|
+
const isBrowser = () => typeof window !== 'undefined';
|
|
12
|
+
/**
|
|
13
|
+
* Creates one BrowserACR for the app, starts automatic collection on mount and flushes it on unmount. The client is
|
|
14
|
+
* created once, so later changes to `config` are ignored. During server-side rendering no client is created (it would
|
|
15
|
+
* start timers on the server); the hooks are no-ops until the provider renders in the browser.
|
|
16
|
+
*/
|
|
17
|
+
function ACRProvider({ config, collectUserTraits = false, children }) {
|
|
18
|
+
const [client] = (0, react_1.useState)(() => {
|
|
19
|
+
if (!isBrowser()) {
|
|
20
|
+
// Still fail fast on a secret key, even though no client is created here.
|
|
21
|
+
(0, browser_1.assertPublishableOptions)(config);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return new browser_1.BrowserACR(config, sdk_info_js_1.SDK_INFO);
|
|
25
|
+
});
|
|
26
|
+
(0, react_1.useEffect)(() => {
|
|
27
|
+
if (!client)
|
|
28
|
+
return undefined;
|
|
29
|
+
client.start();
|
|
30
|
+
return () => {
|
|
31
|
+
client.stop();
|
|
32
|
+
void client.flush();
|
|
33
|
+
};
|
|
34
|
+
}, [client]);
|
|
35
|
+
const value = (0, react_1.useMemo)(() => ({ client, collectUserTraits }), [client, collectUserTraits]);
|
|
36
|
+
return (0, jsx_runtime_1.jsx)(exports.ACRContext.Provider, { value: value, children: children });
|
|
37
|
+
}
|
|
38
|
+
function useACRContext() {
|
|
39
|
+
const value = (0, react_1.useContext)(exports.ACRContext);
|
|
40
|
+
if (!value)
|
|
41
|
+
throw new Error('[acr] ACR hooks and components must be used inside <ACRProvider>');
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.tsx"],"names":[],"mappings":";;;AAkCA,kCAqBC;AAED,sCAIC;;AA7DD,iCAAgG;AAChG,8CAAgG;AAChG,+CAAyC;AAW5B,QAAA,UAAU,GAAG,IAAA,qBAAa,EAAyB,IAAI,CAAC,CAAC;AActE,MAAM,SAAS,GAAG,GAAY,EAAE,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC;AAE/D;;;;GAIG;AACH,SAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,iBAAiB,GAAG,KAAK,EAAE,QAAQ,EAAoB;IAC3F,MAAM,CAAC,MAAM,CAAC,GAAG,IAAA,gBAAQ,EAAoB,GAAG,EAAE;QAChD,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACjB,0EAA0E;YAC1E,IAAA,kCAAwB,EAAC,MAAM,CAAC,CAAC;YACjC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,oBAAU,CAAC,MAAM,EAAE,sBAAQ,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,IAAI,EAAE,CAAC;YACd,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,MAAM,KAAK,GAAG,IAAA,eAAO,EAAkB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC3G,OAAO,uBAAC,kBAAU,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAuB,CAAC;AAC7E,CAAC;AAED,SAAgB,aAAa;IAC3B,MAAM,KAAK,GAAG,IAAA,kBAAU,EAAC,kBAAU,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IAChG,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useACRControlState = useACRControlState;
|
|
4
|
+
exports.useFlag = useFlag;
|
|
5
|
+
exports.useACRControls = useACRControls;
|
|
6
|
+
exports.ACRMaintenanceGate = ACRMaintenanceGate;
|
|
7
|
+
exports.ACRAnnouncement = ACRAnnouncement;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
const react_1 = require("react");
|
|
10
|
+
const browser_1 = require("@lib-acr/browser");
|
|
11
|
+
const context_js_1 = require("./context.js");
|
|
12
|
+
const noopUnsubscribe = () => undefined;
|
|
13
|
+
const getServerState = () => browser_1.EMPTY_CONTROL_STATE;
|
|
14
|
+
/**
|
|
15
|
+
* The full control state, re-rendering whenever it changes. During server-side rendering (no client) it is the empty
|
|
16
|
+
* state: nothing active, every flag falls back.
|
|
17
|
+
*/
|
|
18
|
+
function useACRControlState() {
|
|
19
|
+
const { client } = (0, context_js_1.useACRContext)();
|
|
20
|
+
const subscribe = (0, react_1.useCallback)((onChange) => (client ? client.onControlChange(() => onChange()) : noopUnsubscribe), [client]);
|
|
21
|
+
const getSnapshot = (0, react_1.useCallback)(() => (client ? client.getControlState() : browser_1.EMPTY_CONTROL_STATE), [client]);
|
|
22
|
+
return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getServerState);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Value of a feature flag for the current visitor, updated when the snapshot or the identified user changes. Returns
|
|
26
|
+
* `fallback` before the first snapshot, during server-side rendering, and when the flag is missing, disabled or
|
|
27
|
+
* served with another type.
|
|
28
|
+
*
|
|
29
|
+
* @example const newCheckout = useFlag('new-checkout', false);
|
|
30
|
+
*/
|
|
31
|
+
function useFlag(key, fallback) {
|
|
32
|
+
const { client } = (0, context_js_1.useACRContext)();
|
|
33
|
+
const state = useACRControlState();
|
|
34
|
+
// `state` is the dependency that changes on every relevant update; flag() reads the same snapshot.
|
|
35
|
+
return (0, react_1.useMemo)(() => (client && state.ready ? client.flag(key, fallback) : fallback), [client, state, key, fallback]);
|
|
36
|
+
}
|
|
37
|
+
/** Remote-control state for building your own maintenance page, banner or update prompt. */
|
|
38
|
+
function useACRControls() {
|
|
39
|
+
const { client } = (0, context_js_1.useACRContext)();
|
|
40
|
+
const state = useACRControlState();
|
|
41
|
+
return (0, react_1.useMemo)(() => ({
|
|
42
|
+
maintenance: state.maintenance,
|
|
43
|
+
announcement: state.announcement,
|
|
44
|
+
release: state.release,
|
|
45
|
+
flags: state.flags,
|
|
46
|
+
revision: state.revision,
|
|
47
|
+
ready: state.ready,
|
|
48
|
+
reloadRequested: state.reloadRequested,
|
|
49
|
+
dismissAnnouncement: (id) => client?.dismissAnnouncement(id),
|
|
50
|
+
refresh: () => client?.refreshControl() ?? Promise.resolve(),
|
|
51
|
+
}), [client, state]);
|
|
52
|
+
}
|
|
53
|
+
/** Renders its children unless maintenance is on for this visitor. Users in `allowUserIds` always see the children. */
|
|
54
|
+
function ACRMaintenanceGate({ fallback = null, children }) {
|
|
55
|
+
const { maintenance } = useACRControlState();
|
|
56
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: maintenance.active ? fallback : children });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Renders the current announcement until the visitor dismisses it. Turn off the built-in banner with
|
|
60
|
+
* `config.ui = { announcement: false }` when you use this component, or both will show.
|
|
61
|
+
*/
|
|
62
|
+
function ACRAnnouncement({ render }) {
|
|
63
|
+
const { announcement, dismissAnnouncement } = useACRControls();
|
|
64
|
+
if (!announcement || announcement.dismissed)
|
|
65
|
+
return null;
|
|
66
|
+
const dismiss = () => dismissAnnouncement(announcement.id);
|
|
67
|
+
if (render)
|
|
68
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: render({ announcement, dismiss }) });
|
|
69
|
+
return ((0, jsx_runtime_1.jsxs)("div", { role: announcement.severity === 'info' ? 'status' : 'alert', "data-acr-severity": announcement.severity, children: [(0, jsx_runtime_1.jsx)("span", { children: announcement.message }), announcement.linkUrl ? ((0, jsx_runtime_1.jsx)("a", { href: announcement.linkUrl, target: "_blank", rel: "noopener noreferrer", children: "Learn more" })) : null, announcement.dismissible ? ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Dismiss announcement", onClick: dismiss, children: '×' })) : null] }));
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control.js","sourceRoot":"","sources":["../../src/control.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACRErrorBoundary = ACRErrorBoundary;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const context_js_1 = require("./context.js");
|
|
7
|
+
class ACRBoundary extends react_1.Component {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.state = { error: null };
|
|
11
|
+
this.reset = () => {
|
|
12
|
+
this.setState({ error: null });
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
static getDerivedStateFromError(error) {
|
|
16
|
+
return { error };
|
|
17
|
+
}
|
|
18
|
+
componentDidCatch(error, info) {
|
|
19
|
+
this.props.client?.captureError(error, {
|
|
20
|
+
handled: true,
|
|
21
|
+
source: 'frontend',
|
|
22
|
+
attributes: {
|
|
23
|
+
kind: 'render',
|
|
24
|
+
componentStack: info.componentStack ? info.componentStack.slice(0, 2000) : undefined,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
this.props.onError?.(error, info);
|
|
28
|
+
}
|
|
29
|
+
render() {
|
|
30
|
+
const { error } = this.state;
|
|
31
|
+
if (error) {
|
|
32
|
+
const { fallback } = this.props;
|
|
33
|
+
return typeof fallback === 'function' ? fallback(error, this.reset) : (fallback ?? null);
|
|
34
|
+
}
|
|
35
|
+
return this.props.children;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Reports render errors from its subtree to ACR (handled: true, because the boundary handles them) and shows a fallback.
|
|
40
|
+
* It must sit below ACRProvider.
|
|
41
|
+
*/
|
|
42
|
+
function ACRErrorBoundary(props) {
|
|
43
|
+
const { client } = (0, context_js_1.useACRContext)();
|
|
44
|
+
return (0, jsx_runtime_1.jsx)(ACRBoundary, { client: client, ...props });
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=error-boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-boundary.js","sourceRoot":"","sources":["../../src/error-boundary.tsx"],"names":[],"mappings":";;AAyDA,4CAGC;;AA5DD,iCAAkE;AAElE,6CAA6C;AAkB7C,MAAM,WAAY,SAAQ,iBAAiC;IAA3D;;QACW,UAAK,GAAe,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAkBrC,UAAK,GAAG,GAAS,EAAE;YACzB,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC;IAUJ,CAAC;IA5BC,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAEQ,iBAAiB,CAAC,KAAY,EAAE,IAAe;QACtD,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE;YACrC,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,UAAU;YAClB,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;aACrF;SACF,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAMQ,MAAM;QACb,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7B,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAChC,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,KAA4B;IAC3D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,0BAAa,GAAE,CAAC;IACnC,OAAO,uBAAC,WAAW,IAAC,MAAM,EAAE,MAAM,KAAM,KAAK,GAAI,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useACR = useACR;
|
|
4
|
+
exports.useACREvent = useACREvent;
|
|
5
|
+
exports.useACRUser = useACRUser;
|
|
6
|
+
exports.useACRMetrics = useACRMetrics;
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const context_js_1 = require("./context.js");
|
|
9
|
+
const EMPTY_VITALS = Object.freeze({ fcp: null, lcp: null, cls: null, ttfb: null, inpMaxMs: null, pageLoadMs: null });
|
|
10
|
+
const noopUnsubscribe = () => undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Everything the app needs from the client, bound once per provider. Every function is a safe no-op while there is no
|
|
13
|
+
* client (server-side rendering).
|
|
14
|
+
*/
|
|
15
|
+
function useACR() {
|
|
16
|
+
const { client } = (0, context_js_1.useACRContext)();
|
|
17
|
+
return (0, react_1.useMemo)(() => ({
|
|
18
|
+
/** The browser client, or null during server-side rendering. */
|
|
19
|
+
client,
|
|
20
|
+
trackEvent: (name, attributes) => client?.trackEvent(name, attributes),
|
|
21
|
+
log: (level, message, attributes) => client?.log(level, message, attributes),
|
|
22
|
+
captureError: (error, options) => client?.captureError(error, options),
|
|
23
|
+
identify: (userId) => client?.identify(userId),
|
|
24
|
+
trackPageView: (path) => client?.trackPageView(path),
|
|
25
|
+
/** Forgets the visitor (for example on logout): new anonymous id, new session, no user id. */
|
|
26
|
+
reset: () => client?.reset(),
|
|
27
|
+
flush: () => client?.flush() ?? Promise.resolve(),
|
|
28
|
+
}), [client]);
|
|
29
|
+
}
|
|
30
|
+
/** Returns a stable function that records a custom event: `const track = useACREvent(); track('cart_added', {sku})`. */
|
|
31
|
+
function useACREvent() {
|
|
32
|
+
const { client } = (0, context_js_1.useACRContext)();
|
|
33
|
+
return (0, react_1.useCallback)((name, attributes) => client?.trackEvent(name, attributes), [client]);
|
|
34
|
+
}
|
|
35
|
+
function useACRUser() {
|
|
36
|
+
const { client, collectUserTraits } = (0, context_js_1.useACRContext)();
|
|
37
|
+
return (0, react_1.useMemo)(() => ({
|
|
38
|
+
identify: (userId, traits) => {
|
|
39
|
+
if (!client)
|
|
40
|
+
return;
|
|
41
|
+
client.identify(userId);
|
|
42
|
+
if (collectUserTraits && traits)
|
|
43
|
+
client.trackEvent('user_identified', traits);
|
|
44
|
+
},
|
|
45
|
+
clear: () => client?.identify(null),
|
|
46
|
+
reset: () => client?.reset(),
|
|
47
|
+
}), [client, collectUserTraits]);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Live Core Web Vitals for this page (LCP, FCP, CLS, TTFB, approximate INP and page load). Values are null until the
|
|
51
|
+
* browser reports them. The same values are sent to ACR when the page is hidden.
|
|
52
|
+
*/
|
|
53
|
+
function useACRMetrics() {
|
|
54
|
+
const { client } = (0, context_js_1.useACRContext)();
|
|
55
|
+
const subscribe = (0, react_1.useCallback)((onChange) => (client ? client.onVitals(() => onChange()) : noopUnsubscribe), [client]);
|
|
56
|
+
const getSnapshot = (0, react_1.useCallback)(() => (client ? client.getVitals() : EMPTY_VITALS), [client]);
|
|
57
|
+
const getServerSnapshot = (0, react_1.useCallback)(() => EMPTY_VITALS, []);
|
|
58
|
+
return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
exports.SDK_INFO = exports.ACRErrorBoundary = exports.ACRAnnouncement = exports.ACRMaintenanceGate = exports.useACRControlState = exports.useACRControls = exports.useFlag = exports.useACRMetrics = exports.useACRUser = exports.useACREvent = exports.useACR = exports.useACRContext = exports.ACRContext = exports.ACRProvider = void 0;
|
|
18
|
+
var context_js_1 = require("./context.js");
|
|
19
|
+
Object.defineProperty(exports, "ACRProvider", { enumerable: true, get: function () { return context_js_1.ACRProvider; } });
|
|
20
|
+
Object.defineProperty(exports, "ACRContext", { enumerable: true, get: function () { return context_js_1.ACRContext; } });
|
|
21
|
+
Object.defineProperty(exports, "useACRContext", { enumerable: true, get: function () { return context_js_1.useACRContext; } });
|
|
22
|
+
var hooks_js_1 = require("./hooks.js");
|
|
23
|
+
Object.defineProperty(exports, "useACR", { enumerable: true, get: function () { return hooks_js_1.useACR; } });
|
|
24
|
+
Object.defineProperty(exports, "useACREvent", { enumerable: true, get: function () { return hooks_js_1.useACREvent; } });
|
|
25
|
+
Object.defineProperty(exports, "useACRUser", { enumerable: true, get: function () { return hooks_js_1.useACRUser; } });
|
|
26
|
+
Object.defineProperty(exports, "useACRMetrics", { enumerable: true, get: function () { return hooks_js_1.useACRMetrics; } });
|
|
27
|
+
var control_js_1 = require("./control.js");
|
|
28
|
+
Object.defineProperty(exports, "useFlag", { enumerable: true, get: function () { return control_js_1.useFlag; } });
|
|
29
|
+
Object.defineProperty(exports, "useACRControls", { enumerable: true, get: function () { return control_js_1.useACRControls; } });
|
|
30
|
+
Object.defineProperty(exports, "useACRControlState", { enumerable: true, get: function () { return control_js_1.useACRControlState; } });
|
|
31
|
+
Object.defineProperty(exports, "ACRMaintenanceGate", { enumerable: true, get: function () { return control_js_1.ACRMaintenanceGate; } });
|
|
32
|
+
Object.defineProperty(exports, "ACRAnnouncement", { enumerable: true, get: function () { return control_js_1.ACRAnnouncement; } });
|
|
33
|
+
var error_boundary_js_1 = require("./error-boundary.js");
|
|
34
|
+
Object.defineProperty(exports, "ACRErrorBoundary", { enumerable: true, get: function () { return error_boundary_js_1.ACRErrorBoundary; } });
|
|
35
|
+
__exportStar(require("@lib-acr/browser"), exports);
|
|
36
|
+
var sdk_info_js_1 = require("./sdk-info.js");
|
|
37
|
+
Object.defineProperty(exports, "SDK_INFO", { enumerable: true, get: function () { return sdk_info_js_1.SDK_INFO; } });
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2CAAsE;AAA7D,yGAAA,WAAW,OAAA;AAAE,wGAAA,UAAU,OAAA;AAAE,2GAAA,aAAa,OAAA;AAE/C,uCAA4E;AAAnE,kGAAA,MAAM,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,sGAAA,UAAU,OAAA;AAAE,yGAAA,aAAa,OAAA;AAEvD,2CAAgH;AAAvG,qGAAA,OAAO,OAAA;AAAE,4GAAA,cAAc,OAAA;AAAE,gHAAA,kBAAkB,OAAA;AAAE,gHAAA,kBAAkB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAEzF,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AAEzB,mDAAiC;AACjC,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useACRTracking = useACRTracking;
|
|
4
|
+
exports.ACRRouteTracker = ACRRouteTracker;
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const react_router_dom_1 = require("react-router-dom");
|
|
7
|
+
const context_js_1 = require("./context.js");
|
|
8
|
+
/**
|
|
9
|
+
* Records a page view on every route change. It needs React Router v6.4 or later, and it is exported from
|
|
10
|
+
* `@lib-acr/react/router` so apps without a router never load react-router-dom.
|
|
11
|
+
*/
|
|
12
|
+
function useACRTracking() {
|
|
13
|
+
const { client } = (0, context_js_1.useACRContext)();
|
|
14
|
+
const location = (0, react_router_dom_1.useLocation)();
|
|
15
|
+
(0, react_1.useEffect)(() => {
|
|
16
|
+
client?.trackPageView(location.pathname);
|
|
17
|
+
}, [client, location.pathname]);
|
|
18
|
+
}
|
|
19
|
+
/** Renders nothing; add it inside the router for route tracking without a hook call in your layout. */
|
|
20
|
+
function ACRRouteTracker() {
|
|
21
|
+
useACRTracking();
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router.ts"],"names":[],"mappings":";;AAQA,wCAMC;AAGD,0CAGC;AApBD,iCAAkC;AAClC,uDAA+C;AAC/C,6CAA6C;AAE7C;;;GAGG;AACH,SAAgB,cAAc;IAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,0BAAa,GAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAA,8BAAW,GAAE,CAAC;IAC/B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,uGAAuG;AACvG,SAAgB,eAAe;IAC7B,cAAc,EAAE,CAAC;IACjB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SDK_INFO = void 0;
|
|
4
|
+
// Generated by scripts/sync-sdk-info.mjs from package.json. Do not edit by hand.
|
|
5
|
+
exports.SDK_INFO = { name: '@lib-acr/react', version: '0.0.2' };
|
|
6
|
+
//# sourceMappingURL=sdk-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-info.js","sourceRoot":"","sources":["../../src/sdk-info.ts"],"names":[],"mappings":";;;AAAA,iFAAiF;AACpE,QAAA,QAAQ,GAAG,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAW,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { BrowserACR, assertPublishableOptions } from '@lib-acr/browser';
|
|
4
|
+
import { SDK_INFO } from './sdk-info.js';
|
|
5
|
+
export const ACRContext = createContext(null);
|
|
6
|
+
const isBrowser = () => typeof window !== 'undefined';
|
|
7
|
+
/**
|
|
8
|
+
* Creates one BrowserACR for the app, starts automatic collection on mount and flushes it on unmount. The client is
|
|
9
|
+
* created once, so later changes to `config` are ignored. During server-side rendering no client is created (it would
|
|
10
|
+
* start timers on the server); the hooks are no-ops until the provider renders in the browser.
|
|
11
|
+
*/
|
|
12
|
+
export function ACRProvider({ config, collectUserTraits = false, children }) {
|
|
13
|
+
const [client] = useState(() => {
|
|
14
|
+
if (!isBrowser()) {
|
|
15
|
+
// Still fail fast on a secret key, even though no client is created here.
|
|
16
|
+
assertPublishableOptions(config);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return new BrowserACR(config, SDK_INFO);
|
|
20
|
+
});
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!client)
|
|
23
|
+
return undefined;
|
|
24
|
+
client.start();
|
|
25
|
+
return () => {
|
|
26
|
+
client.stop();
|
|
27
|
+
void client.flush();
|
|
28
|
+
};
|
|
29
|
+
}, [client]);
|
|
30
|
+
const value = useMemo(() => ({ client, collectUserTraits }), [client, collectUserTraits]);
|
|
31
|
+
return _jsx(ACRContext.Provider, { value: value, children: children });
|
|
32
|
+
}
|
|
33
|
+
export function useACRContext() {
|
|
34
|
+
const value = useContext(ACRContext);
|
|
35
|
+
if (!value)
|
|
36
|
+
throw new Error('[acr] ACR hooks and components must be used inside <ACRProvider>');
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,wBAAwB,EAA0B,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAWzC,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAyB,IAAI,CAAC,CAAC;AActE,MAAM,SAAS,GAAG,GAAY,EAAE,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC;AAE/D;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,MAAM,EAAE,iBAAiB,GAAG,KAAK,EAAE,QAAQ,EAAoB;IAC3F,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAoB,GAAG,EAAE;QAChD,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACjB,0EAA0E;YAC1E,wBAAwB,CAAC,MAAM,CAAC,CAAC;YACjC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,IAAI,EAAE,CAAC;YACd,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,MAAM,KAAK,GAAG,OAAO,CAAkB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC3G,OAAO,KAAC,UAAU,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAuB,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IAChG,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo, useSyncExternalStore } from 'react';
|
|
3
|
+
import { EMPTY_CONTROL_STATE } from '@lib-acr/browser';
|
|
4
|
+
import { useACRContext } from './context.js';
|
|
5
|
+
const noopUnsubscribe = () => undefined;
|
|
6
|
+
const getServerState = () => EMPTY_CONTROL_STATE;
|
|
7
|
+
/**
|
|
8
|
+
* The full control state, re-rendering whenever it changes. During server-side rendering (no client) it is the empty
|
|
9
|
+
* state: nothing active, every flag falls back.
|
|
10
|
+
*/
|
|
11
|
+
export function useACRControlState() {
|
|
12
|
+
const { client } = useACRContext();
|
|
13
|
+
const subscribe = useCallback((onChange) => (client ? client.onControlChange(() => onChange()) : noopUnsubscribe), [client]);
|
|
14
|
+
const getSnapshot = useCallback(() => (client ? client.getControlState() : EMPTY_CONTROL_STATE), [client]);
|
|
15
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerState);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Value of a feature flag for the current visitor, updated when the snapshot or the identified user changes. Returns
|
|
19
|
+
* `fallback` before the first snapshot, during server-side rendering, and when the flag is missing, disabled or
|
|
20
|
+
* served with another type.
|
|
21
|
+
*
|
|
22
|
+
* @example const newCheckout = useFlag('new-checkout', false);
|
|
23
|
+
*/
|
|
24
|
+
export function useFlag(key, fallback) {
|
|
25
|
+
const { client } = useACRContext();
|
|
26
|
+
const state = useACRControlState();
|
|
27
|
+
// `state` is the dependency that changes on every relevant update; flag() reads the same snapshot.
|
|
28
|
+
return useMemo(() => (client && state.ready ? client.flag(key, fallback) : fallback), [client, state, key, fallback]);
|
|
29
|
+
}
|
|
30
|
+
/** Remote-control state for building your own maintenance page, banner or update prompt. */
|
|
31
|
+
export function useACRControls() {
|
|
32
|
+
const { client } = useACRContext();
|
|
33
|
+
const state = useACRControlState();
|
|
34
|
+
return useMemo(() => ({
|
|
35
|
+
maintenance: state.maintenance,
|
|
36
|
+
announcement: state.announcement,
|
|
37
|
+
release: state.release,
|
|
38
|
+
flags: state.flags,
|
|
39
|
+
revision: state.revision,
|
|
40
|
+
ready: state.ready,
|
|
41
|
+
reloadRequested: state.reloadRequested,
|
|
42
|
+
dismissAnnouncement: (id) => client?.dismissAnnouncement(id),
|
|
43
|
+
refresh: () => client?.refreshControl() ?? Promise.resolve(),
|
|
44
|
+
}), [client, state]);
|
|
45
|
+
}
|
|
46
|
+
/** Renders its children unless maintenance is on for this visitor. Users in `allowUserIds` always see the children. */
|
|
47
|
+
export function ACRMaintenanceGate({ fallback = null, children }) {
|
|
48
|
+
const { maintenance } = useACRControlState();
|
|
49
|
+
return _jsx(_Fragment, { children: maintenance.active ? fallback : children });
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Renders the current announcement until the visitor dismisses it. Turn off the built-in banner with
|
|
53
|
+
* `config.ui = { announcement: false }` when you use this component, or both will show.
|
|
54
|
+
*/
|
|
55
|
+
export function ACRAnnouncement({ render }) {
|
|
56
|
+
const { announcement, dismissAnnouncement } = useACRControls();
|
|
57
|
+
if (!announcement || announcement.dismissed)
|
|
58
|
+
return null;
|
|
59
|
+
const dismiss = () => dismissAnnouncement(announcement.id);
|
|
60
|
+
if (render)
|
|
61
|
+
return _jsx(_Fragment, { children: render({ announcement, dismiss }) });
|
|
62
|
+
return (_jsxs("div", { role: announcement.severity === 'info' ? 'status' : 'alert', "data-acr-severity": announcement.severity, children: [_jsx("span", { children: announcement.message }), announcement.linkUrl ? (_jsx("a", { href: announcement.linkUrl, target: "_blank", rel: "noopener noreferrer", children: "Learn more" })) : null, announcement.dismissible ? (_jsx("button", { type: "button", "aria-label": "Dismiss announcement", onClick: dismiss, children: '×' })) : null] }));
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control.js","sourceRoot":"","sources":["../../src/control.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Component } from 'react';
|
|
3
|
+
import { useACRContext } from './context.js';
|
|
4
|
+
class ACRBoundary extends Component {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.state = { error: null };
|
|
8
|
+
this.reset = () => {
|
|
9
|
+
this.setState({ error: null });
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
static getDerivedStateFromError(error) {
|
|
13
|
+
return { error };
|
|
14
|
+
}
|
|
15
|
+
componentDidCatch(error, info) {
|
|
16
|
+
this.props.client?.captureError(error, {
|
|
17
|
+
handled: true,
|
|
18
|
+
source: 'frontend',
|
|
19
|
+
attributes: {
|
|
20
|
+
kind: 'render',
|
|
21
|
+
componentStack: info.componentStack ? info.componentStack.slice(0, 2000) : undefined,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
this.props.onError?.(error, info);
|
|
25
|
+
}
|
|
26
|
+
render() {
|
|
27
|
+
const { error } = this.state;
|
|
28
|
+
if (error) {
|
|
29
|
+
const { fallback } = this.props;
|
|
30
|
+
return typeof fallback === 'function' ? fallback(error, this.reset) : (fallback ?? null);
|
|
31
|
+
}
|
|
32
|
+
return this.props.children;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Reports render errors from its subtree to ACR (handled: true, because the boundary handles them) and shows a fallback.
|
|
37
|
+
* It must sit below ACRProvider.
|
|
38
|
+
*/
|
|
39
|
+
export function ACRErrorBoundary(props) {
|
|
40
|
+
const { client } = useACRContext();
|
|
41
|
+
return _jsx(ACRBoundary, { client: client, ...props });
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=error-boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-boundary.js","sourceRoot":"","sources":["../../src/error-boundary.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAkB7C,MAAM,WAAY,SAAQ,SAAiC;IAA3D;;QACW,UAAK,GAAe,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAkBrC,UAAK,GAAG,GAAS,EAAE;YACzB,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC;IAUJ,CAAC;IA5BC,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAEQ,iBAAiB,CAAC,KAAY,EAAE,IAAe;QACtD,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE;YACrC,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,UAAU;YAClB,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;aACrF;SACF,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAMQ,MAAM;QACb,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7B,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAChC,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IAC3D,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;IACnC,OAAO,KAAC,WAAW,IAAC,MAAM,EAAE,MAAM,KAAM,KAAK,GAAI,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useCallback, useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
import { useACRContext } from './context.js';
|
|
3
|
+
const EMPTY_VITALS = Object.freeze({ fcp: null, lcp: null, cls: null, ttfb: null, inpMaxMs: null, pageLoadMs: null });
|
|
4
|
+
const noopUnsubscribe = () => undefined;
|
|
5
|
+
/**
|
|
6
|
+
* Everything the app needs from the client, bound once per provider. Every function is a safe no-op while there is no
|
|
7
|
+
* client (server-side rendering).
|
|
8
|
+
*/
|
|
9
|
+
export function useACR() {
|
|
10
|
+
const { client } = useACRContext();
|
|
11
|
+
return useMemo(() => ({
|
|
12
|
+
/** The browser client, or null during server-side rendering. */
|
|
13
|
+
client,
|
|
14
|
+
trackEvent: (name, attributes) => client?.trackEvent(name, attributes),
|
|
15
|
+
log: (level, message, attributes) => client?.log(level, message, attributes),
|
|
16
|
+
captureError: (error, options) => client?.captureError(error, options),
|
|
17
|
+
identify: (userId) => client?.identify(userId),
|
|
18
|
+
trackPageView: (path) => client?.trackPageView(path),
|
|
19
|
+
/** Forgets the visitor (for example on logout): new anonymous id, new session, no user id. */
|
|
20
|
+
reset: () => client?.reset(),
|
|
21
|
+
flush: () => client?.flush() ?? Promise.resolve(),
|
|
22
|
+
}), [client]);
|
|
23
|
+
}
|
|
24
|
+
/** Returns a stable function that records a custom event: `const track = useACREvent(); track('cart_added', {sku})`. */
|
|
25
|
+
export function useACREvent() {
|
|
26
|
+
const { client } = useACRContext();
|
|
27
|
+
return useCallback((name, attributes) => client?.trackEvent(name, attributes), [client]);
|
|
28
|
+
}
|
|
29
|
+
export function useACRUser() {
|
|
30
|
+
const { client, collectUserTraits } = useACRContext();
|
|
31
|
+
return useMemo(() => ({
|
|
32
|
+
identify: (userId, traits) => {
|
|
33
|
+
if (!client)
|
|
34
|
+
return;
|
|
35
|
+
client.identify(userId);
|
|
36
|
+
if (collectUserTraits && traits)
|
|
37
|
+
client.trackEvent('user_identified', traits);
|
|
38
|
+
},
|
|
39
|
+
clear: () => client?.identify(null),
|
|
40
|
+
reset: () => client?.reset(),
|
|
41
|
+
}), [client, collectUserTraits]);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Live Core Web Vitals for this page (LCP, FCP, CLS, TTFB, approximate INP and page load). Values are null until the
|
|
45
|
+
* browser reports them. The same values are sent to ACR when the page is hidden.
|
|
46
|
+
*/
|
|
47
|
+
export function useACRMetrics() {
|
|
48
|
+
const { client } = useACRContext();
|
|
49
|
+
const subscribe = useCallback((onChange) => (client ? client.onVitals(() => onChange()) : noopUnsubscribe), [client]);
|
|
50
|
+
const getSnapshot = useCallback(() => (client ? client.getVitals() : EMPTY_VITALS), [client]);
|
|
51
|
+
const getServerSnapshot = useCallback(() => EMPTY_VITALS, []);
|
|
52
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ACRProvider, ACRContext, useACRContext } from './context.js';
|
|
2
|
+
export { useACR, useACREvent, useACRUser, useACRMetrics } from './hooks.js';
|
|
3
|
+
export { useFlag, useACRControls, useACRControlState, ACRMaintenanceGate, ACRAnnouncement } from './control.js';
|
|
4
|
+
export { ACRErrorBoundary } from './error-boundary.js';
|
|
5
|
+
export * from '@lib-acr/browser';
|
|
6
|
+
export { SDK_INFO } from './sdk-info.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEhH,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useLocation } from 'react-router-dom';
|
|
3
|
+
import { useACRContext } from './context.js';
|
|
4
|
+
/**
|
|
5
|
+
* Records a page view on every route change. It needs React Router v6.4 or later, and it is exported from
|
|
6
|
+
* `@lib-acr/react/router` so apps without a router never load react-router-dom.
|
|
7
|
+
*/
|
|
8
|
+
export function useACRTracking() {
|
|
9
|
+
const { client } = useACRContext();
|
|
10
|
+
const location = useLocation();
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
client?.trackPageView(location.pathname);
|
|
13
|
+
}, [client, location.pathname]);
|
|
14
|
+
}
|
|
15
|
+
/** Renders nothing; add it inside the router for route tracking without a hook call in your layout. */
|
|
16
|
+
export function ACRRouteTracker() {
|
|
17
|
+
useACRTracking();
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,eAAe;IAC7B,cAAc,EAAE,CAAC;IACjB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-info.js","sourceRoot":"","sources":["../../src/sdk-info.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAW,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { BrowserACR, type BrowserAcrOptions } from '@lib-acr/browser';
|
|
3
|
+
export interface ACRContextValue {
|
|
4
|
+
/**
|
|
5
|
+
* The browser client. It is null during server-side rendering, where no client is created; hooks then do nothing.
|
|
6
|
+
*/
|
|
7
|
+
client: BrowserACR | null;
|
|
8
|
+
/** Whether identify(userId, traits) may send traits. Off by default because traits are often personal data. */
|
|
9
|
+
collectUserTraits: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const ACRContext: import("react").Context<ACRContextValue | null>;
|
|
12
|
+
export interface ACRProviderProps {
|
|
13
|
+
/**
|
|
14
|
+
* Browser options. Use the publishable key only; a secret key is rejected. `config.ui` controls the built-in
|
|
15
|
+
* maintenance screen, announcement banner and update prompt (set parts to false to render your own with
|
|
16
|
+
* useACRControls(), ACRMaintenanceGate or ACRAnnouncement).
|
|
17
|
+
*/
|
|
18
|
+
config: BrowserAcrOptions;
|
|
19
|
+
/** Allow identify(userId, traits) to send traits. Default false. */
|
|
20
|
+
collectUserTraits?: boolean;
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates one BrowserACR for the app, starts automatic collection on mount and flushes it on unmount. The client is
|
|
25
|
+
* created once, so later changes to `config` are ignored. During server-side rendering no client is created (it would
|
|
26
|
+
* start timers on the server); the hooks are no-ops until the provider renders in the browser.
|
|
27
|
+
*/
|
|
28
|
+
export declare function ACRProvider({ config, collectUserTraits, children }: ACRProviderProps): import("react").JSX.Element;
|
|
29
|
+
export declare function useACRContext(): ACRContextValue;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type AnnouncementState, type ControlState, type MaintenanceState, type ReleaseState } from '@lib-acr/browser';
|
|
3
|
+
/**
|
|
4
|
+
* The full control state, re-rendering whenever it changes. During server-side rendering (no client) it is the empty
|
|
5
|
+
* state: nothing active, every flag falls back.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useACRControlState(): ControlState;
|
|
8
|
+
/**
|
|
9
|
+
* Value of a feature flag for the current visitor, updated when the snapshot or the identified user changes. Returns
|
|
10
|
+
* `fallback` before the first snapshot, during server-side rendering, and when the flag is missing, disabled or
|
|
11
|
+
* served with another type.
|
|
12
|
+
*
|
|
13
|
+
* @example const newCheckout = useFlag('new-checkout', false);
|
|
14
|
+
*/
|
|
15
|
+
export declare function useFlag<T>(key: string, fallback: T): T;
|
|
16
|
+
export interface ACRControls {
|
|
17
|
+
/** Maintenance state for the current visitor (`active` is false for users in `allowUserIds`). */
|
|
18
|
+
maintenance: MaintenanceState;
|
|
19
|
+
/** Announcement inside its time window, or null. Check `dismissed` before showing it. */
|
|
20
|
+
announcement: AnnouncementState | null;
|
|
21
|
+
release: ReleaseState | null;
|
|
22
|
+
/** Every flag evaluated for the current visitor. */
|
|
23
|
+
flags: Readonly<Record<string, unknown>>;
|
|
24
|
+
/** Snapshot revision, null before the first snapshot. */
|
|
25
|
+
revision: number | null;
|
|
26
|
+
/** A snapshot has been received. */
|
|
27
|
+
ready: boolean;
|
|
28
|
+
/** A `reload` command asked the app to offer a reload. */
|
|
29
|
+
reloadRequested: boolean;
|
|
30
|
+
/** Hides the announcement for this visitor (remembered per id). */
|
|
31
|
+
dismissAnnouncement: (id: string) => void;
|
|
32
|
+
/** Fetches the control snapshot now. */
|
|
33
|
+
refresh: () => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/** Remote-control state for building your own maintenance page, banner or update prompt. */
|
|
36
|
+
export declare function useACRControls(): ACRControls;
|
|
37
|
+
export interface ACRMaintenanceGateProps {
|
|
38
|
+
/**
|
|
39
|
+
* Rendered instead of the children while maintenance applies to this visitor. Default: nothing, so the built-in
|
|
40
|
+
* maintenance screen (or your own UI elsewhere) is what the visitor sees.
|
|
41
|
+
*/
|
|
42
|
+
fallback?: ReactNode;
|
|
43
|
+
children?: ReactNode;
|
|
44
|
+
}
|
|
45
|
+
/** Renders its children unless maintenance is on for this visitor. Users in `allowUserIds` always see the children. */
|
|
46
|
+
export declare function ACRMaintenanceGate({ fallback, children }: ACRMaintenanceGateProps): import("react").JSX.Element;
|
|
47
|
+
export interface ACRAnnouncementRenderProps {
|
|
48
|
+
announcement: AnnouncementState;
|
|
49
|
+
dismiss: () => void;
|
|
50
|
+
}
|
|
51
|
+
export interface ACRAnnouncementProps {
|
|
52
|
+
/** Custom rendering. Without it a minimal, unstyled banner is rendered (role=status or alert, with a dismiss button). */
|
|
53
|
+
render?: (props: ACRAnnouncementRenderProps) => ReactNode;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Renders the current announcement until the visitor dismisses it. Turn off the built-in banner with
|
|
57
|
+
* `config.ui = { announcement: false }` when you use this component, or both will show.
|
|
58
|
+
*/
|
|
59
|
+
export declare function ACRAnnouncement({ render }: ACRAnnouncementProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ErrorInfo, type ReactNode } from 'react';
|
|
2
|
+
export interface ACRErrorBoundaryProps {
|
|
3
|
+
/** Shown instead of the children after an error. A function receives the error and a reset callback. */
|
|
4
|
+
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
5
|
+
/** Called after the error is reported. */
|
|
6
|
+
onError?: (error: Error, info: ErrorInfo) => void;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Reports render errors from its subtree to ACR (handled: true, because the boundary handles them) and shows a fallback.
|
|
11
|
+
* It must sit below ACRProvider.
|
|
12
|
+
*/
|
|
13
|
+
export declare function ACRErrorBoundary(props: ACRErrorBoundaryProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Attributes, ErrorOptions, LogLevel, VitalsSnapshot } from '@lib-acr/browser';
|
|
2
|
+
/**
|
|
3
|
+
* Everything the app needs from the client, bound once per provider. Every function is a safe no-op while there is no
|
|
4
|
+
* client (server-side rendering).
|
|
5
|
+
*/
|
|
6
|
+
export declare function useACR(): {
|
|
7
|
+
/** The browser client, or null during server-side rendering. */
|
|
8
|
+
client: import("@lib-acr/browser").BrowserACR | null;
|
|
9
|
+
trackEvent: (name: string, attributes?: Attributes) => void | undefined;
|
|
10
|
+
log: (level: LogLevel, message: string, attributes?: Attributes) => void | undefined;
|
|
11
|
+
captureError: (error: unknown, options?: ErrorOptions) => void | undefined;
|
|
12
|
+
identify: (userId: string | null) => void | undefined;
|
|
13
|
+
trackPageView: (path?: string) => void | undefined;
|
|
14
|
+
/** Forgets the visitor (for example on logout): new anonymous id, new session, no user id. */
|
|
15
|
+
reset: () => void | undefined;
|
|
16
|
+
flush: () => Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
/** Returns a stable function that records a custom event: `const track = useACREvent(); track('cart_added', {sku})`. */
|
|
19
|
+
export declare function useACREvent(): (name: string, attributes?: Attributes) => void;
|
|
20
|
+
export interface ACRUser {
|
|
21
|
+
/**
|
|
22
|
+
* Attaches a user id to later telemetry. Traits are sent only when the provider sets `collectUserTraits`, and
|
|
23
|
+
* sensitive keys are redacted before they leave the page.
|
|
24
|
+
*/
|
|
25
|
+
identify: (userId: string, traits?: Attributes) => void;
|
|
26
|
+
/** Stops attaching the user id. */
|
|
27
|
+
clear: () => void;
|
|
28
|
+
/** Like clear(), and also replaces the anonymous id and starts a new session. Use it on logout. */
|
|
29
|
+
reset: () => void;
|
|
30
|
+
}
|
|
31
|
+
export declare function useACRUser(): ACRUser;
|
|
32
|
+
/**
|
|
33
|
+
* Live Core Web Vitals for this page (LCP, FCP, CLS, TTFB, approximate INP and page load). Values are null until the
|
|
34
|
+
* browser reports them. The same values are sent to ACR when the page is hidden.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useACRMetrics(): VitalsSnapshot;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { ACRProvider, ACRContext, useACRContext } from './context.js';
|
|
2
|
+
export type { ACRContextValue, ACRProviderProps } from './context.js';
|
|
3
|
+
export { useACR, useACREvent, useACRUser, useACRMetrics } from './hooks.js';
|
|
4
|
+
export type { ACRUser } from './hooks.js';
|
|
5
|
+
export { useFlag, useACRControls, useACRControlState, ACRMaintenanceGate, ACRAnnouncement } from './control.js';
|
|
6
|
+
export type { ACRControls, ACRMaintenanceGateProps, ACRAnnouncementProps, ACRAnnouncementRenderProps } from './control.js';
|
|
7
|
+
export { ACRErrorBoundary } from './error-boundary.js';
|
|
8
|
+
export type { ACRErrorBoundaryProps } from './error-boundary.js';
|
|
9
|
+
export * from '@lib-acr/browser';
|
|
10
|
+
export { SDK_INFO } from './sdk-info.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Records a page view on every route change. It needs React Router v6.4 or later, and it is exported from
|
|
3
|
+
* `@lib-acr/react/router` so apps without a router never load react-router-dom.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useACRTracking(): void;
|
|
6
|
+
/** Renders nothing; add it inside the router for route tracking without a hook call in your layout. */
|
|
7
|
+
export declare function ACRRouteTracker(): null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lib-acr/react",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "ACR SDK for React: ACRProvider, hooks for events, users and vitals, an error boundary, and React Router page tracking.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types/index.d.ts",
|
|
12
|
+
"import": "./dist/esm/index.js",
|
|
13
|
+
"require": "./dist/cjs/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./router": {
|
|
16
|
+
"types": "./dist/types/router.d.ts",
|
|
17
|
+
"import": "./dist/esm/router.js",
|
|
18
|
+
"require": "./dist/cjs/router.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.17"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@lib-acr/browser": "0.0.2",
|
|
32
|
+
"@lib-acr/core": "0.0.2"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": ">=18.0.0 <20",
|
|
36
|
+
"react-router-dom": ">=6.4.0 <8"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"react-router-dom": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@testing-library/dom": "^10.4.0",
|
|
45
|
+
"@testing-library/react": "^16.1.0",
|
|
46
|
+
"@types/react": "^18.3.12",
|
|
47
|
+
"@types/react-dom": "^18.3.1",
|
|
48
|
+
"react": "^18.3.1",
|
|
49
|
+
"react-dom": "^18.3.1",
|
|
50
|
+
"react-router-dom": "^6.28.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "node ../../scripts/build-package.mjs",
|
|
54
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
55
|
+
"test": "vitest run"
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/application-control-room/application.git",
|
|
60
|
+
"directory": "packages/react"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/application-control-room/application/tree/main/packages/react#readme",
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
}
|
|
66
|
+
}
|