@farming-labs/theme 0.1.135 → 0.1.137

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.
@@ -1,15 +1,105 @@
1
1
  "use client";
2
2
 
3
3
  //#region src/client-analytics.ts
4
+ const VISITOR_ID_STORAGE_KEY = "fd:analytics:visitor-id";
5
+ const SESSION_ID_STORAGE_KEY = "fd:analytics:session-id";
6
+ function createAnalyticsId(prefix) {
7
+ return `${prefix}_${typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 12)}`}`;
8
+ }
9
+ function readStorage(storage, key) {
10
+ try {
11
+ return storage?.getItem(key)?.trim() || void 0;
12
+ } catch {
13
+ return;
14
+ }
15
+ }
16
+ function writeStorage(storage, key, value) {
17
+ try {
18
+ storage?.setItem(key, value);
19
+ } catch {}
20
+ }
21
+ function getBrowserStorage(target, key) {
22
+ try {
23
+ return target[key];
24
+ } catch {
25
+ return;
26
+ }
27
+ }
28
+ function getOrCreateClientId({ cachedValue, create, storage, storageKey }) {
29
+ const stored = readStorage(storage, storageKey);
30
+ const value = stored ?? cachedValue ?? create();
31
+ if (!stored) writeStorage(storage, storageKey, value);
32
+ return value;
33
+ }
34
+ function getDocsClientAnalyticsIdentity() {
35
+ if (typeof window === "undefined") return null;
36
+ const target = window;
37
+ const visitorId = getOrCreateClientId({
38
+ cachedValue: target.__fdAnalyticsVisitorId__,
39
+ create: () => createAnalyticsId("visitor"),
40
+ storage: getBrowserStorage(target, "localStorage"),
41
+ storageKey: VISITOR_ID_STORAGE_KEY
42
+ });
43
+ const sessionId = getOrCreateClientId({
44
+ cachedValue: target.__fdAnalyticsSessionId__,
45
+ create: () => createAnalyticsId("session"),
46
+ storage: getBrowserStorage(target, "sessionStorage"),
47
+ storageKey: SESSION_ID_STORAGE_KEY
48
+ });
49
+ target.__fdAnalyticsVisitorId__ = visitorId;
50
+ target.__fdAnalyticsSessionId__ = sessionId;
51
+ return {
52
+ anonymousId: visitorId,
53
+ visitorId,
54
+ sessionId,
55
+ visitor: { id: visitorId },
56
+ session: { id: sessionId }
57
+ };
58
+ }
59
+ function asRecord(value) {
60
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
61
+ }
62
+ function asString(value) {
63
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
64
+ }
65
+ function normalizeClientAnalyticsProperties(identity, properties) {
66
+ const provided = properties ?? {};
67
+ const visitorId = asString(provided.visitorId) ?? asString(asRecord(provided.visitor).id) ?? asString(provided.anonymousId) ?? identity?.visitorId;
68
+ const sessionId = asString(provided.sessionId) ?? asString(asRecord(provided.session).id) ?? identity?.sessionId;
69
+ const merged = {
70
+ ...identity,
71
+ ...provided
72
+ };
73
+ return {
74
+ ...merged,
75
+ ...visitorId ? {
76
+ anonymousId: asString(provided.anonymousId) ?? visitorId,
77
+ visitorId,
78
+ visitor: {
79
+ ...asRecord(merged.visitor),
80
+ id: visitorId
81
+ }
82
+ } : {},
83
+ ...sessionId ? {
84
+ sessionId,
85
+ session: {
86
+ ...asRecord(merged.session),
87
+ id: sessionId
88
+ }
89
+ } : {}
90
+ };
91
+ }
4
92
  function emitClientAnalyticsEvent(event) {
5
93
  if (typeof window === "undefined") return;
94
+ const identity = getDocsClientAnalyticsIdentity();
6
95
  const normalized = {
7
96
  ...event,
8
97
  source: "client",
9
- path: window.location.pathname,
10
- url: window.location.href,
11
- referrer: document.referrer || void 0,
12
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
98
+ path: event.path ?? window.location.pathname,
99
+ url: event.url ?? window.location.href,
100
+ referrer: event.referrer ?? (document.referrer || void 0),
101
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
102
+ properties: normalizeClientAnalyticsProperties(identity, event.properties)
13
103
  };
14
104
  const target = window;
15
105
  try {
@@ -5,6 +5,7 @@ type CopyHandler = (data: CodeBlockCopyData) => void;
5
5
  type FeedbackHandler = (data: DocsFeedbackData) => void | Promise<void>;
6
6
  type AIActionHandler = (data: DocsAskAIActionData) => void | Promise<void>;
7
7
  type AIFeedbackHandler = (data: DocsAskAIFeedbackData) => void | Promise<void>;
8
+ declare function isDocsClientAnalyticsEnabled(analytics?: boolean | DocsAnalyticsConfig): boolean;
8
9
  declare function DocsClientHooks({
9
10
  onCopyClick,
10
11
  onFeedback,
@@ -19,4 +20,4 @@ declare function DocsClientHooks({
19
20
  analytics?: boolean | DocsAnalyticsConfig;
20
21
  }): null;
21
22
  //#endregion
22
- export { DocsClientHooks };
23
+ export { DocsClientHooks, isDocsClientAnalyticsEnabled };
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { emitClientAnalyticsEvent } from "./client-analytics.mjs";
4
4
  import { useEffect } from "react";
5
- import { emitDocsAnalyticsEvent } from "@farming-labs/docs";
5
+ import { emitDocsAnalyticsEvent, resolveDocsAnalyticsConfig } from "@farming-labs/docs";
6
6
 
7
7
  //#region src/docs-client-hooks.tsx
8
8
  function useWindowHook(key, handler) {
@@ -18,10 +18,13 @@ function useWindowHook(key, handler) {
18
18
  };
19
19
  }, [handler, key]);
20
20
  }
21
+ function isDocsClientAnalyticsEnabled(analytics) {
22
+ return resolveDocsAnalyticsConfig(analytics).enabled;
23
+ }
21
24
  function useAnalyticsHook(analytics) {
22
25
  useEffect(() => {
23
26
  if (typeof window === "undefined") return;
24
- if (!analytics) return;
27
+ if (!isDocsClientAnalyticsEnabled(analytics)) return;
25
28
  const target = window;
26
29
  const handler = (event) => {
27
30
  emitDocsAnalyticsEvent(analytics, event);
@@ -40,7 +43,7 @@ function useAnalyticsHook(analytics) {
40
43
  function useCodeCopyAnalytics(analytics) {
41
44
  useEffect(() => {
42
45
  if (typeof window === "undefined") return;
43
- if (!analytics) return;
46
+ if (!isDocsClientAnalyticsEnabled(analytics)) return;
44
47
  const handleClick = (event) => {
45
48
  const target = event.target;
46
49
  if (!target.closest?.("button")) return;
@@ -76,4 +79,4 @@ function DocsClientHooks({ onCopyClick, onFeedback, onAIActions, onAIFeedback, a
76
79
  }
77
80
 
78
81
  //#endregion
79
- export { DocsClientHooks };
82
+ export { DocsClientHooks, isDocsClientAnalyticsEnabled };
package/dist/index.d.mts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { CommandGridUIDefaults, commandGrid } from "./command-grid/index.mjs";
2
2
  import { ConcreteUIDefaults, concrete } from "./concrete/index.mjs";
3
3
  import { DefaultUIDefaults, fumadocs } from "./default/index.mjs";
4
+ import { DocsClientHooks } from "./docs-client-hooks.mjs";
4
5
  import { DocsCommandSearch } from "./docs-command-search.mjs";
5
6
  import { createDocsLayout, createDocsMetadata, createPageMetadata } from "./docs-layout.mjs";
6
7
  import { DocsPageClient } from "./docs-page-client.mjs";
7
8
  import { HardlineUIDefaults, hardline } from "./hardline/index.mjs";
8
9
  import { RootProvider } from "./provider.mjs";
9
10
  import { LedgerUIDefaults, ledger } from "./ledger/index.mjs";
10
- import { DocsClientHooks } from "./docs-client-hooks.mjs";
11
11
  import { DocsFeedback, DocsFeedbackProps } from "./docs-feedback.mjs";
12
12
  import { PageActions } from "./page-actions.mjs";
13
13
  import { withLangInUrl } from "./i18n.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.1.135",
3
+ "version": "0.1.137",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -139,7 +139,7 @@
139
139
  "tsdown": "^0.20.3",
140
140
  "typescript": "^5.9.3",
141
141
  "vitest": "^3.2.4",
142
- "@farming-labs/docs": "0.1.135"
142
+ "@farming-labs/docs": "0.1.137"
143
143
  },
144
144
  "peerDependencies": {
145
145
  "@farming-labs/docs": ">=0.0.1",