@atlaskit/insm 0.0.1 → 0.1.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +203 -5
  3. package/dist/cjs/index.js +55 -1
  4. package/dist/cjs/inp-measurers/inp.js +184 -0
  5. package/dist/cjs/insm-period.js +345 -0
  6. package/dist/cjs/insm-session.js +183 -0
  7. package/dist/cjs/insm.js +129 -0
  8. package/dist/cjs/period-measurers/afps.js +193 -0
  9. package/dist/cjs/types.js +5 -0
  10. package/dist/es2019/index.js +54 -1
  11. package/dist/es2019/inp-measurers/inp.js +142 -0
  12. package/dist/es2019/insm-period.js +246 -0
  13. package/dist/es2019/insm-session.js +149 -0
  14. package/dist/es2019/insm.js +105 -0
  15. package/dist/es2019/period-measurers/afps.js +153 -0
  16. package/dist/es2019/types.js +1 -0
  17. package/dist/esm/index.js +54 -1
  18. package/dist/esm/inp-measurers/inp.js +177 -0
  19. package/dist/esm/insm-period.js +339 -0
  20. package/dist/esm/insm-session.js +177 -0
  21. package/dist/esm/insm.js +122 -0
  22. package/dist/esm/period-measurers/afps.js +186 -0
  23. package/dist/esm/types.js +1 -0
  24. package/dist/types/index.d.ts +10 -1
  25. package/dist/types/inp-measurers/inp.d.ts +37 -0
  26. package/dist/types/insm-period.d.ts +72 -0
  27. package/dist/types/insm-session.d.ts +91 -0
  28. package/dist/types/insm.d.ts +61 -0
  29. package/dist/types/period-measurers/afps.d.ts +57 -0
  30. package/dist/types/types.d.ts +81 -0
  31. package/dist/types-ts4.5/index.d.ts +10 -1
  32. package/dist/types-ts4.5/inp-measurers/inp.d.ts +37 -0
  33. package/dist/types-ts4.5/insm-period.d.ts +72 -0
  34. package/dist/types-ts4.5/insm-session.d.ts +91 -0
  35. package/dist/types-ts4.5/insm.d.ts +61 -0
  36. package/dist/types-ts4.5/period-measurers/afps.d.ts +57 -0
  37. package/dist/types-ts4.5/types.d.ts +81 -0
  38. package/package.json +6 -5
