@shware/analytics 10.3.0 → 11.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 (48) hide show
  1. package/README.md +33 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.d.cts +3 -3
  4. package/dist/index.d.mts +3 -3
  5. package/dist/index.mjs +2 -2
  6. package/dist/schema/index.cjs +5 -0
  7. package/dist/schema/index.cjs.map +1 -1
  8. package/dist/schema/index.d.cts +5 -0
  9. package/dist/schema/index.d.cts.map +1 -1
  10. package/dist/schema/index.d.mts +5 -0
  11. package/dist/schema/index.d.mts.map +1 -1
  12. package/dist/schema/index.mjs +5 -0
  13. package/dist/schema/index.mjs.map +1 -1
  14. package/dist/server/openai-conversions-api.cjs +1 -1
  15. package/dist/server/openai-conversions-api.mjs +1 -1
  16. package/dist/server/reddit-conversions-api.cjs +1 -1
  17. package/dist/server/reddit-conversions-api.mjs +1 -1
  18. package/dist/setup/index.cjs +1 -4
  19. package/dist/setup/index.cjs.map +1 -1
  20. package/dist/setup/index.d.cts +1 -2
  21. package/dist/setup/index.d.cts.map +1 -1
  22. package/dist/setup/index.d.mts +1 -2
  23. package/dist/setup/index.d.mts.map +1 -1
  24. package/dist/setup/index.mjs +1 -4
  25. package/dist/setup/index.mjs.map +1 -1
  26. package/dist/third-parties/microsoft-uet.cjs +6 -9
  27. package/dist/third-parties/microsoft-uet.cjs.map +1 -1
  28. package/dist/third-parties/microsoft-uet.d.cts.map +1 -1
  29. package/dist/third-parties/microsoft-uet.d.mts.map +1 -1
  30. package/dist/third-parties/microsoft-uet.mjs +7 -10
  31. package/dist/third-parties/microsoft-uet.mjs.map +1 -1
  32. package/dist/track/index.cjs +11 -25
  33. package/dist/track/index.cjs.map +1 -1
  34. package/dist/track/index.d.cts +5 -5
  35. package/dist/track/index.d.cts.map +1 -1
  36. package/dist/track/index.d.mts +5 -5
  37. package/dist/track/index.d.mts.map +1 -1
  38. package/dist/track/index.mjs +12 -26
  39. package/dist/track/index.mjs.map +1 -1
  40. package/dist/visitor/index.cjs +19 -52
  41. package/dist/visitor/index.cjs.map +1 -1
  42. package/dist/visitor/index.d.cts +10 -1
  43. package/dist/visitor/index.d.cts.map +1 -1
  44. package/dist/visitor/index.d.mts +10 -1
  45. package/dist/visitor/index.d.mts.map +1 -1
  46. package/dist/visitor/index.mjs +20 -53
  47. package/dist/visitor/index.mjs.map +1 -1
  48. package/package.json +1 -1
@@ -2,61 +2,29 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_setup_index = require("../setup/index.cjs");
3
3
  const require_constants_storage = require("../constants/storage.cjs");
4
4
  let _shware_utils = require("@shware/utils");
5
+ let uuid = require("uuid");
5
6
  //#region src/visitor/index.ts
