@webiny/telemetry 0.0.0-unstable.e622468070 → 0.0.0-unstable.e9333b3f0b

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/cli.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export declare interface SendEventParams {
2
2
  event: string;
3
- user?: string;
4
3
  version?: string;
5
4
  properties: Record<string, any>;
6
5
  }
package/cli.js CHANGED
@@ -1,17 +1,24 @@
1
1
  import { globalConfig } from "@webiny/global-config";
2
2
  import { isCI } from "ci-info";
3
- import { WTS } from "wts-client/node.js";
3
+ import { WTS } from "@webiny/wts-client/node";
4
4
  import baseSendEvent from "./sendEvent.js";
5
- import readJson from "load-json-file";
5
+ import { loadJsonFileSync } from "load-json-file";
6
6
  import path from "path";
7
7
 
8
- export const sendEvent = async ({ event, user, version, properties }) => {
8
+ export const sendEvent = async ({ event, version, properties }) => {
9
9
  const shouldSend = isEnabled();
10
10
  if (!shouldSend) {
11
11
  return;
12
12
  }
13
13
 
14
- const wts = new WTS();
14
+ // Use the canonical Webiny machine id — the top-level `id` field in
15
+ // `~/.webiny/config`, owned by @webiny/global-config. The admin app
16
+ // (REACT_APP_WEBINY_TELEMETRY_USER_ID) and the website install/finish alias
17
+ // both key off this same id, so passing it here keeps CLI, admin, and
18
+ // website events on a single PostHog person. Without it, the WTS client
19
+ // falls back to its own `user.id` field, which is a different UUID and
20
+ // fragments funnels across surfaces.
21
+ const wts = new WTS({ source: "cli", distinctId: globalConfig.get("id") });
15
22
 
16
23
  const wcpProperties = {};
17
24
  const [wcpOrgId, wcpProjectId] = getWcpOrgProjectId();
@@ -20,15 +27,31 @@ export const sendEvent = async ({ event, user, version, properties }) => {
20
27
  wcpProperties.wcpProjectId = wcpProjectId;
21
28
  }
22
29
 
30
+ const installationProperties = {};
31
+ const installationId = getInstallationId();
32
+ if (installationId) {
33
+ installationProperties.installation_id = installationId;
34
+ }
35
+
36
+ // Which hosting type the project runs on ("aws" or "server"). Both CLI bins set this marker at
37
+ // startup (`cli-aws/src/bin.ts`, `cli-server/src/bin.ts`), so every CLI event can be segmented
38
+ // by hosting type without each call site passing it along. Omitted when unset rather than
39
+ // defaulted to "aws", because a missing marker means an unknown caller, not an AWS project.
40
+ const hostingTypeProperties = {};
41
+ if (process.env.WEBINY_HOSTING_TYPE) {
42
+ hostingTypeProperties.hostingType = process.env.WEBINY_HOSTING_TYPE;
43
+ }
44
+
23
45
  const packageJsonPath = path.join(import.meta.dirname, "package.json");
24
- const packageJson = await readJson(packageJsonPath);
46
+ const packageJson = loadJsonFileSync(packageJsonPath);
25
47
 
26
48
  return baseSendEvent({
27
49
  event,
28
- user: user || globalConfig.get("id"),
29
50
  properties: {
30
51
  ...properties,
31
52
  ...wcpProperties,
53
+ ...installationProperties,
54
+ ...hostingTypeProperties,
32
55
  version: version || packageJson.version,
33
56
  ci: isCI,
34
57
  newUser: Boolean(globalConfig.get("newUser"))
@@ -38,14 +61,30 @@ export const sendEvent = async ({ event, user, version, properties }) => {
38
61
  };
39
62
 
40
63
  const getWcpOrgProjectId = () => {
41
- // In CLI, WCP project ID is stored in the `WCP_PROJECT_ID` environment variable.
42
- const id = process.env.WCP_PROJECT_ID;
64
+ // In CLI, project ID is stored in the `WEBINY_PROJECT_ID` or `WCP_PROJECT_ID` environment variable.
65
+ const id = process.env.WEBINY_PROJECT_ID || process.env.WCP_PROJECT_ID;
43
66
  if (typeof id === "string") {
44
67
  return id.split("/");
45
68
  }
46
69
  return [];
47
70
  };
48
71
 
72
+ /**
73
+ * Reads the anonymous per-project installation id from
74
+ * `<project-root>/package.json` → `webiny.installationId`. Generated once at
75
+ * `create-webiny-project` time and tracked in git so it stays stable across
76
+ * machines that share the project. Returns null if the file is missing or
77
+ * unreadable — telemetry events still fire, just without the property.
78
+ */
79
+ const getInstallationId = () => {
80
+ try {
81
+ const data = loadJsonFileSync(path.join(process.cwd(), "package.json"));
82
+ return typeof data?.webiny?.installationId === "string" ? data.webiny.installationId : null;
83
+ } catch {
84
+ return null;
85
+ }
86
+ };
87
+
49
88
  export const enable = () => {
50
89
  globalConfig.set("telemetry", true);
51
90
  };
package/package.json CHANGED
@@ -1,19 +1,17 @@
1
1
  {
2
2
  "name": "@webiny/telemetry",
3
- "version": "0.0.0-unstable.e622468070",
3
+ "version": "0.0.0-unstable.e9333b3f0b",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "dependencies": {
7
- "@webiny/global-config": "0.0.0-unstable.e622468070",
7
+ "@webiny/global-config": "0.0.0-unstable.e9333b3f0b",
8
+ "@webiny/wts-client": "3.1.4",
8
9
  "ci-info": "4.4.0",
9
10
  "jsesc": "3.1.0",
10
- "load-json-file": "6.2.0",
11
- "strip-ansi": "6.0.1",
12
- "wts-client": "2.0.0"
11
+ "load-json-file": "7.0.1",
12
+ "strip-ansi": "7.2.0"
13
13
  },
14
14
  "publishConfig": {
15
- "access": "public",
16
- "directory": "."
17
- },
18
- "gitHead": "e622468070839dc3f9552c85bf2bbac12d2ef88f"
15
+ "access": "public"
16
+ }
19
17
  }
package/react.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export declare function sendEvent(ev: string, properties?: Record<string, any>): Promise<any>;
2
+ export declare function getMachineId(): string | null;
package/react.js CHANGED
@@ -1,5 +1,92 @@
1
1
  import baseSendEvent from "./sendEvent.js";
2
- import { WTS } from "wts-client/admin.js";
2
+ import { WTS } from "@webiny/wts-client/web";
3
+
4
+ const STORAGE_MACHINE_ID = "wts_machine_id";
5
+ // Storage key kept as-is: ids persisted by earlier versions still live under it, and renaming the
6
+ // key would silently mint a new installation id for every existing admin session.
7
+ const STORAGE_INSTALLATION_ID = "wts_project_id";
8
+
9
+ let wtsInstance = null;
10
+ let installationId = null;
11
+ let distinctId = null;
12
+
13
+ /**
14
+ * Resolves the WTS client identity for the admin app.
15
+ *
16
+ * Priority for `distinct_id` (machine_id):
17
+ * 1. URL param `wts_did` on first load. Persisted to localStorage.
18
+ * 2. localStorage (subsequent loads).
19
+ * 3. `process.env.REACT_APP_WEBINY_TELEMETRY_USER_ID` (build-time fallback,
20
+ * set by `SetAdminAppEnvVarsBefore{Build,Watch}` from `~/.webiny/config`).
21
+ *
22
+ * Priority for `installation_id`:
23
+ * 1. URL param `iid` on first load. Persisted to localStorage.
24
+ * 2. localStorage.
25
+ * 3. `process.env.REACT_APP_WEBINY_INSTALLATION_ID` (build-time fallback,
26
+ * set from `<project>/package.json` → `webiny.installationId`).
27
+ *
28
+ * Attached as a super-property on every admin event so PostHog funnels can
29
+ * group per-install. The property name matches the one the CLI sends
30
+ * (`telemetry/cli.js`) so CLI and admin events for the same project join on a
31
+ * single property.
32
+ */
33
+ const initWts = () => {
34
+ if (wtsInstance) {
35
+ return wtsInstance;
36
+ }
37
+
38
+ distinctId = process.env.REACT_APP_WEBINY_TELEMETRY_USER_ID;
39
+ installationId = process.env.REACT_APP_WEBINY_INSTALLATION_ID || null;
40
+
41
+ if (typeof window !== "undefined") {
42
+ const params = new URLSearchParams(window.location.search);
43
+
44
+ const fromUrl = params.get("wts_did");
45
+ if (fromUrl) {
46
+ distinctId = fromUrl;
47
+ try {
48
+ window.localStorage.setItem(STORAGE_MACHINE_ID, fromUrl);
49
+ } catch {
50
+ // localStorage unavailable; URL value is used for this session only.
51
+ }
52
+ } else {
53
+ try {
54
+ distinctId = window.localStorage.getItem(STORAGE_MACHINE_ID) || distinctId;
55
+ } catch {
56
+ // ignore
57
+ }
58
+ }
59
+
60
+ const iidFromUrl = params.get("iid");
61
+ if (iidFromUrl) {
62
+ installationId = iidFromUrl;
63
+ try {
64
+ window.localStorage.setItem(STORAGE_INSTALLATION_ID, iidFromUrl);
65
+ } catch {
66
+ // ignore
67
+ }
68
+ } else {
69
+ try {
70
+ installationId =
71
+ window.localStorage.getItem(STORAGE_INSTALLATION_ID) || installationId;
72
+ } catch {
73
+ // env-var value (set above) is used as fallback.
74
+ }
75
+ }
76
+ }
77
+
78
+ wtsInstance = new WTS({ source: "admin", distinctId });
79
+ return wtsInstance;
80
+ };
81
+
82
+ /**
83
+ * Returns the machine_id used by admin events, if known. Used by the
84
+ * install/finish CTA to construct the alias handoff URL.
85
+ */
86
+ export const getMachineId = () => {
87
+ initWts();
88
+ return distinctId || null;
89
+ };
3
90
 
4
91
  export const sendEvent = async (event, properties = {}) => {
5
92
  const shouldSend = process.env.REACT_APP_WEBINY_TELEMETRY !== "false";
@@ -7,7 +94,7 @@ export const sendEvent = async (event, properties = {}) => {
7
94
  return;
8
95
  }
9
96
 
10
- const wts = new WTS();
97
+ const wts = initWts();
11
98
 
12
99
  const wcpProperties = {};
13
100
  const [wcpOrgId, wcpProjectId] = getWcpOrgProjectId();
@@ -16,12 +103,23 @@ export const sendEvent = async (event, properties = {}) => {
16
103
  wcpProperties.wcpProjectId = wcpProjectId;
17
104
  }
18
105
 
106
+ const installationProperties = {};
107
+ if (installationId) {
108
+ installationProperties.installation_id = installationId;
109
+ }
110
+
111
+ const hostingTypeProperties = {};
112
+ if (process.env.REACT_APP_WEBINY_HOSTING_TYPE) {
113
+ hostingTypeProperties.hostingType = process.env.REACT_APP_WEBINY_HOSTING_TYPE;
114
+ }
115
+
19
116
  return baseSendEvent({
20
117
  event,
21
- user: process.env.REACT_APP_WEBINY_TELEMETRY_USER_ID,
22
118
  properties: {
23
119
  ...properties,
24
120
  ...wcpProperties,
121
+ ...installationProperties,
122
+ ...hostingTypeProperties,
25
123
  version: process.env.REACT_APP_WEBINY_VERSION,
26
124
  ci: process.env.REACT_APP_IS_CI === "true",
27
125
  newUser: process.env.REACT_APP_WEBINY_TELEMETRY_NEW_USER === "true"
@@ -31,8 +129,8 @@ export const sendEvent = async (event, properties = {}) => {
31
129
  };
32
130
 
33
131
  const getWcpOrgProjectId = () => {
34
- // In React applications, WCP project ID is stored in the `REACT_APP_WCP_PROJECT_ID` environment variable.
35
- const id = process.env.REACT_APP_WCP_PROJECT_ID;
132
+ // In React applications, project ID is stored in the `REACT_APP_WEBINY_PROJECT_ID` or `REACT_APP_WCP_PROJECT_ID` environment variable.
133
+ const id = process.env.REACT_APP_WEBINY_PROJECT_ID || process.env.REACT_APP_WCP_PROJECT_ID;
36
134
  if (typeof id === "string") {
37
135
  return id.split("/");
38
136
  }
package/sendEvent.js CHANGED
@@ -4,17 +4,16 @@ import jsesc from "jsesc";
4
4
  /**
5
5
  * The main `sendEvent` function.
6
6
  * NOTE: don't use this in your app directly. Instead, use the one from `cli.js` or `react.js` files accordingly.
7
+ *
8
+ * Identity is owned by the WTS instance — `cli.js` reads `~/.webiny/config`,
9
+ * `react.js` reads URL params / localStorage / env. This function only
10
+ * validates and sanitises the event payload before dispatching.
7
11
  */
8
- export default ({ event, user, properties, wts } = {}) => {
9
- // 1. Check for the existence of required base parameters.
12
+ export default ({ event, properties, wts } = {}) => {
10
13
  if (!event) {
11
14
  throw new Error(`Cannot send event - missing "event" name.`);
12
15
  }
13
16
 
14
- if (!user) {
15
- throw new Error(`Cannot send event - missing "user" ID.`);
16
- }
17
-
18
17
  if (!properties) {
19
18
  throw new Error(`Cannot send event - missing "properties" object.`);
20
19
  }
@@ -23,7 +22,6 @@ export default ({ event, user, properties, wts } = {}) => {
23
22
  throw new Error(`Cannot send event - missing "wts" instance.`);
24
23
  }
25
24
 
26
- // 2. Extract properties and check for existence of required properties.
27
25
  if (!properties.version) {
28
26
  throw new Error(`Cannot send event - missing "version" property.`);
29
27
  }
@@ -38,7 +36,6 @@ export default ({ event, user, properties, wts } = {}) => {
38
36
  throw new Error(`Cannot send event - missing "newUser" boolean property.`);
39
37
  }
40
38
 
41
- // 2. Sanitize properties.
42
39
  const sanitizedProperties = {
43
40
  ...properties,
44
41
  newUser: properties.newUser === true ? "yes" : "no",
@@ -56,6 +53,5 @@ export default ({ event, user, properties, wts } = {}) => {
56
53
  sanitizedProperties[key] = sanitizedValue;
57
54
  }
58
55
 
59
- // 3. Send.
60
- return wts.trackEvent(user, event, sanitizedProperties);
56
+ return wts.track(event, sanitizedProperties);
61
57
  };