@@ -0,0 +1,72 @@
1
+ import type { INSMSession } from './insm-session';
2
+ import type { Measure } from './types';
3
+ export declare class PeriodTracking {
4
+ private periodMeasurements;
5
+ /**
6
+ * On creation - this is set to the currently running sessions features,
7
+ * and is updated throughout the period for any additional features triggered.
8
+ * It is reset when starting a new period.
9
+ */
10
+ private latestPeriodFeatures;
11
+ private latestHeavyTasks;
12
+ state: 'inactive' | 'active';
13
+ pauses: Set<string>;
14
+ /**
15
+ * Warning: this can be reset mid period when pausing/resuming.
16
+ * It's intended use is to build the `periodMeasurements` duration.
17
+ */
18
+ private currentPeriodStart;
19
+ private session;
20
+ constructor(session: INSMSession);
21
+ startFeature(featureName: string): void;
22
+ startHeavyTask(heavyTaskName: string): void;
23
+ endHeavyTask(heavyTaskName: string): void;
24
+ /**
25
+ * Sets a pause based on a key. If this is the first pause, then it will also halt
26
+ * running interactivity measures, and update the current measurements duration.
27
+ */
28
+ pause(pauseName: string): void;
29
+ /**
30
+ * Releases a pause for a key.
31
+ *
32
+ * **NOTE**: The session will only resume if this was the only
33
+ * currently tracked pause key.
34
+ */
35
+ resume(pauseName: string): void;
36
+ get endResults(): {
37
+ active: {
38
+ features: Set<string>;
39
+ heavyTasks: Set<string>;
40
+ measurements: {
41
+ [key: string]: Measure;
42
+ };
43
+ duration: number;
44
+ count: number;
45
+ };
46
+ inactive: {
47
+ features: Set<string>;
48
+ heavyTasks: Set<string>;
49
+ measurements: {
50
+ [key: string]: Measure;
51
+ };
52
+ duration: number;
53
+ count: number;
54
+ };
55
+ };
56
+ private activeStartListeners;
57
+ private setupActiveStartInteractionListeners;
58
+ private activeEndListeners;
59
+ /**
60
+ * This works by;
61
+ * On setup
62
+ * - starts activity listeners
63
+ * - on activity received
64
+ * - possible end active count down started.
65
+ * 3 animation frames or 3 seconds of inactivity - whichever comes first
66
+ * - any existing end count down ended
67
+ */
68
+ private activeEndCountDownAbortController;
69
+ private activeEndCountDownVisibilityListener;
70
+ private setupEndActiveInteractionListeners;
71
+ private changePeriodAndTrackLast;
72
+ }
@@ -0,0 +1,91 @@
1
+ import type { INSM } from './insm';
2
+ import type { AddedProperties, ExperienceProperties } from './types';
3
+ import { PeriodTracking } from './insm-period';
4
+ /**
5
+ * Only intended for internal use.
6
+ *
7
+ * Exported for consumers who may require the type.
8
+ */
9
+ export declare class INSMSession {
10
+ private experienceKey;
11
+ private experienceProperties;
12
+ private startedAt;
13
+ insm: INSM;
14
+ private running;
15
+ private addedProperties;
16
+ runningFeatures: Set<string>;
17
+ periodTracking: PeriodTracking;
18
+ constructor(experienceKey: string, experienceProperties: ExperienceProperties, insm: INSM);
19
+ /**
20
+ * Adds a feature to the currently running session
21
+ */
22
+ startFeature(featureName: string): void;
23
+ /**
24
+ * Ends a features usage in the currently running session
25
+ */
26
+ endFeature(featureName: string): void;
27
+ /**
28
+ * Returns details on the current session.
29
+ */
30
+ get details(): {
31
+ experienceKey: string;
32
+ experienceProperties: ExperienceProperties;
33
+ paused: boolean;
34
+ periodState: "inactive" | "active";
35
+ /**
36
+ * The only scenario where this value should return false is when
37
+ * the experience has been stopped early.
38
+ */
39
+ running: boolean;
40
+ };
41
+ /**
42
+ * This api takes either a static single-level key-value object, or callbacks which return the same and
43
+ * will be evaluated on session end.
44
+ *
45
+ * When ending a session, all properties received via this api are merged, in order, into the resulting
46
+ * insm event’s properties; last write wins.
47
+ *
48
+ * Callback values are evaluated at session end.
49
+ *
50
+ * For example, for the following
51
+ *
52
+ * ```ts
53
+ * insm.experience.addProperties({ one: 1, two: 2 });
54
+ * insm.experience.addProperties(() => ({ one: 'one' }));
55
+ * insm.experience.addProperties({ three: 3 });
56
+ * ```
57
+ *
58
+ * The resulting added properties will be
59
+ *
60
+ * ```ts
61
+ * { one: 'one', two: 2, three: 3 }
62
+ * ```
63
+ */
64
+ addProperties(propertiesToAdd: AddedProperties): void;
65
+ /**
66
+ * In some scenarios (ie. when a page error boundary is hit), you will want to exit early.
67
+ * This is api supports these scenarios
68
+ *
69
+ * ```ts
70
+ * insm.stopEarly(reasonKey: string, description: string);
71
+ * ```
72
+ *
73
+ * Sessions closed early are identifiable by their end details
74
+ * `"endDetails": { stoppedBy: "early-stop", reasonKey, description }`.
75
+ *
76
+ * **Note**: The session is ended as soon as this is called, and any `addProperties` handlers will
77
+ * called immediately.
78
+ */
79
+ earlyStop(reason: string, description?: string): void;
80
+ end(endDetails: {
81
+ stoppedBy: 'new-experience';
82
+ experienceKey: string;
83
+ contentId?: string | null;
84
+ } | {
85
+ stoppedBy: 'pagehide';
86
+ } | {
87
+ stoppedBy: 'early-stop';
88
+ reason: string;
89
+ description?: string;
90
+ }): void;
91
+ }
@@ -0,0 +1,61 @@
1
+ import type { AnalyticsWebClient } from '@atlaskit/analytics-listeners';
2
+ import { INSMSession } from './insm-session';
3
+ import type { ExperienceProperties, INSMOptions } from './types';
4
+ import { AnimationFPSIM } from './period-measurers/afps';
5
+ import { INPTracker } from './inp-measurers/inp';
6
+ export declare class INSM {
7
+ analyticsWebClient?: AnalyticsWebClient;
8
+ runningSession?: INSMSession;
9
+ options: INSMOptions;
10
+ periodMeasurers: [
11
+ AnimationFPSIM,
12
+ INPTracker,
13
+ INPTracker,
14
+ INPTracker,
15
+ INPTracker,
16
+ INPTracker,
17
+ INPTracker
18
+ ];
19
+ /**
20
+ * Heavy tasks are tracked at the insm layer as heavy tasks
21
+ * are expected at times to be unrelated to the current
22
+ * page session.
23
+ */
24
+ runningHeavyTasks: Set<string>;
25
+ constructor(options: INSMOptions);
26
+ /**
27
+ * Starts a heavy task in the currently running session.
28
+ *
29
+ * This also pauses measurement.
30
+ */
31
+ startHeavyTask(heavyTaskName: string): void;
32
+ /**
33
+ * Ends a heavy task in the currently running session
34
+ */
35
+ endHeavyTask(heavyTaskName: string): void;
36
+ /**
37
+ * Call this when starting a new experience. This is expected to be wired to the product
38
+ * routing solution.
39
+ *
40
+ * It's expected this call will be paired with a `insm.session.startHeavyTask('page-load')` and subsequent `insm.session.endHeavyTask('page-load')`
41
+ * so that performance degradations linked to the page initialisation are excluded from the active interactivity monitoring.
42
+ *
43
+ *
44
+ * ```ts
45
+ * insm.start('edit-page', { initial: true, contentId: '9001' })
46
+ * insm.session.startHeavyTask(''page-load')
47
+ * // ... heavy initialisation work
48
+ * insm.session.endHeavyTask(''page-load')
49
+ * ```
50
+ */
51
+ start(experienceKey: string, experienceProperties: ExperienceProperties): void;
52
+ /**
53
+ * This prematurely halts any running experience measurement. It's expected to be used in
54
+ * scenarios such as when error boundaries are hit.
55
+ */
56
+ stopEarly(reasonKey: string, description: string): void;
57
+ /**
58
+ * Gets the current running session details
59
+ */
60
+ get session(): INSMSession | undefined;
61
+ }
@@ -0,0 +1,57 @@
1
+ import type { PeriodMeasurer } from '../types';
2
+ export declare class AnimationFPSIM implements PeriodMeasurer {
3
+ /**
4
+ * AFPS stands for Animation Frames Per Second
5
+ */
6
+ name: string;
7
+ monitor: AnimationFPSMonitor;
8
+ start(paused: boolean): {
9
+ numerator: number;
10
+ denominator: number;
11
+ max: number;
12
+ min: number;
13
+ average: number;
14
+ };
15
+ end(): {
16
+ numerator: number;
17
+ denominator: number;
18
+ max: number;
19
+ min: number;
20
+ average: number;
21
+ };
22
+ pause(): void;
23
+ resume(): void;
24
+ }
25
+ declare class AnimationFPSMonitor {
26
+ paused: boolean;
27
+ private currentState;
28
+ private windowFrameCount;
29
+ private windowTotalTime;
30
+ private currentFrameStart;
31
+ animationFrame?: ReturnType<typeof requestAnimationFrame>;
32
+ private measureWindowFPS;
33
+ /**
34
+ * If there is running tracking - it will be reset
35
+ */
36
+ private startWindowTracking;
37
+ startNewWindow(paused: boolean): {
38
+ numerator: number;
39
+ denominator: number;
40
+ max: number;
41
+ min: number;
42
+ average: number;
43
+ };
44
+ private resetWindow;
45
+ private endWindowTracking;
46
+ private resetOverallTracking;
47
+ end(): {
48
+ numerator: number;
49
+ denominator: number;
50
+ max: number;
51
+ min: number;
52
+ average: number;
53
+ };
54
+ pause(): void;
55
+ resume(): void;
56
+ }
57
+ export {};
@@ -0,0 +1,81 @@
1
+ import type { AnalyticsWebClient } from '@atlaskit/analytics-listeners';
2
+ export type INSMOptions = {
3
+ getAnalyticsWebClient: Promise<AnalyticsWebClient>;
4
+ /**
5
+ * If an experience is missing or not enabled - no session event will be fired
6
+ */
7
+ experiences: {
8
+ [key: string]: {
9
+ enabled: boolean;
10
+ } | undefined;
11
+ };
12
+ };
13
+ export type ExperienceProperties = {
14
+ /**
15
+ * Whether this represents the initial page the user is visiting
16
+ */
17
+ initial: boolean;
18
+ /**
19
+ * An optional content id (ie. for a Confluence page - the page id)
20
+ *
21
+ * Leaf experiences such as the Confluence Space Overview are
22
+ * not expected to provide this property.
23
+ */
24
+ contentId?: string | null;
25
+ };
26
+ export type AddedProperties = {
27
+ [key: string]: string | number | boolean;
28
+ } | (() => {
29
+ [key: string]: string | number | boolean;
30
+ });
31
+ export type Measure = {
32
+ numerator: number;
33
+ denominator: number;
34
+ max: number;
35
+ min: number;
36
+ average: number;
37
+ };
38
+ export interface PeriodMeasurer {
39
+ /**
40
+ * Name of the interactivity measurement (measures in the resulting insm event will be under this key)
41
+ */
42
+ name: string;
43
+ /**
44
+ * Called when an the state changes, and/or a new experience session starts.
45
+ *
46
+ * Implementers should take care to handle if measurements can be received after
47
+ * an animation frame. In this scenario, on resetting - the consumer needs to
48
+ * discard any measurements started before the reset.
49
+ *
50
+ * The possibility of data loss due to this is mitigated by the inactive period logic
51
+ * where a session can not be marked as inactive until
52
+ * - at least 3 seconds of no user activity
53
+ * - and 2 animation frames since the last user activity.
54
+ *
55
+ * **Important** consumers need to handle starting up both initial tracking and also
56
+ * when tracking is restarted after an end.
57
+ *
58
+ * @returns The interactivity measurements for the current period (or undefined for the initial start)
59
+ */
60
+ start: (
61
+ /**
62
+ * When started with paused = true. it indicates a heavy task is running at startup time.
63
+ */
64
+ paused: boolean) => Measure | undefined;
65
+ /**
66
+ * Run any cleanup, and report the last periods interactivity.
67
+ *
68
+ * Important note: A new period can start after the end has been reached
69
+ * in cases where the measurement was ended due to a scenario such as an
70
+ * error boundary being hit (via `insm.stopEarly`).
71
+ */
72
+ end: () => Measure;
73
+ /**
74
+ * Pauses measurement (ie. when heavy work is triggered)
75
+ */
76
+ pause: () => void;
77
+ /**
78
+ * Pauses measurement (ie. when heavy work completes)
79
+ */
80
+ resume: () => void;
81
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/insm",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "INSM tooling measures user-perceived interactivity of a page",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -24,13 +24,14 @@
24
24
  }
25
25
  },
26
26
  "atlaskit:src": "src/index.ts",
27
- "af:exports": {
28
- ".": "./src/index.ts"
29
- },
30
27
  "dependencies": {
31
- "@atlaskit/tmp-editor-statsig": "^10.0.0",
28
+ "@atlaskit/analytics-listeners": "^9.0.0",
29
+ "@atlaskit/tmp-editor-statsig": "^11.9.0",
32
30
  "@babel/runtime": "^7.0.0"
33
31
  },
32
+ "devDependencies": {
33
+ "@atlaskit/editor-test-helpers": "workspace:^"
34
+ },
34
35
  "peerDependencies": {
35
36
  "react": "^18.2.0"
36
37
  },