6
- async function createVisitor() {
7
- const tags = await require_setup_index.config.getTags();
8
- const dto = {
9
- device_id: await require_setup_index.config.getDeviceId(),
10
- platform: require_setup_index.config.platform,
11
- environment: require_setup_index.config.environment,
12
- tags,
13
- properties: tags
14
- };
15
- const response = await (0, _shware_utils.fetch)(`${require_setup_index.config.endpoint}/visitors`, {
16
- method: "POST",
17
- credentials: "include",
18
- headers: await require_setup_index.config.getHeaders(),
19
- body: JSON.stringify(dto)
20
- });
21
- if (!response.ok) throw new Error(`Failed to create visitor: ${response.status} ${await response.text()}`);
22
- const data = await response.json();
23
- if (data.id) require_setup_index.config.storage.setItem(require_constants_storage.keys.visitor_id, data.id);
24
- return data;
25
- }
26
- async function getOrCreateVisitor() {
27
- const visitorId = require_setup_index.config.storage.getItem(require_constants_storage.keys.visitor_id);
28
- if (visitorId && visitorId !== "undefined") {
29
- const tags = await require_setup_index.config.getTags();
30
- const response = await (0, _shware_utils.fetch)(`${require_setup_index.config.endpoint}/visitors/${visitorId}`, {
31
- method: "PATCH",
32
- credentials: "include",
33
- headers: await require_setup_index.config.getHeaders(),
34
- body: JSON.stringify({ tags })
35
- });
36
- if (!response.ok) return createVisitor();
37
- const data = await response.json();
38
- if (data.id) require_setup_index.config.storage.setItem(require_constants_storage.keys.visitor_id, data.id);
39
- return data;
40
- } else return createVisitor();
41
- }
42
- let visitorFetcher = null;
43
- async function getVisitor() {
44
- if (require_setup_index.cache.visitor) return require_setup_index.cache.visitor;
45
- if (visitorFetcher) return visitorFetcher;
46
- visitorFetcher = getOrCreateVisitor();
47
- try {
48
- require_setup_index.cache.visitor = await visitorFetcher;
49
- return require_setup_index.cache.visitor;
50
- } finally {
51
- visitorFetcher = null;
52
- }
7
+ /**
8
+ * This visitor's id: generated here on the first visit (uuidv7) and kept in `config.storage`, so
9
+ * it exists the moment the page loads. The first events, and the beacon of a visit left within a
10
+ * second, go out without waiting for a round trip; the server creates the visitor from its first
11
+ * events. An id a server issued to an older client is kept as it is: the visitor continues.
12
+ *
13
+ * Needs a server that creates visitors from events (see the README, "Visitors"); one that still
14
+ * only creates them on `POST /visitors` rejects the events of a new visitor.
15
+ */
16
+ function visitorId() {
17
+ const stored = require_setup_index.config.storage.getItem(require_constants_storage.keys.visitor_id);
18
+ if (stored && stored !== "undefined") return stored;
19
+ const id = (0, uuid.v7)();
20
+ require_setup_index.config.storage.setItem(require_constants_storage.keys.visitor_id, id);
21
+ return id;
53
22
  }
54
23
  async function setVisitor(dto) {
55
- const { id } = await getVisitor();
56
- const tags = await require_setup_index.config.getTags();
24
+ const id = visitorId();
57
25
  const body = {
58
26
  ...dto,
59
- tags
27
+ tags: await require_setup_index.config.getTags()
60
28
  };
61
29
  const response = await (0, _shware_utils.fetch)(`${require_setup_index.config.endpoint}/visitors/${id}`, {
62
30
  method: "PATCH",
@@ -77,11 +45,10 @@ async function setVisitor(dto) {
77
45
  if (e instanceof Error) console.log(e.message);
78
46
  }
79
47
  });
80
- require_setup_index.cache.visitor = data;
81
48
  return data;
82
49
  }
83
50
  //#endregion
84
- exports.getVisitor = getVisitor;
85
51
  exports.setVisitor = setVisitor;
52
+ exports.visitorId = visitorId;
86
53
 
87
54
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["config","fetch","keys","cache"],"sources":["../../src/visitor/index.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { keys } from '../constants/storage';\nimport type { CreateVisitorDTO, UpdateVisitorDTO } from '../schema/index';\nimport { cache, config } from '../setup/index';\nimport type { Visitor, VisitorProperties } from './types';\n\nasync function createVisitor(): Promise<Visitor> {\n const tags = await config.getTags();\n const dto: CreateVisitorDTO = {\n device_id: await config.getDeviceId(),\n platform: config.platform,\n environment: config.environment,\n tags,\n properties: tags as VisitorProperties,\n };\n\n const response = await fetch(`${config.endpoint}/visitors`, {\n method: 'POST',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(dto),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create visitor: ${response.status} ${await response.text()}`);\n }\n const data = (await response.json()) as Visitor;\n if (data.id) {\n config.storage.setItem(keys.visitor_id, data.id);\n }\n return data;\n}\n\nasync function getOrCreateVisitor(): Promise<Visitor> {\n const visitorId = config.storage.getItem(keys.visitor_id);\n if (visitorId && visitorId !== 'undefined') {\n // PATCH, not GET: `tags` is the last-touch counterpart to `initial_tags`,\n // and the only thing that ever refreshed it was `setVisitor`, which hosts\n // call when they identify a user. A visitor who never signs in therefore\n // kept the browser, screen, and release captured on their first ever page\n // load — for the rest of their life — leaving `tags` permanently equal to\n // `initial_tags` and the two columns pointless.\n //\n // Costs nothing extra: this replaces the request that was already here.\n const tags = await config.getTags();\n const response = await fetch(`${config.endpoint}/visitors/${visitorId}`, {\n method: 'PATCH',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify({ tags } satisfies UpdateVisitorDTO),\n });\n\n if (!response.ok) return createVisitor();\n const data = (await response.json()) as Visitor;\n\n if (data.id) {\n config.storage.setItem(keys.visitor_id, data.id);\n }\n return data;\n } else {\n return createVisitor();\n }\n}\n\nlet visitorFetcher: Promise<Visitor> | null = null;\n\nexport async function getVisitor(): Promise<Visitor> {\n if (cache.visitor) return cache.visitor;\n if (visitorFetcher) return visitorFetcher;\n visitorFetcher = getOrCreateVisitor();\n try {\n cache.visitor = await visitorFetcher;\n return cache.visitor;\n } finally {\n // In a `finally`, so a rejected attempt is not left in `visitorFetcher` for every later\n // caller to await again: `sendEvents` needs a visitor for every batch, and one failed\n // request would otherwise stop the page from reporting anything until it is reloaded.\n visitorFetcher = null;\n }\n}\n\nexport async function setVisitor(dto: Omit<UpdateVisitorDTO, 'tags'>) {\n const { id } = await getVisitor();\n const tags = await config.getTags();\n const body: UpdateVisitorDTO = { ...dto, tags };\n const response = await fetch(`${config.endpoint}/visitors/${id}`, {\n method: 'PATCH',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(body),\n });\n\n if (!response.ok) throw new Error('Failed to set visitor');\n const data = (await response.json()) as Visitor;\n\n // Setters get the server's distinct_id — the person the visitor now belongs to — not anything\n // the client could have said about it.\n const identity = { ...body, distinct_id: data.distinct_id ?? null };\n config.thirdPartyUserSetters.forEach((setter) => {\n try {\n setter(identity);\n } catch (e: unknown) {\n // The visitor was updated before this ran, so a third-party setter throwing must not skip\n // the cache write below or reject a call that already succeeded.\n if (e instanceof Error) console.log(e.message);\n }\n });\n cache.visitor = data;\n return data;\n}\n"],"mappings":";;;;;AAMA,eAAe,gBAAkC;CAC/C,MAAM,OAAO,MAAMA,oBAAAA,OAAO,QAAQ;CAClC,MAAM,MAAwB;EAC5B,WAAW,MAAMA,oBAAAA,OAAO,YAAY;EACpC,UAAUA,oBAAAA,OAAO;EACjB,aAAaA,oBAAAA,OAAO;EACpB;EACA,YAAY;CACd;CAEA,MAAM,WAAW,OAAA,GAAMC,cAAAA,MAAAA,CAAM,GAAGD,oBAAAA,OAAO,SAAS,YAAY;EAC1D,QAAQ;EACR,aAAa;EACb,SAAS,MAAMA,oBAAAA,OAAO,WAAW;EACjC,MAAM,KAAK,UAAU,GAAG;CAC1B,CAAC;CAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,6BAA6B,SAAS,OAAO,GAAG,MAAM,SAAS,KAAK,GAAG;CAEzF,MAAM,OAAQ,MAAM,SAAS,KAAK;CAClC,IAAI,KAAK,IACP,oBAAA,OAAO,QAAQ,QAAQE,0BAAAA,KAAK,YAAY,KAAK,EAAE;CAEjD,OAAO;AACT;AAEA,eAAe,qBAAuC;CACpD,MAAM,YAAYF,oBAAAA,OAAO,QAAQ,QAAQE,0BAAAA,KAAK,UAAU;CACxD,IAAI,aAAa,cAAc,aAAa;EAS1C,MAAM,OAAO,MAAMF,oBAAAA,OAAO,QAAQ;EAClC,MAAM,WAAW,OAAA,GAAMC,cAAAA,MAAAA,CAAM,GAAGD,oBAAAA,OAAO,SAAS,YAAY,aAAa;GACvE,QAAQ;GACR,aAAa;GACb,SAAS,MAAMA,oBAAAA,OAAO,WAAW;GACjC,MAAM,KAAK,UAAU,EAAE,KAAK,CAA4B;EAC1D,CAAC;EAED,IAAI,CAAC,SAAS,IAAI,OAAO,cAAc;EACvC,MAAM,OAAQ,MAAM,SAAS,KAAK;EAElC,IAAI,KAAK,IACP,oBAAA,OAAO,QAAQ,QAAQE,0BAAAA,KAAK,YAAY,KAAK,EAAE;EAEjD,OAAO;CACT,OACE,OAAO,cAAc;AAEzB;AAEA,IAAI,iBAA0C;AAE9C,eAAsB,aAA+B;CACnD,IAAIC,oBAAAA,MAAM,SAAS,OAAOA,oBAAAA,MAAM;CAChC,IAAI,gBAAgB,OAAO;CAC3B,iBAAiB,mBAAmB;CACpC,IAAI;EACF,oBAAA,MAAM,UAAU,MAAM;EACtB,OAAOA,oBAAAA,MAAM;CACf,UAAU;EAIR,iBAAiB;CACnB;AACF;AAEA,eAAsB,WAAW,KAAqC;CACpE,MAAM,EAAE,OAAO,MAAM,WAAW;CAChC,MAAM,OAAO,MAAMH,oBAAAA,OAAO,QAAQ;CAClC,MAAM,OAAyB;EAAE,GAAG;EAAK;CAAK;CAC9C,MAAM,WAAW,OAAA,GAAMC,cAAAA,MAAAA,CAAM,GAAGD,oBAAAA,OAAO,SAAS,YAAY,MAAM;EAChE,QAAQ;EACR,aAAa;EACb,SAAS,MAAMA,oBAAAA,OAAO,WAAW;EACjC,MAAM,KAAK,UAAU,IAAI;CAC3B,CAAC;CAED,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,uBAAuB;CACzD,MAAM,OAAQ,MAAM,SAAS,KAAK;CAIlC,MAAM,WAAW;EAAE,GAAG;EAAM,aAAa,KAAK,eAAe;CAAK;CAClE,oBAAA,OAAO,sBAAsB,SAAS,WAAW;EAC/C,IAAI;GACF,OAAO,QAAQ;EACjB,SAAS,GAAY;GAGnB,IAAI,aAAa,OAAO,QAAQ,IAAI,EAAE,OAAO;EAC/C;CACF,CAAC;CACD,oBAAA,MAAM,UAAU;CAChB,OAAO;AACT"}
1
+ {"version":3,"file":"index.cjs","names":["config","keys","uuidv7","fetch"],"sources":["../../src/visitor/index.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { v7 as uuidv7 } from 'uuid';\nimport { keys } from '../constants/storage';\nimport type { UpdateVisitorDTO } from '../schema/index';\nimport { config } from '../setup/index';\nimport type { Visitor } from './types';\n\n/**\n * This visitor's id: generated here on the first visit (uuidv7) and kept in `config.storage`, so\n * it exists the moment the page loads. The first events, and the beacon of a visit left within a\n * second, go out without waiting for a round trip; the server creates the visitor from its first\n * events. An id a server issued to an older client is kept as it is: the visitor continues.\n *\n * Needs a server that creates visitors from events (see the README, \"Visitors\"); one that still\n * only creates them on `POST /visitors` rejects the events of a new visitor.\n */\nexport function visitorId(): string {\n const stored = config.storage.getItem(keys.visitor_id);\n if (stored && stored !== 'undefined') return stored;\n const id = uuidv7();\n config.storage.setItem(keys.visitor_id, id);\n return id;\n}\n\nexport async function setVisitor(dto: Omit<UpdateVisitorDTO, 'tags'>) {\n const id = visitorId();\n const body: UpdateVisitorDTO = { ...dto, tags: await config.getTags() };\n const response = await fetch(`${config.endpoint}/visitors/${id}`, {\n method: 'PATCH',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(body),\n });\n\n if (!response.ok) throw new Error('Failed to set visitor');\n const data = (await response.json()) as Visitor;\n\n // Setters get the server's distinct_id — the person the visitor now belongs to — not anything\n // the client could have said about it.\n const identity = { ...body, distinct_id: data.distinct_id ?? null };\n config.thirdPartyUserSetters.forEach((setter) => {\n try {\n setter(identity);\n } catch (e: unknown) {\n // The visitor was updated before this ran, so a third-party setter throwing must not reject\n // a call that already succeeded.\n if (e instanceof Error) console.log(e.message);\n }\n });\n return data;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,SAAgB,YAAoB;CAClC,MAAM,SAASA,oBAAAA,OAAO,QAAQ,QAAQC,0BAAAA,KAAK,UAAU;CACrD,IAAI,UAAU,WAAW,aAAa,OAAO;CAC7C,MAAM,MAAA,GAAKC,KAAAA,GAAAA,CAAO;CAClB,oBAAA,OAAO,QAAQ,QAAQD,0BAAAA,KAAK,YAAY,EAAE;CAC1C,OAAO;AACT;AAEA,eAAsB,WAAW,KAAqC;CACpE,MAAM,KAAK,UAAU;CACrB,MAAM,OAAyB;EAAE,GAAG;EAAK,MAAM,MAAMD,oBAAAA,OAAO,QAAQ;CAAE;CACtE,MAAM,WAAW,OAAA,GAAMG,cAAAA,MAAAA,CAAM,GAAGH,oBAAAA,OAAO,SAAS,YAAY,MAAM;EAChE,QAAQ;EACR,aAAa;EACb,SAAS,MAAMA,oBAAAA,OAAO,WAAW;EACjC,MAAM,KAAK,UAAU,IAAI;CAC3B,CAAC;CAED,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,uBAAuB;CACzD,MAAM,OAAQ,MAAM,SAAS,KAAK;CAIlC,MAAM,WAAW;EAAE,GAAG;EAAM,aAAa,KAAK,eAAe;CAAK;CAClE,oBAAA,OAAO,sBAAsB,SAAS,WAAW;EAC/C,IAAI;GACF,OAAO,QAAQ;EACjB,SAAS,GAAY;GAGnB,IAAI,aAAa,OAAO,QAAQ,IAAI,EAAE,OAAO;EAC/C;CACF,CAAC;CACD,OAAO;AACT"}
@@ -1,7 +1,16 @@
1
1
  import { UpdateVisitorDTO } from "../schema/index.cjs";
2
2
  import { Visitor } from "./types.cjs";
3
3
  //#region src/visitor/index.d.ts
4
- export declare function getVisitor(): Promise<Visitor>;
4
+ /**
5
+ * This visitor's id: generated here on the first visit (uuidv7) and kept in `config.storage`, so
6
+ * it exists the moment the page loads. The first events, and the beacon of a visit left within a
7
+ * second, go out without waiting for a round trip; the server creates the visitor from its first
8
+ * events. An id a server issued to an older client is kept as it is: the visitor continues.
9
+ *
10
+ * Needs a server that creates visitors from events (see the README, "Visitors"); one that still
11
+ * only creates them on `POST /visitors` rejects the events of a new visitor.
12
+ */
13
+ export declare function visitorId(): string;
5
14
  export declare function setVisitor(dto: Omit<UpdateVisitorDTO, 'tags'>): Promise<Visitor>;
6
15
  //#endregion
7
16
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/visitor/index.ts"],"mappings":";;;wBAkEsB,cAAc,QAAQ;wBAetB,WAAW,KAAK,KAAK,4BAAyB,QAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/visitor/index.ts"],"mappings":";;;;;;;;;;;;wBAgBgB;wBAQM,WAAW,KAAK,KAAK,4BAAyB,QAAA"}
@@ -1,7 +1,16 @@
1
1
  import { UpdateVisitorDTO } from "../schema/index.mjs";
2
2
  import { Visitor } from "./types.mjs";
3
3
  //#region src/visitor/index.d.ts
4
- export declare function getVisitor(): Promise<Visitor>;
4
+ /**
5
+ * This visitor's id: generated here on the first visit (uuidv7) and kept in `config.storage`, so
6
+ * it exists the moment the page loads. The first events, and the beacon of a visit left within a
7
+ * second, go out without waiting for a round trip; the server creates the visitor from its first
8
+ * events. An id a server issued to an older client is kept as it is: the visitor continues.
9
+ *
10
+ * Needs a server that creates visitors from events (see the README, "Visitors"); one that still
11
+ * only creates them on `POST /visitors` rejects the events of a new visitor.
12
+ */
13
+ export declare function visitorId(): string;
5
14
  export declare function setVisitor(dto: Omit<UpdateVisitorDTO, 'tags'>): Promise<Visitor>;
6
15
  //#endregion
7
16
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/visitor/index.ts"],"mappings":";;;wBAkEsB,cAAc,QAAQ;wBAetB,WAAW,KAAK,KAAK,4BAAyB,QAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/visitor/index.ts"],"mappings":";;;;;;;;;;;;wBAgBgB;wBAQM,WAAW,KAAK,KAAK,4BAAyB,QAAA"}
@@ -1,61 +1,29 @@
1
- import { cache, config } from "../setup/index.mjs";
1
+ import { config } from "../setup/index.mjs";
2
2
  import { keys } from "../constants/storage.mjs";
3
3
  import { fetch } from "@shware/utils";
4
+ import { v7 } from "uuid";
4
5
  //#region src/visitor/index.ts
5
- async function createVisitor() {
6
- const tags = await config.getTags();
7
- const dto = {
8
- device_id: await config.getDeviceId(),
9
- platform: config.platform,
10
- environment: config.environment,
11
- tags,
12
- properties: tags
13
- };
14
- const response = await fetch(`${config.endpoint}/visitors`, {
15
- method: "POST",
16
- credentials: "include",
17
- headers: await config.getHeaders(),
18
- body: JSON.stringify(dto)
19
- });
20
- if (!response.ok) throw new Error(`Failed to create visitor: ${response.status} ${await response.text()}`);
21
- const data = await response.json();
22
- if (data.id) config.storage.setItem(keys.visitor_id, data.id);
23
- return data;
24
- }
25
- async function getOrCreateVisitor() {
26
- const visitorId = config.storage.getItem(keys.visitor_id);
27
- if (visitorId && visitorId !== "undefined") {
28
- const tags = await config.getTags();
29
- const response = await fetch(`${config.endpoint}/visitors/${visitorId}`, {
30
- method: "PATCH",
31
- credentials: "include",
32
- headers: await config.getHeaders(),
33
- body: JSON.stringify({ tags })
34
- });
35
- if (!response.ok) return createVisitor();
36
- const data = await response.json();
37
- if (data.id) config.storage.setItem(keys.visitor_id, data.id);
38
- return data;
39
- } else return createVisitor();
40
- }
41
- let visitorFetcher = null;
42
- async function getVisitor() {
43
- if (cache.visitor) return cache.visitor;
44
- if (visitorFetcher) return visitorFetcher;
45
- visitorFetcher = getOrCreateVisitor();
46
- try {
47
- cache.visitor = await visitorFetcher;
48
- return cache.visitor;
49
- } finally {
50
- visitorFetcher = null;
51
- }
6
+ /**
7
+ * This visitor's id: generated here on the first visit (uuidv7) and kept in `config.storage`, so
8
+ * it exists the moment the page loads. The first events, and the beacon of a visit left within a
9
+ * second, go out without waiting for a round trip; the server creates the visitor from its first
10
+ * events. An id a server issued to an older client is kept as it is: the visitor continues.
11
+ *
12
+ * Needs a server that creates visitors from events (see the README, "Visitors"); one that still
13
+ * only creates them on `POST /visitors` rejects the events of a new visitor.
14
+ */
15
+ function visitorId() {
16
+ const stored = config.storage.getItem(keys.visitor_id);
17
+ if (stored && stored !== "undefined") return stored;
18
+ const id = v7();
19
+ config.storage.setItem(keys.visitor_id, id);
20
+ return id;
52
21
  }
53
22
  async function setVisitor(dto) {
54
- const { id } = await getVisitor();
55
- const tags = await config.getTags();
23
+ const id = visitorId();
56
24
  const body = {
57
25
  ...dto,
58
- tags
26
+ tags: await config.getTags()
59
27
  };
60
28
  const response = await fetch(`${config.endpoint}/visitors/${id}`, {
61
29
  method: "PATCH",
@@ -76,10 +44,9 @@ async function setVisitor(dto) {
76
44
  if (e instanceof Error) console.log(e.message);
77
45
  }
78
46
  });
79
- cache.visitor = data;
80
47
  return data;
81
48
  }
82
49
  //#endregion
83
- export { getVisitor, setVisitor };
50
+ export { setVisitor, visitorId };
84
51
 
85
52
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/visitor/index.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { keys } from '../constants/storage';\nimport type { CreateVisitorDTO, UpdateVisitorDTO } from '../schema/index';\nimport { cache, config } from '../setup/index';\nimport type { Visitor, VisitorProperties } from './types';\n\nasync function createVisitor(): Promise<Visitor> {\n const tags = await config.getTags();\n const dto: CreateVisitorDTO = {\n device_id: await config.getDeviceId(),\n platform: config.platform,\n environment: config.environment,\n tags,\n properties: tags as VisitorProperties,\n };\n\n const response = await fetch(`${config.endpoint}/visitors`, {\n method: 'POST',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(dto),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create visitor: ${response.status} ${await response.text()}`);\n }\n const data = (await response.json()) as Visitor;\n if (data.id) {\n config.storage.setItem(keys.visitor_id, data.id);\n }\n return data;\n}\n\nasync function getOrCreateVisitor(): Promise<Visitor> {\n const visitorId = config.storage.getItem(keys.visitor_id);\n if (visitorId && visitorId !== 'undefined') {\n // PATCH, not GET: `tags` is the last-touch counterpart to `initial_tags`,\n // and the only thing that ever refreshed it was `setVisitor`, which hosts\n // call when they identify a user. A visitor who never signs in therefore\n // kept the browser, screen, and release captured on their first ever page\n // load — for the rest of their life — leaving `tags` permanently equal to\n // `initial_tags` and the two columns pointless.\n //\n // Costs nothing extra: this replaces the request that was already here.\n const tags = await config.getTags();\n const response = await fetch(`${config.endpoint}/visitors/${visitorId}`, {\n method: 'PATCH',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify({ tags } satisfies UpdateVisitorDTO),\n });\n\n if (!response.ok) return createVisitor();\n const data = (await response.json()) as Visitor;\n\n if (data.id) {\n config.storage.setItem(keys.visitor_id, data.id);\n }\n return data;\n } else {\n return createVisitor();\n }\n}\n\nlet visitorFetcher: Promise<Visitor> | null = null;\n\nexport async function getVisitor(): Promise<Visitor> {\n if (cache.visitor) return cache.visitor;\n if (visitorFetcher) return visitorFetcher;\n visitorFetcher = getOrCreateVisitor();\n try {\n cache.visitor = await visitorFetcher;\n return cache.visitor;\n } finally {\n // In a `finally`, so a rejected attempt is not left in `visitorFetcher` for every later\n // caller to await again: `sendEvents` needs a visitor for every batch, and one failed\n // request would otherwise stop the page from reporting anything until it is reloaded.\n visitorFetcher = null;\n }\n}\n\nexport async function setVisitor(dto: Omit<UpdateVisitorDTO, 'tags'>) {\n const { id } = await getVisitor();\n const tags = await config.getTags();\n const body: UpdateVisitorDTO = { ...dto, tags };\n const response = await fetch(`${config.endpoint}/visitors/${id}`, {\n method: 'PATCH',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(body),\n });\n\n if (!response.ok) throw new Error('Failed to set visitor');\n const data = (await response.json()) as Visitor;\n\n // Setters get the server's distinct_id — the person the visitor now belongs to — not anything\n // the client could have said about it.\n const identity = { ...body, distinct_id: data.distinct_id ?? null };\n config.thirdPartyUserSetters.forEach((setter) => {\n try {\n setter(identity);\n } catch (e: unknown) {\n // The visitor was updated before this ran, so a third-party setter throwing must not skip\n // the cache write below or reject a call that already succeeded.\n if (e instanceof Error) console.log(e.message);\n }\n });\n cache.visitor = data;\n return data;\n}\n"],"mappings":";;;;AAMA,eAAe,gBAAkC;CAC/C,MAAM,OAAO,MAAM,OAAO,QAAQ;CAClC,MAAM,MAAwB;EAC5B,WAAW,MAAM,OAAO,YAAY;EACpC,UAAU,OAAO;EACjB,aAAa,OAAO;EACpB;EACA,YAAY;CACd;CAEA,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,SAAS,YAAY;EAC1D,QAAQ;EACR,aAAa;EACb,SAAS,MAAM,OAAO,WAAW;EACjC,MAAM,KAAK,UAAU,GAAG;CAC1B,CAAC;CAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,6BAA6B,SAAS,OAAO,GAAG,MAAM,SAAS,KAAK,GAAG;CAEzF,MAAM,OAAQ,MAAM,SAAS,KAAK;CAClC,IAAI,KAAK,IACP,OAAO,QAAQ,QAAQ,KAAK,YAAY,KAAK,EAAE;CAEjD,OAAO;AACT;AAEA,eAAe,qBAAuC;CACpD,MAAM,YAAY,OAAO,QAAQ,QAAQ,KAAK,UAAU;CACxD,IAAI,aAAa,cAAc,aAAa;EAS1C,MAAM,OAAO,MAAM,OAAO,QAAQ;EAClC,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,SAAS,YAAY,aAAa;GACvE,QAAQ;GACR,aAAa;GACb,SAAS,MAAM,OAAO,WAAW;GACjC,MAAM,KAAK,UAAU,EAAE,KAAK,CAA4B;EAC1D,CAAC;EAED,IAAI,CAAC,SAAS,IAAI,OAAO,cAAc;EACvC,MAAM,OAAQ,MAAM,SAAS,KAAK;EAElC,IAAI,KAAK,IACP,OAAO,QAAQ,QAAQ,KAAK,YAAY,KAAK,EAAE;EAEjD,OAAO;CACT,OACE,OAAO,cAAc;AAEzB;AAEA,IAAI,iBAA0C;AAE9C,eAAsB,aAA+B;CACnD,IAAI,MAAM,SAAS,OAAO,MAAM;CAChC,IAAI,gBAAgB,OAAO;CAC3B,iBAAiB,mBAAmB;CACpC,IAAI;EACF,MAAM,UAAU,MAAM;EACtB,OAAO,MAAM;CACf,UAAU;EAIR,iBAAiB;CACnB;AACF;AAEA,eAAsB,WAAW,KAAqC;CACpE,MAAM,EAAE,OAAO,MAAM,WAAW;CAChC,MAAM,OAAO,MAAM,OAAO,QAAQ;CAClC,MAAM,OAAyB;EAAE,GAAG;EAAK;CAAK;CAC9C,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,SAAS,YAAY,MAAM;EAChE,QAAQ;EACR,aAAa;EACb,SAAS,MAAM,OAAO,WAAW;EACjC,MAAM,KAAK,UAAU,IAAI;CAC3B,CAAC;CAED,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,uBAAuB;CACzD,MAAM,OAAQ,MAAM,SAAS,KAAK;CAIlC,MAAM,WAAW;EAAE,GAAG;EAAM,aAAa,KAAK,eAAe;CAAK;CAClE,OAAO,sBAAsB,SAAS,WAAW;EAC/C,IAAI;GACF,OAAO,QAAQ;EACjB,SAAS,GAAY;GAGnB,IAAI,aAAa,OAAO,QAAQ,IAAI,EAAE,OAAO;EAC/C;CACF,CAAC;CACD,MAAM,UAAU;CAChB,OAAO;AACT"}
1
+ {"version":3,"file":"index.mjs","names":["uuidv7"],"sources":["../../src/visitor/index.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { v7 as uuidv7 } from 'uuid';\nimport { keys } from '../constants/storage';\nimport type { UpdateVisitorDTO } from '../schema/index';\nimport { config } from '../setup/index';\nimport type { Visitor } from './types';\n\n/**\n * This visitor's id: generated here on the first visit (uuidv7) and kept in `config.storage`, so\n * it exists the moment the page loads. The first events, and the beacon of a visit left within a\n * second, go out without waiting for a round trip; the server creates the visitor from its first\n * events. An id a server issued to an older client is kept as it is: the visitor continues.\n *\n * Needs a server that creates visitors from events (see the README, \"Visitors\"); one that still\n * only creates them on `POST /visitors` rejects the events of a new visitor.\n */\nexport function visitorId(): string {\n const stored = config.storage.getItem(keys.visitor_id);\n if (stored && stored !== 'undefined') return stored;\n const id = uuidv7();\n config.storage.setItem(keys.visitor_id, id);\n return id;\n}\n\nexport async function setVisitor(dto: Omit<UpdateVisitorDTO, 'tags'>) {\n const id = visitorId();\n const body: UpdateVisitorDTO = { ...dto, tags: await config.getTags() };\n const response = await fetch(`${config.endpoint}/visitors/${id}`, {\n method: 'PATCH',\n credentials: 'include',\n headers: await config.getHeaders(),\n body: JSON.stringify(body),\n });\n\n if (!response.ok) throw new Error('Failed to set visitor');\n const data = (await response.json()) as Visitor;\n\n // Setters get the server's distinct_id — the person the visitor now belongs to — not anything\n // the client could have said about it.\n const identity = { ...body, distinct_id: data.distinct_id ?? null };\n config.thirdPartyUserSetters.forEach((setter) => {\n try {\n setter(identity);\n } catch (e: unknown) {\n // The visitor was updated before this ran, so a third-party setter throwing must not reject\n // a call that already succeeded.\n if (e instanceof Error) console.log(e.message);\n }\n });\n return data;\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,SAAgB,YAAoB;CAClC,MAAM,SAAS,OAAO,QAAQ,QAAQ,KAAK,UAAU;CACrD,IAAI,UAAU,WAAW,aAAa,OAAO;CAC7C,MAAM,KAAKA,GAAO;CAClB,OAAO,QAAQ,QAAQ,KAAK,YAAY,EAAE;CAC1C,OAAO;AACT;AAEA,eAAsB,WAAW,KAAqC;CACpE,MAAM,KAAK,UAAU;CACrB,MAAM,OAAyB;EAAE,GAAG;EAAK,MAAM,MAAM,OAAO,QAAQ;CAAE;CACtE,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,SAAS,YAAY,MAAM;EAChE,QAAQ;EACR,aAAa;EACb,SAAS,MAAM,OAAO,WAAW;EACjC,MAAM,KAAK,UAAU,IAAI;CAC3B,CAAC;CAED,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,uBAAuB;CACzD,MAAM,OAAQ,MAAM,SAAS,KAAK;CAIlC,MAAM,WAAW;EAAE,GAAG;EAAM,aAAa,KAAK,eAAe;CAAK;CAClE,OAAO,sBAAsB,SAAS,WAAW;EAC/C,IAAI;GACF,OAAO,QAAQ;EACjB,SAAS,GAAY;GAGnB,IAAI,aAAa,OAAO,QAAQ,IAAI,EAAE,OAAO;EAC/C;CACF,CAAC;CACD,OAAO;AACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shware/analytics",
3
- "version": "10.3.0",
3
+ "version": "11.0.1",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",