@lib-acr/browser 0.0.0-stage → 0.0.1

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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -2
  3. package/dist/cjs/browser-acr.js +600 -0
  4. package/dist/cjs/browser-acr.js.map +1 -0
  5. package/dist/cjs/captured.js +19 -0
  6. package/dist/cjs/captured.js.map +1 -0
  7. package/dist/cjs/control/command-runner.js +289 -0
  8. package/dist/cjs/control/command-runner.js.map +1 -0
  9. package/dist/cjs/control/ui.js +277 -0
  10. package/dist/cjs/control/ui.js.map +1 -0
  11. package/dist/cjs/device.js +54 -0
  12. package/dist/cjs/device.js.map +1 -0
  13. package/dist/cjs/fetch-tracker.js +63 -0
  14. package/dist/cjs/fetch-tracker.js.map +1 -0
  15. package/dist/cjs/identity.js +120 -0
  16. package/dist/cjs/identity.js.map +1 -0
  17. package/dist/cjs/index.js +73 -0
  18. package/dist/cjs/index.js.map +1 -0
  19. package/dist/cjs/sdk-info.js +6 -0
  20. package/dist/cjs/sdk-info.js.map +1 -0
  21. package/dist/cjs/url.js +31 -0
  22. package/dist/cjs/url.js.map +1 -0
  23. package/dist/cjs/web-vitals.js +160 -0
  24. package/dist/cjs/web-vitals.js.map +1 -0
  25. package/dist/esm/browser-acr.js +595 -0
  26. package/dist/esm/browser-acr.js.map +1 -0
  27. package/dist/esm/captured.js +15 -0
  28. package/dist/esm/captured.js.map +1 -0
  29. package/dist/esm/control/command-runner.js +284 -0
  30. package/dist/esm/control/command-runner.js.map +1 -0
  31. package/dist/esm/control/ui.js +272 -0
  32. package/dist/esm/control/ui.js.map +1 -0
  33. package/dist/esm/device.js +50 -0
  34. package/dist/esm/device.js.map +1 -0
  35. package/dist/esm/fetch-tracker.js +60 -0
  36. package/dist/esm/fetch-tracker.js.map +1 -0
  37. package/dist/esm/identity.js +112 -0
  38. package/dist/esm/identity.js.map +1 -0
  39. package/dist/esm/index.js +13 -0
  40. package/dist/esm/index.js.map +1 -0
  41. package/dist/esm/package.json +3 -0
  42. package/dist/esm/sdk-info.js +3 -0
  43. package/dist/esm/sdk-info.js.map +1 -0
  44. package/dist/esm/url.js +27 -0
  45. package/dist/esm/url.js.map +1 -0
  46. package/dist/esm/web-vitals.js +156 -0
  47. package/dist/esm/web-vitals.js.map +1 -0
  48. package/dist/types/browser-acr.d.ts +142 -0
  49. package/dist/types/captured.d.ts +4 -0
  50. package/dist/types/control/command-runner.d.ts +57 -0
  51. package/dist/types/control/ui.d.ts +50 -0
  52. package/dist/types/device.d.ts +24 -0
  53. package/dist/types/fetch-tracker.d.ts +15 -0
  54. package/dist/types/identity.d.ts +48 -0
  55. package/dist/types/index.d.ts +17 -0
  56. package/dist/types/sdk-info.d.ts +4 -0
  57. package/dist/types/url.d.ts +10 -0
  58. package/dist/types/web-vitals.d.ts +50 -0
  59. package/package.json +40 -4
@@ -0,0 +1,50 @@
1
+ import type { ControlState } from '@lib-acr/core';
2
+ /** Which built-in screens the SDK renders. Turn one off to render your own from the control state. */
3
+ export interface ControlUiOptions {
4
+ /** Full-page maintenance screen. Default true. */
5
+ maintenance?: boolean;
6
+ /** Announcement banner at the top of the page. Default true. */
7
+ announcement?: boolean;
8
+ /** "New version available" prompt (release checks and `reload` commands in prompt mode). Default true. */
9
+ release?: boolean;
10
+ }
11
+ export type ResolvedUiOptions = Required<ControlUiOptions>;
12
+ export declare function resolveUiOptions(ui: boolean | ControlUiOptions | undefined): ResolvedUiOptions;
13
+ export interface ControlUiActions {
14
+ dismissAnnouncement(id: string): void;
15
+ reload(): void;
16
+ refresh(): void;
17
+ }
18
+ /** Tag of the element that hosts the shadow root. */
19
+ export declare const UI_HOST_TAG = "acr-ui";
20
+ /**
21
+ * The default remote-control UI: maintenance screen, announcement banner and "new version available" prompt. It lives
22
+ * in the shadow root of one `<acr-ui>` element, so the page's CSS cannot reach it and its CSS cannot leak out. Server
23
+ * text is always inserted as text, never as HTML. The element is added only while something is shown.
24
+ */
25
+ export declare class ControlUI {
26
+ private readonly options;
27
+ private readonly actions;
28
+ private host;
29
+ private wrap;
30
+ private readonly keys;
31
+ private readonly elements;
32
+ private dismissedToast;
33
+ private lastState;
34
+ private restoreFocus;
35
+ private savedOverflow;
36
+ private waitingForBody;
37
+ constructor(options: ResolvedUiOptions, actions: ControlUiActions);
38
+ render(state: ControlState): void;
39
+ /** Removes the element and everything it shows. */
40
+ destroy(): void;
41
+ private mount;
42
+ private unmount;
43
+ /** Replaces a section only when its content changes, so focus and animations are not disturbed. */
44
+ private update;
45
+ private buildMaintenance;
46
+ private onMaintenanceShown;
47
+ private onMaintenanceHidden;
48
+ private buildBanner;
49
+ private buildToast;
50
+ }
@@ -0,0 +1,24 @@
1
+ export interface DeviceInfo {
2
+ browser: string;
3
+ os: string;
4
+ deviceType: 'desktop' | 'mobile' | 'tablet' | 'unknown';
5
+ language: string | null;
6
+ timezone: string | null;
7
+ screen: string | null;
8
+ viewport: string | null;
9
+ }
10
+ /** Coarse browser, OS and device class from a user agent string. No version numbers are kept for the OS. */
11
+ export declare function parseUserAgent(ua: string): Pick<DeviceInfo, 'browser' | 'os' | 'deviceType'>;
12
+ /** Device and locale facts for session_start. Reads only what the browser exposes without a permission prompt. */
13
+ export declare function getDeviceInfo(env?: {
14
+ userAgent?: string;
15
+ language?: string;
16
+ screen?: {
17
+ width: number;
18
+ height: number;
19
+ };
20
+ viewport?: {
21
+ width: number;
22
+ height: number;
23
+ };
24
+ }): DeviceInfo;
@@ -0,0 +1,15 @@
1
+ import type { AcrClient } from '@lib-acr/core';
2
+ export interface FetchTrackerOptions {
3
+ /** Requests whose URL starts with one of these prefixes are not recorded (for example the ACR endpoint). */
4
+ ignoreUrlPrefixes: string[];
5
+ base: string;
6
+ }
7
+ type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
8
+ /**
9
+ * Wraps fetch to record duration and outcome per request. Request and response bodies and headers are never read.
10
+ * URLs are reduced to origin and a normalized path. Network failures are also reported as frontend errors, and then
11
+ * rethrown so the application behaves as before. A rethrown failure is marked as captured, so it is not reported a
12
+ * second time as an unhandled rejection.
13
+ */
14
+ export declare function wrapFetch(original: FetchFn, client: AcrClient, options: FetchTrackerOptions): FetchFn;
15
+ export {};
@@ -0,0 +1,48 @@
1
+ /** The subset of Web Storage used here. Storage can be blocked, so every access is guarded. */
2
+ export interface KeyValueStore {
3
+ getItem(key: string): string | null;
4
+ setItem(key: string, value: string): void;
5
+ removeItem?(key: string): void;
6
+ }
7
+ export declare const ANONYMOUS_KEY = "acr.anonymousId";
8
+ export declare const SESSION_KEY = "acr.session";
9
+ /** ISO time this browser first ran the SDK. Commands issued before it are skipped (nothing stale to clear). */
10
+ export declare const FIRST_SEEN_KEY = "acr.firstSeen";
11
+ export declare function randomId(): string;
12
+ /** Stable id for one browser profile. Kept in localStorage when available, in memory otherwise. */
13
+ export declare function getOrCreateAnonymousId(store: KeyValueStore | null, memory: {
14
+ id?: string;
15
+ }): string;
16
+ /**
17
+ * Returns `acr.firstSeen`, writing the current time when it is missing (a new visitor, or a visitor whose anonymous id
18
+ * predates this key). It is never changed afterwards, not even by reset(). With storage blocked the value lives in
19
+ * memory for this page only.
20
+ */
21
+ export declare function ensureFirstSeen(store: KeyValueStore | null, memory: {
22
+ firstSeen?: string;
23
+ }, now?: () => number): string;
24
+ /** Replaces the anonymous id with a new one (for example on logout) and returns it. */
25
+ export declare function resetAnonymousId(store: KeyValueStore | null, memory: {
26
+ id?: string;
27
+ }): string;
28
+ export interface SessionState {
29
+ id: string;
30
+ isNew: boolean;
31
+ }
32
+ /**
33
+ * Browser session: a new id after `timeoutMs` without activity, or when the tab is new. The state lives in
34
+ * sessionStorage, so it survives reloads of the same tab but not a new tab.
35
+ */
36
+ export declare class SessionTracker {
37
+ private readonly store;
38
+ private readonly timeoutMs;
39
+ private readonly now;
40
+ private memory;
41
+ constructor(store: KeyValueStore | null, timeoutMs: number, now?: () => number);
42
+ /** Returns the current session and marks it active. */
43
+ touch(): SessionState;
44
+ /** Ends the current session and starts a new one now. */
45
+ reset(): SessionState;
46
+ private read;
47
+ private write;
48
+ }
@@ -0,0 +1,17 @@
1
+ export { BrowserACR, BROWSER_SDK, DISMISSED_KEY, FORCE_RELOAD_KEY, QUEUE_KEY, assertPublishableOptions } from './browser-acr.js';
2
+ export type { BrowserAcrOptions, Teardown } from './browser-acr.js';
3
+ export { normalizePath, describeUrl } from './url.js';
4
+ export { getDeviceInfo, parseUserAgent } from './device.js';
5
+ export type { DeviceInfo } from './device.js';
6
+ export { ANONYMOUS_KEY, FIRST_SEEN_KEY, SessionTracker, ensureFirstSeen, getOrCreateAnonymousId, resetAnonymousId } from './identity.js';
7
+ export { CommandRunner, clearCaches, COMMANDS_KEY, COMMAND_APPLIED_EVENT, RELOAD_GUARD_KEY, RESERVED_PREFIX } from './control/command-runner.js';
8
+ export type { CacheCategory, CategoryResult, CommandOutcome, CommandResult, CommandRunnerDeps } from './control/command-runner.js';
9
+ export { ControlUI, UI_HOST_TAG, resolveUiOptions } from './control/ui.js';
10
+ export type { ControlUiActions, ControlUiOptions, ResolvedUiOptions } from './control/ui.js';
11
+ export type { KeyValueStore, SessionState } from './identity.js';
12
+ export { VitalsCollector } from './web-vitals.js';
13
+ export type { VitalsSnapshot } from './web-vitals.js';
14
+ export { wrapFetch } from './fetch-tracker.js';
15
+ export { markCaptured, wasCaptured } from './captured.js';
16
+ export { AcrError, CORE_SDK, REDACTED, REDACTED_EMAIL, SCHEMA_VERSION, EMPTY_CONTROL_STATE, clearCacheParams, compareSemver, describeError, evaluateFlag, fnv1a32, formatTraceparent, generateId, generateSpanId, generateTraceId, inRollout, isSensitiveKey, parseSemver, parseSnapshot, parseTraceparent, planCommands, redactAttributes, redactString, reloadParams, rolloutBucket, sessionSampled, typedFlagValue, } from '@lib-acr/core';
17
+ export type { AcrOptions, Announcement, AnnouncementSeverity, AnnouncementState, Attributes, AttributeValue, ClearCacheParams, ClientStats, CommandDecision, CommandSkipReason, ControlCommand, ControlListener, ControlSnapshot, ControlState, FlagDefinition, FlagOptions, FlagType, MaintenanceControl, MaintenanceState, ReleaseControl, ReleaseMode, ReleaseState, ReloadParams, SdkControl, ErrorDescription, ErrorOptions, FetchLike, FetchResponseLike, HeartbeatOptions, ItemOptions, LogLevel, QueuedItem, SdkInfo, TelemetryKind, TraceContext, } from '@lib-acr/core';
@@ -0,0 +1,4 @@
1
+ export declare const SDK_INFO: {
2
+ readonly name: "@lib-acr/browser";
3
+ readonly version: "0.0.1";
4
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Path for telemetry: no query string, no fragment, and ids replaced with :id so that /orders/42 and /orders/43 group
3
+ * together. Query strings often carry tokens or personal data, so they are never sent.
4
+ */
5
+ export declare function normalizePath(pathname: string): string;
6
+ /** Absolute URL to origin + normalized path, for api_request events. */
7
+ export declare function describeUrl(input: string, base: string): {
8
+ host: string;
9
+ path: string;
10
+ } | null;
@@ -0,0 +1,50 @@
1
+ export interface VitalsSnapshot {
2
+ /** First Contentful Paint, ms. */
3
+ fcp: number | null;
4
+ /** Largest Contentful Paint, ms. */
5
+ lcp: number | null;
6
+ /** Cumulative Layout Shift (session windows), unitless. */
7
+ cls: number | null;
8
+ /** Time to First Byte, ms. */
9
+ ttfb: number | null;
10
+ /**
11
+ * Longest interaction duration seen, ms. This approximates INP: it is the maximum, not the 98th percentile
12
+ * that the official metric uses.
13
+ */
14
+ inpMaxMs: number | null;
15
+ /** Load event end, ms. */
16
+ pageLoadMs: number | null;
17
+ }
18
+ type Listener = (snapshot: VitalsSnapshot) => void;
19
+ /**
20
+ * Collects Core Web Vitals with PerformanceObserver. It degrades silently where an entry type is unsupported, which is
21
+ * the case in older browsers and in jsdom.
22
+ */
23
+ export declare class VitalsCollector {
24
+ private state;
25
+ /** Stable reference, replaced only when a value changes, so useSyncExternalStore does not loop. */
26
+ private current;
27
+ private observers;
28
+ private readonly listeners;
29
+ private clsWindow;
30
+ private clsMax;
31
+ private loadListener;
32
+ private loadTimer;
33
+ start(): void;
34
+ stop(): void;
35
+ snapshot(): VitalsSnapshot;
36
+ subscribe(listener: Listener): () => void;
37
+ private observeEntries;
38
+ /**
39
+ * `loadEventEnd` is only known once the load event has finished. When start() runs earlier (the usual case for an
40
+ * SDK loaded in the page head), read the navigation entry again just after `load`.
41
+ */
42
+ private watchLoad;
43
+ private observe;
44
+ /** Session windows: a window ends after 1 s of quiet or 5 s in total; CLS is the largest window. */
45
+ private addLayoutShift;
46
+ private readNavigation;
47
+ private update;
48
+ private publish;
49
+ }
50
+ export {};
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@lib-acr/browser",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.0.1",
4
+ "description": "ACR SDK for browsers: JavaScript errors, page views, fetch timing, web vitals and sessions. Uses publishable keys only.",
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
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "sideEffects": false,
22
+ "engines": {
23
+ "node": ">=18.17"
24
+ },
25
+ "dependencies": {
26
+ "@lib-acr/core": "0.0.1"
27
+ },
28
+ "scripts": {
29
+ "build": "node ../../scripts/build-package.mjs",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit",
31
+ "test": "vitest run"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/application-control-room/application.git",
36
+ "directory": "packages/browser"
37
+ },
38
+ "homepage": "https://github.com/application-control-room/application/tree/main/packages/browser#readme",
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }