@genn-inc/cluebase-frontend-sdk 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 (140) hide show
  1. package/README.md +222 -0
  2. package/dist/_generated/correlation-attribute-keys.d.ts +2 -0
  3. package/dist/_generated/correlation-attribute-keys.js +33 -0
  4. package/dist/_generated/identity-pii-policy.d.ts +38 -0
  5. package/dist/_generated/identity-pii-policy.js +103 -0
  6. package/dist/_generated/ingest-privacy-tables.d.ts +120 -0
  7. package/dist/_generated/ingest-privacy-tables.js +890 -0
  8. package/dist/_generated/otel-resource-keys.d.ts +21 -0
  9. package/dist/_generated/otel-resource-keys.js +72 -0
  10. package/dist/_generated/otel-span-conversion.d.ts +48 -0
  11. package/dist/_generated/otel-span-conversion.js +69 -0
  12. package/dist/_generated/parameter-snapshot-leaf.d.ts +11 -0
  13. package/dist/_generated/parameter-snapshot-leaf.js +37 -0
  14. package/dist/_generated/sdk-runtime-policy.d.ts +7 -0
  15. package/dist/_generated/sdk-runtime-policy.js +14 -0
  16. package/dist/_generated/usage-governor-tables.d.ts +15 -0
  17. package/dist/_generated/usage-governor-tables.js +44 -0
  18. package/dist/context/browser-storage.d.ts +15 -0
  19. package/dist/context/browser-storage.js +38 -0
  20. package/dist/context/environment.d.ts +37 -0
  21. package/dist/context/environment.js +229 -0
  22. package/dist/context/identity.d.ts +16 -0
  23. package/dist/context/identity.js +139 -0
  24. package/dist/context/organization.d.ts +16 -0
  25. package/dist/context/organization.js +60 -0
  26. package/dist/context/session.d.ts +28 -0
  27. package/dist/context/session.js +130 -0
  28. package/dist/context/tab.d.ts +22 -0
  29. package/dist/context/tab.js +117 -0
  30. package/dist/context/trace.d.ts +76 -0
  31. package/dist/context/trace.js +163 -0
  32. package/dist/exporter/batch.d.ts +94 -0
  33. package/dist/exporter/batch.js +485 -0
  34. package/dist/exporter/browser-outbox-records.d.ts +159 -0
  35. package/dist/exporter/browser-outbox-records.js +341 -0
  36. package/dist/exporter/browser-outbox.d.ts +112 -0
  37. package/dist/exporter/browser-outbox.js +876 -0
  38. package/dist/exporter/client.d.ts +92 -0
  39. package/dist/exporter/client.js +541 -0
  40. package/dist/exporter/delivery-response-contract.d.ts +103 -0
  41. package/dist/exporter/delivery-response-contract.js +147 -0
  42. package/dist/exporter/delivery-response.d.ts +37 -0
  43. package/dist/exporter/delivery-response.js +151 -0
  44. package/dist/exporter/diagnostics.d.ts +42 -0
  45. package/dist/exporter/diagnostics.js +148 -0
  46. package/dist/exporter/event-id.d.ts +10 -0
  47. package/dist/exporter/event-id.js +75 -0
  48. package/dist/exporter/frontend-resource.d.ts +20 -0
  49. package/dist/exporter/frontend-resource.js +52 -0
  50. package/dist/exporter/local-minimization.d.ts +29 -0
  51. package/dist/exporter/local-minimization.js +87 -0
  52. package/dist/exporter/observation-source-event-exporter.d.ts +69 -0
  53. package/dist/exporter/observation-source-event-exporter.js +154 -0
  54. package/dist/exporter/parameter-snapshot.d.ts +14 -0
  55. package/dist/exporter/parameter-snapshot.js +215 -0
  56. package/dist/exporter/path-template.d.ts +1 -0
  57. package/dist/exporter/path-template.js +67 -0
  58. package/dist/exporter/raw-event-to-source-signal.d.ts +12 -0
  59. package/dist/exporter/raw-event-to-source-signal.js +297 -0
  60. package/dist/exporter/retry.d.ts +8 -0
  61. package/dist/exporter/retry.js +8 -0
  62. package/dist/exporter/sanitize.d.ts +30 -0
  63. package/dist/exporter/sanitize.js +177 -0
  64. package/dist/exporter/schema-evidence.d.ts +20 -0
  65. package/dist/exporter/schema-evidence.js +232 -0
  66. package/dist/exporter/source-event.d.ts +99 -0
  67. package/dist/exporter/source-event.js +13 -0
  68. package/dist/exporter/span-to-source-signal.d.ts +8 -0
  69. package/dist/exporter/span-to-source-signal.js +174 -0
  70. package/dist/exporter/text-fingerprint.d.ts +9 -0
  71. package/dist/exporter/text-fingerprint.js +16 -0
  72. package/dist/index.d.ts +25 -0
  73. package/dist/index.js +158 -0
  74. package/dist/instrumentation/action.d.ts +2 -0
  75. package/dist/instrumentation/action.js +171 -0
  76. package/dist/instrumentation/cluebase-authored-attributes.d.ts +4 -0
  77. package/dist/instrumentation/cluebase-authored-attributes.js +32 -0
  78. package/dist/instrumentation/common.d.ts +73 -0
  79. package/dist/instrumentation/common.js +90 -0
  80. package/dist/instrumentation/dom.d.ts +63 -0
  81. package/dist/instrumentation/dom.js +640 -0
  82. package/dist/instrumentation/drag.d.ts +2 -0
  83. package/dist/instrumentation/drag.js +82 -0
  84. package/dist/instrumentation/error.d.ts +2 -0
  85. package/dist/instrumentation/error.js +318 -0
  86. package/dist/instrumentation/form.d.ts +2 -0
  87. package/dist/instrumentation/form.js +132 -0
  88. package/dist/instrumentation/input.d.ts +2 -0
  89. package/dist/instrumentation/input.js +180 -0
  90. package/dist/instrumentation/location.d.ts +11 -0
  91. package/dist/instrumentation/location.js +69 -0
  92. package/dist/instrumentation/naming-evidence.d.ts +20 -0
  93. package/dist/instrumentation/naming-evidence.js +11 -0
  94. package/dist/instrumentation/navigation.d.ts +2 -0
  95. package/dist/instrumentation/navigation.js +144 -0
  96. package/dist/instrumentation/network-request.d.ts +103 -0
  97. package/dist/instrumentation/network-request.js +285 -0
  98. package/dist/instrumentation/network-span-attributes.d.ts +25 -0
  99. package/dist/instrumentation/network-span-attributes.js +153 -0
  100. package/dist/instrumentation/network.d.ts +16 -0
  101. package/dist/instrumentation/network.js +502 -0
  102. package/dist/instrumentation/page.d.ts +2 -0
  103. package/dist/instrumentation/page.js +56 -0
  104. package/dist/instrumentation/performance.d.ts +2 -0
  105. package/dist/instrumentation/performance.js +365 -0
  106. package/dist/integrations/next-router.d.ts +16 -0
  107. package/dist/integrations/next-router.js +18 -0
  108. package/dist/integrations/react-router.d.ts +7 -0
  109. package/dist/integrations/react-router.js +61 -0
  110. package/dist/internal/internal-log.d.ts +23 -0
  111. package/dist/internal/internal-log.js +47 -0
  112. package/dist/internal/metrics.d.ts +10 -0
  113. package/dist/internal/metrics.js +50 -0
  114. package/dist/provider/_version.d.ts +1 -0
  115. package/dist/provider/_version.js +3 -0
  116. package/dist/provider/config.d.ts +12 -0
  117. package/dist/provider/config.js +177 -0
  118. package/dist/provider/constants.d.ts +62 -0
  119. package/dist/provider/constants.js +118 -0
  120. package/dist/provider/contracts.d.ts +51 -0
  121. package/dist/provider/contracts.js +46 -0
  122. package/dist/provider/privacy-policy-store.d.ts +20 -0
  123. package/dist/provider/privacy-policy-store.js +56 -0
  124. package/dist/provider/sampling.d.ts +2 -0
  125. package/dist/provider/sampling.js +17 -0
  126. package/dist/provider/sdk-event-pipeline.d.ts +60 -0
  127. package/dist/provider/sdk-event-pipeline.js +478 -0
  128. package/dist/provider/sdk-input-normalization.d.ts +7 -0
  129. package/dist/provider/sdk-input-normalization.js +113 -0
  130. package/dist/provider/sdk-otel-pipeline.d.ts +14 -0
  131. package/dist/provider/sdk-otel-pipeline.js +148 -0
  132. package/dist/provider/sdk-runtime-surfaces.d.ts +49 -0
  133. package/dist/provider/sdk-runtime-surfaces.js +216 -0
  134. package/dist/provider/sdk.d.ts +2 -0
  135. package/dist/provider/sdk.js +614 -0
  136. package/dist/provider/types.d.ts +323 -0
  137. package/dist/provider/types.js +1 -0
  138. package/dist/provider/usage-governor.d.ts +7 -0
  139. package/dist/provider/usage-governor.js +116 -0
  140. package/package.json +59 -0
@@ -0,0 +1,177 @@
1
+ import { ALWAYS_DENIED_KEYS, DEFAULT_CONFIG, DEFAULT_FRONTEND_SOURCE_IDENTIFIER, MAX_DIAGNOSTIC_TTL_MS, } from "./constants.js";
2
+ import { SDK_COLLECTION_MODE } from "./contracts.js";
3
+ const normalizeKeyList = (keys) => {
4
+ if (!keys || keys.length === 0) {
5
+ return [];
6
+ }
7
+ return [
8
+ ...new Set(keys.map((key) => key.trim().toLowerCase()).filter(Boolean)),
9
+ ];
10
+ };
11
+ const clampRate = (value, fallback) => {
12
+ if (typeof value !== "number" || Number.isNaN(value)) {
13
+ return fallback;
14
+ }
15
+ if (value < 0) {
16
+ return 0;
17
+ }
18
+ if (value > 1) {
19
+ return 1;
20
+ }
21
+ return value;
22
+ };
23
+ const positiveIntOrFallback = (value, fallback) => {
24
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
25
+ return fallback;
26
+ }
27
+ return Math.floor(value);
28
+ };
29
+ const normalizeCollectionMode = (value) => {
30
+ if (value === SDK_COLLECTION_MODE.diagnostic) {
31
+ return SDK_COLLECTION_MODE.diagnostic;
32
+ }
33
+ return SDK_COLLECTION_MODE.standard;
34
+ };
35
+ const normalizeDiagnosticScope = (value) => {
36
+ if (!value) {
37
+ return {};
38
+ }
39
+ const entries = Object.entries(value)
40
+ .map(([key, scopeValue]) => [key.trim(), scopeValue])
41
+ .filter(([key]) => key.length > 0)
42
+ .filter(([, scopeValue]) => {
43
+ return (scopeValue === null ||
44
+ typeof scopeValue === "string" ||
45
+ typeof scopeValue === "number" ||
46
+ typeof scopeValue === "boolean");
47
+ })
48
+ .slice(0, 20);
49
+ return Object.fromEntries(entries);
50
+ };
51
+ const normalizeDiagnosticConfig = (value) => {
52
+ const ttlMs = Math.min(positiveIntOrFallback(value?.ttlMs, DEFAULT_CONFIG.collection.diagnostic.ttlMs), MAX_DIAGNOSTIC_TTL_MS);
53
+ const budgetWindowMs = positiveIntOrFallback(value?.budgetWindowMs, DEFAULT_CONFIG.collection.diagnostic.budgetWindowMs);
54
+ const maxEventsPerWindow = positiveIntOrFallback(value?.maxEventsPerWindow, DEFAULT_CONFIG.collection.diagnostic.maxEventsPerWindow);
55
+ return {
56
+ enabledCollectors: normalizeKeyList(value?.enabledCollectors),
57
+ scope: normalizeDiagnosticScope(value?.scope),
58
+ ttlMs,
59
+ expiresAt: value?.expiresAt?.trim() || null,
60
+ budgetWindowMs,
61
+ maxEventsPerWindow,
62
+ sampleRate: clampRate(value?.sampleRate, DEFAULT_CONFIG.collection.diagnostic.sampleRate),
63
+ reason: value?.reason?.trim() || null,
64
+ enabledBy: value?.enabledBy?.trim() || null,
65
+ };
66
+ };
67
+ // The protocols Cluebase can actually deliver over. Every place that rejects an
68
+ // address reads this one set, so a browser-only scheme cannot be refused here
69
+ // and accepted there.
70
+ const SENDABLE_PROTOCOLS = new Set(["http:", "https:"]);
71
+ /**
72
+ * Read an address Cluebase is able to send to, or `null` when it cannot.
73
+ *
74
+ * `cluebase.init` refuses such an endpoint loudly and `resolveConfig` drops it to
75
+ * `""`; the two reactions differ but the question does not, so neither restates
76
+ * the rule.
77
+ */
78
+ export const parseSendableUrl = (value) => {
79
+ try {
80
+ const parsed = new URL(value.trim());
81
+ return SENDABLE_PROTOCOLS.has(parsed.protocol) ? parsed : null;
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ };
87
+ const normalizeConfiguredEndpoint = (value) => parseSendableUrl(value)?.toString() ?? "";
88
+ const normalizeTracePropagationOrigin = (value) => {
89
+ const trimmed = value.trim();
90
+ if (!trimmed) {
91
+ return null;
92
+ }
93
+ // Unlike an endpoint, an origin that does not parse is kept as written: the
94
+ // caller matches these against request URLs itself.
95
+ try {
96
+ const parsed = new URL(trimmed);
97
+ return SENDABLE_PROTOCOLS.has(parsed.protocol) ? parsed.origin : null;
98
+ }
99
+ catch {
100
+ return trimmed;
101
+ }
102
+ };
103
+ // The SDK does not emit a ProjectService environment. The server resolves
104
+ // dev/prod from the `projectKey` prefix and ProjectService.
105
+ export const resolveConfig = (options) => {
106
+ const originalEndpoint = normalizeConfiguredEndpoint(options.endpoint);
107
+ const customDenied = normalizeKeyList(options.privacy?.deniedKeys);
108
+ const deniedKeys = [...new Set([...ALWAYS_DENIED_KEYS, ...customDenied])];
109
+ const tracePropagationOrigins = [
110
+ ...new Set((options.network?.tracePropagationOrigins ?? [])
111
+ .map(normalizeTracePropagationOrigin)
112
+ .filter((origin) => Boolean(origin))),
113
+ ];
114
+ return {
115
+ endpoint: originalEndpoint,
116
+ originalEndpoint,
117
+ localSetupCheckEndpoint: null,
118
+ projectKey: options.projectKey?.trim() ?? "",
119
+ producerId: DEFAULT_FRONTEND_SOURCE_IDENTIFIER,
120
+ transportProfile: DEFAULT_CONFIG.transportProfile,
121
+ autocapture: options.autocapture ?? DEFAULT_CONFIG.autocapture,
122
+ captureNetwork: options.captureNetwork ?? DEFAULT_CONFIG.captureNetwork,
123
+ captureErrors: options.captureErrors ?? DEFAULT_CONFIG.captureErrors,
124
+ capturePerformance: options.capturePerformance ?? DEFAULT_CONFIG.capturePerformance,
125
+ network: {
126
+ tracePropagationOrigins,
127
+ },
128
+ batch: {
129
+ enabled: options.batch?.enabled ?? DEFAULT_CONFIG.batch.enabled,
130
+ maxSize: positiveIntOrFallback(options.batch?.maxSize, DEFAULT_CONFIG.batch.maxSize),
131
+ flushIntervalMs: positiveIntOrFallback(options.batch?.flushIntervalMs, DEFAULT_CONFIG.batch.flushIntervalMs),
132
+ maxEventBytes: positiveIntOrFallback(options.batch?.maxEventBytes, DEFAULT_CONFIG.batch.maxEventBytes),
133
+ maxPayloadBytes: positiveIntOrFallback(options.batch?.maxPayloadBytes, DEFAULT_CONFIG.batch.maxPayloadBytes),
134
+ },
135
+ privacy: {
136
+ deniedKeys,
137
+ },
138
+ sampling: {
139
+ sessionSampleRate: clampRate(options.sampling?.sessionSampleRate, DEFAULT_CONFIG.sampling.sessionSampleRate),
140
+ },
141
+ collection: {
142
+ mode: normalizeCollectionMode(undefined),
143
+ diagnostic: normalizeDiagnosticConfig(undefined),
144
+ },
145
+ release: {
146
+ frontendRelease: options.release?.frontendRelease?.trim() ||
147
+ DEFAULT_CONFIG.release.frontendRelease,
148
+ },
149
+ contextTraitsProvider: options.contextTraitsProvider,
150
+ otelProviderMode: options.otelProviderMode ?? "detect",
151
+ };
152
+ };
153
+ export const validateConfig = (config) => {
154
+ const issues = [];
155
+ if (!config.endpoint) {
156
+ issues.push("endpoint is required");
157
+ }
158
+ if (!config.projectKey) {
159
+ issues.push("projectKey is required");
160
+ }
161
+ if (config.batch.flushIntervalMs < 100) {
162
+ issues.push("batch.flushIntervalMs must be >= 100ms");
163
+ }
164
+ if (config.batch.maxSize < 1) {
165
+ issues.push("batch.maxSize must be >= 1");
166
+ }
167
+ if (config.batch.maxEventBytes < 512) {
168
+ issues.push("batch.maxEventBytes must be >= 512 bytes");
169
+ }
170
+ if (config.batch.maxPayloadBytes < config.batch.maxEventBytes) {
171
+ issues.push("batch.maxPayloadBytes must be >= batch.maxEventBytes");
172
+ }
173
+ return issues;
174
+ };
175
+ export const isConfigValid = (config) => {
176
+ return validateConfig(config).length === 0;
177
+ };
@@ -0,0 +1,62 @@
1
+ import type { CluebaseConsentState, ResolvedCluebaseConfig } from "./types.js";
2
+ import { SDK_VERSION } from "./_version.js";
3
+ export { SDK_VERSION };
4
+ export declare const SDK_NAME = "@genn-inc/cluebase-frontend-sdk";
5
+ export declare const EVENT_SOURCE: "frontend_sdk";
6
+ export declare const EVENT_VERSION = 1;
7
+ export declare const SOURCE_SCHEMA_VERSION = "1";
8
+ export declare const SURFACE_TYPE: "web";
9
+ export declare const DEFAULT_FRONTEND_SOURCE_IDENTIFIER = "frontend";
10
+ export declare const MASKED_VALUE = "__cluebase_masked__";
11
+ export declare const FORBIDDEN_PARAMETER_VALUE = "__forbidden__";
12
+ export declare const TRUNCATED_PARAMETER_VALUE = "__truncated__";
13
+ export declare const DEFAULT_CONSENT: CluebaseConsentState;
14
+ export declare const CONSENT_DENIED_ABORT_REASON = "consent_denied";
15
+ export declare const DEFAULT_SESSION_TIMEOUT_MS: number;
16
+ export declare const DEFAULT_LIFECYCLE_INTERACTION_GRACE_WINDOW_MS = 30000;
17
+ export declare const DEFAULT_MAX_EVENT_PAYLOAD_BYTES: number;
18
+ export declare const DEFAULT_BATCH_FLUSH_THRESHOLD_BYTES: number;
19
+ export declare const DEFAULT_MAX_BATCH_PAYLOAD_BYTES: number;
20
+ export declare const DEFAULT_MAX_BATCH_QUEUE_BYTES: number;
21
+ export declare const DEFAULT_MAX_BATCH_QUEUE_ITEMS = 5000;
22
+ export declare const DEFAULT_DIAGNOSTIC_TTL_MS = 600000;
23
+ export declare const MAX_DIAGNOSTIC_TTL_MS = 1800000;
24
+ export declare const DEFAULT_DIAGNOSTIC_BUDGET_WINDOW_MS = 60000;
25
+ export declare const DEFAULT_DIAGNOSTIC_MAX_EVENTS_PER_WINDOW = 100;
26
+ export declare const STORAGE_KEYS: {
27
+ readonly anonymousId: "cluebase:anon_id";
28
+ readonly userId: "cluebase:user_id";
29
+ readonly organizationId: "cluebase:organization_id";
30
+ readonly tabId: "cluebase:tab_id";
31
+ readonly sessionId: "cluebase:session_id";
32
+ readonly sessionStartedAt: "cluebase:session_started_at";
33
+ readonly sessionLastActivityAt: "cluebase:session_last_activity_at";
34
+ };
35
+ /**
36
+ * SDK-side denied keys. Stripped from event properties / metrics /
37
+ * traits / headers / body fragments before the value is set as an OTel
38
+ * span attribute or attached to a source-signal envelope.
39
+ *
40
+ * This list is a strict superset of the server-side hard-deny list
41
+ * (`INGEST_HARD_DENY_KEYS`) **by construction**: both are emitted from the
42
+ * same canonical data file,
43
+ * `packages/cluebase-shared/src/ingest/ingest-privacy-policy.v1.json`.
44
+ * It is not a hand-kept copy asserted by a parity test any more.
45
+ *
46
+ * `@cluebase/shared` is `private: true` and the published SDK is built with
47
+ * `tsc` alone (no bundling), so the SDK cannot import it at customer
48
+ * runtime. The shared-contract generator therefore writes the table into
49
+ * this package instead; the generated file's banner names the command to
50
+ * re-run. Do not re-declare the list, or the command, here.
51
+ */
52
+ export declare const ALWAYS_DENIED_KEYS: readonly string[];
53
+ export declare const MAX_SCHEMA_DEPTH = 6;
54
+ export declare const MAX_SCHEMA_KEYS = 512;
55
+ export declare const MAX_SANITIZE_DEPTH = 20;
56
+ export declare const DEFAULT_CONFIG: ResolvedCluebaseConfig;
57
+ export declare const RETRY_CONFIG: {
58
+ readonly maxAttempts: 3;
59
+ readonly baseDelayMs: 300;
60
+ readonly maxDelayMs: 5000;
61
+ readonly maxAdditiveJitterMs: 200;
62
+ };
@@ -0,0 +1,118 @@
1
+ import { SDK_ALWAYS_DENIED_KEYS } from "../_generated/ingest-privacy-tables.js";
2
+ import { SDK_RETRY_POLICY } from "../_generated/sdk-runtime-policy.js";
3
+ import { SDK_VERSION } from "./_version.js";
4
+ // SDK_VERSION SoT is `package.json` `version`, generated into
5
+ // `_version.ts` by `scripts/generate-version-module.mjs` at build time.
6
+ // Re-export it here so call sites keep the existing import path.
7
+ export { SDK_VERSION };
8
+ export const SDK_NAME = "@genn-inc/cluebase-frontend-sdk";
9
+ export const EVENT_SOURCE = "frontend_sdk";
10
+ export const EVENT_VERSION = 1;
11
+ export const SOURCE_SCHEMA_VERSION = "1";
12
+ export const SURFACE_TYPE = "web";
13
+ export const DEFAULT_FRONTEND_SOURCE_IDENTIFIER = "frontend";
14
+ // Canonical mask sentinel — matches Node SDK / Python SDK / `@cluebase/shared`
15
+ // (`__cluebase_masked__`). Aligning across the 3 SDKs lets downstream analytics
16
+ // queries and redaction UIs detect masked values by a single string literal.
17
+ export const MASKED_VALUE = "__cluebase_masked__";
18
+ export const FORBIDDEN_PARAMETER_VALUE = "__forbidden__";
19
+ export const TRUNCATED_PARAMETER_VALUE = "__truncated__";
20
+ export const DEFAULT_CONSENT = "unknown";
21
+ // 送信中の取りやめが「同意の取り消し」だったのかを、中断の理由で見分けます。
22
+ // 立てる側 (`batch.ts`) と読む側 (`client.ts`) が別の module なので、字面を
23
+ // 両方へ書くと片方だけずれても誰も気づきません。**ずれると取り消し後の束が
24
+ // 破棄ではなく再送**になり、利用者が拒否した後の観測が顧客のサイトから出ます。
25
+ // 正本を 1 つにして、両端がここを読みます (AGENTS §0.2)。
26
+ export const CONSENT_DENIED_ABORT_REASON = "consent_denied";
27
+ export const DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1000;
28
+ export const DEFAULT_LIFECYCLE_INTERACTION_GRACE_WINDOW_MS = 30000;
29
+ export const DEFAULT_MAX_EVENT_PAYLOAD_BYTES = 48 * 1024;
30
+ export const DEFAULT_BATCH_FLUSH_THRESHOLD_BYTES = 48 * 1024;
31
+ export const DEFAULT_MAX_BATCH_PAYLOAD_BYTES = 60 * 1024;
32
+ export const DEFAULT_MAX_BATCH_QUEUE_BYTES = 300 * 1024;
33
+ // EventQueue の保持件数上限。 maxBytes (300KB) と二重にガードして OOM を防ぐ。
34
+ // queueMaxBytes は payload size 起点で削るが、件数ベースの暴走 (極小 event の連射等)
35
+ // を捕捉するために件数上限も併設する。
36
+ export const DEFAULT_MAX_BATCH_QUEUE_ITEMS = 5000;
37
+ export const DEFAULT_DIAGNOSTIC_TTL_MS = 600000;
38
+ export const MAX_DIAGNOSTIC_TTL_MS = 1800000;
39
+ export const DEFAULT_DIAGNOSTIC_BUDGET_WINDOW_MS = 60000;
40
+ export const DEFAULT_DIAGNOSTIC_MAX_EVENTS_PER_WINDOW = 100;
41
+ export const STORAGE_KEYS = {
42
+ anonymousId: "cluebase:anon_id",
43
+ userId: "cluebase:user_id",
44
+ organizationId: "cluebase:organization_id",
45
+ tabId: "cluebase:tab_id",
46
+ sessionId: "cluebase:session_id",
47
+ sessionStartedAt: "cluebase:session_started_at",
48
+ sessionLastActivityAt: "cluebase:session_last_activity_at",
49
+ };
50
+ /**
51
+ * SDK-side denied keys. Stripped from event properties / metrics /
52
+ * traits / headers / body fragments before the value is set as an OTel
53
+ * span attribute or attached to a source-signal envelope.
54
+ *
55
+ * This list is a strict superset of the server-side hard-deny list
56
+ * (`INGEST_HARD_DENY_KEYS`) **by construction**: both are emitted from the
57
+ * same canonical data file,
58
+ * `packages/cluebase-shared/src/ingest/ingest-privacy-policy.v1.json`.
59
+ * It is not a hand-kept copy asserted by a parity test any more.
60
+ *
61
+ * `@cluebase/shared` is `private: true` and the published SDK is built with
62
+ * `tsc` alone (no bundling), so the SDK cannot import it at customer
63
+ * runtime. The shared-contract generator therefore writes the table into
64
+ * this package instead; the generated file's banner names the command to
65
+ * re-run. Do not re-declare the list, or the command, here.
66
+ */
67
+ export const ALWAYS_DENIED_KEYS = SDK_ALWAYS_DENIED_KEYS;
68
+ export const MAX_SCHEMA_DEPTH = 6;
69
+ export const MAX_SCHEMA_KEYS = 512;
70
+ export const MAX_SANITIZE_DEPTH = 20;
71
+ export const DEFAULT_CONFIG = {
72
+ endpoint: "",
73
+ originalEndpoint: "",
74
+ localSetupCheckEndpoint: null,
75
+ projectKey: "",
76
+ producerId: DEFAULT_FRONTEND_SOURCE_IDENTIFIER,
77
+ transportProfile: "standard",
78
+ autocapture: true,
79
+ captureNetwork: true,
80
+ captureErrors: true,
81
+ capturePerformance: true,
82
+ network: {
83
+ tracePropagationOrigins: [],
84
+ },
85
+ batch: {
86
+ enabled: true,
87
+ maxSize: 50,
88
+ flushIntervalMs: 180000,
89
+ maxEventBytes: DEFAULT_MAX_EVENT_PAYLOAD_BYTES,
90
+ maxPayloadBytes: DEFAULT_MAX_BATCH_PAYLOAD_BYTES,
91
+ },
92
+ privacy: {
93
+ deniedKeys: [...ALWAYS_DENIED_KEYS],
94
+ },
95
+ sampling: {
96
+ sessionSampleRate: 1,
97
+ },
98
+ collection: {
99
+ mode: "standard",
100
+ diagnostic: {
101
+ enabledCollectors: [],
102
+ scope: {},
103
+ ttlMs: DEFAULT_DIAGNOSTIC_TTL_MS,
104
+ expiresAt: null,
105
+ budgetWindowMs: DEFAULT_DIAGNOSTIC_BUDGET_WINDOW_MS,
106
+ maxEventsPerWindow: DEFAULT_DIAGNOSTIC_MAX_EVENTS_PER_WINDOW,
107
+ sampleRate: 1,
108
+ reason: null,
109
+ enabledBy: null,
110
+ },
111
+ },
112
+ release: {
113
+ frontendRelease: SDK_VERSION,
114
+ },
115
+ contextTraitsProvider: undefined,
116
+ otelProviderMode: "detect",
117
+ };
118
+ export const RETRY_CONFIG = SDK_RETRY_POLICY;
@@ -0,0 +1,51 @@
1
+ export declare const FRONTEND_EVENT_CATEGORY: {
2
+ readonly session: "session";
3
+ readonly navigation: "navigation";
4
+ readonly action: "action";
5
+ readonly interaction: "interaction";
6
+ readonly request: "request";
7
+ readonly error: "error";
8
+ readonly performance: "performance";
9
+ readonly support: "support";
10
+ readonly custom: "custom";
11
+ };
12
+ export type FrontendEventCategory = (typeof FRONTEND_EVENT_CATEGORY)[keyof typeof FRONTEND_EVENT_CATEGORY];
13
+ export declare const FRONTEND_EVENT_STATUS: {
14
+ readonly observed: "observed";
15
+ readonly finished: "finished";
16
+ readonly failed: "failed";
17
+ };
18
+ export type FrontendEventStatus = (typeof FRONTEND_EVENT_STATUS)[keyof typeof FRONTEND_EVENT_STATUS];
19
+ export declare const SDK_COLLECTION_MODE: {
20
+ readonly standard: "standard";
21
+ readonly diagnostic: "diagnostic";
22
+ };
23
+ export type SdkCollectionMode = (typeof SDK_COLLECTION_MODE)[keyof typeof SDK_COLLECTION_MODE];
24
+ export declare const FRONTEND_EVENT_NAME: {
25
+ readonly sessionStarted: "session_started";
26
+ readonly sessionEnded: "session_ended";
27
+ readonly pageView: "page_view";
28
+ readonly pageLeave: "page_leave";
29
+ readonly routeChange: "route_change";
30
+ readonly elementClicked: "element_clicked";
31
+ readonly formSubmitted: "form_submitted";
32
+ readonly inputCommitted: "input_committed";
33
+ readonly toggleChanged: "toggle_changed";
34
+ readonly selectionCommitted: "selection_committed";
35
+ readonly fileSelected: "file_selected";
36
+ readonly dragDropCompleted: "drag_drop_completed";
37
+ readonly requestStarted: "request_started";
38
+ readonly requestFinished: "request_finished";
39
+ readonly requestFailed: "request_failed";
40
+ readonly frontendError: "frontend_error";
41
+ readonly unhandledRejection: "unhandled_rejection";
42
+ readonly performanceSummaryObserved: "performance_summary_observed";
43
+ readonly customEmitted: "custom_emitted";
44
+ };
45
+ export type FrontendEventName = (typeof FRONTEND_EVENT_NAME)[keyof typeof FRONTEND_EVENT_NAME];
46
+ export declare const PARAMETER_CAPTURE_MODE: {
47
+ readonly maskedFingerprint: "masked_fingerprint";
48
+ readonly forbidden: "forbidden";
49
+ readonly truncated: "truncated";
50
+ };
51
+ export type ParameterCaptureMode = (typeof PARAMETER_CAPTURE_MODE)[keyof typeof PARAMETER_CAPTURE_MODE];
@@ -0,0 +1,46 @@
1
+ export const FRONTEND_EVENT_CATEGORY = {
2
+ session: "session",
3
+ navigation: "navigation",
4
+ action: "action",
5
+ interaction: "interaction",
6
+ request: "request",
7
+ error: "error",
8
+ performance: "performance",
9
+ support: "support",
10
+ custom: "custom",
11
+ };
12
+ export const FRONTEND_EVENT_STATUS = {
13
+ observed: "observed",
14
+ finished: "finished",
15
+ failed: "failed",
16
+ };
17
+ export const SDK_COLLECTION_MODE = {
18
+ standard: "standard",
19
+ diagnostic: "diagnostic",
20
+ };
21
+ export const FRONTEND_EVENT_NAME = {
22
+ sessionStarted: "session_started",
23
+ sessionEnded: "session_ended",
24
+ pageView: "page_view",
25
+ pageLeave: "page_leave",
26
+ routeChange: "route_change",
27
+ elementClicked: "element_clicked",
28
+ formSubmitted: "form_submitted",
29
+ inputCommitted: "input_committed",
30
+ toggleChanged: "toggle_changed",
31
+ selectionCommitted: "selection_committed",
32
+ fileSelected: "file_selected",
33
+ dragDropCompleted: "drag_drop_completed",
34
+ requestStarted: "request_started",
35
+ requestFinished: "request_finished",
36
+ requestFailed: "request_failed",
37
+ frontendError: "frontend_error",
38
+ unhandledRejection: "unhandled_rejection",
39
+ performanceSummaryObserved: "performance_summary_observed",
40
+ customEmitted: "custom_emitted",
41
+ };
42
+ export const PARAMETER_CAPTURE_MODE = {
43
+ maskedFingerprint: "masked_fingerprint",
44
+ forbidden: "forbidden",
45
+ truncated: "truncated",
46
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 届いた方針を適用する。**契約に合わない形は捨てて、伏せた側へ倒れます。**
3
+ * 途中まで読めた分を活かすと、顧客が決めていない項目を平文にし得ます。
4
+ */
5
+ export declare const applyDeliveredPrivacyPolicy: (policy: unknown) => void;
6
+ /** まだ何も適用していない状態へ戻す。= 全部伏せる。 */
7
+ export declare const resetPrivacyPolicy: () => void;
8
+ /** 塊の要求に載せる合印。持っていなければ載せない。 */
9
+ export declare const appliedPrivacyPolicyToken: () => string | undefined;
10
+ /**
11
+ * この入口・この向きで、顧客が平文にした項目名。
12
+ *
13
+ * **入口が決まらない時は空を返します。**曖昧に当てると、顧客が 1 つの入口について
14
+ * 決めたことが、関係のない通信まで平文にします。
15
+ */
16
+ export declare const allowedValuePathsFor: (input: {
17
+ method: string | null | undefined;
18
+ pathTemplate: string | null | undefined;
19
+ direction: "request" | "response";
20
+ }) => readonly string[];
@@ -0,0 +1,56 @@
1
+ import { ingestPrivacyPolicySchema, } from "../exporter/delivery-response-contract.js";
2
+ const policyKey = (input) => `${input.direction} ${input.method.toUpperCase()} ${input.pathTemplate}`;
3
+ let appliedToken = null;
4
+ let allowedPathsByEntrance = new Map();
5
+ /**
6
+ * 届いた方針を適用する。**契約に合わない形は捨てて、伏せた側へ倒れます。**
7
+ * 途中まで読めた分を活かすと、顧客が決めていない項目を平文にし得ます。
8
+ */
9
+ export const applyDeliveredPrivacyPolicy = (policy) => {
10
+ const parsed = ingestPrivacyPolicySchema.safeParse(policy);
11
+ if (!parsed.success) {
12
+ resetPrivacyPolicy();
13
+ return;
14
+ }
15
+ setPrivacyPolicy(parsed.data);
16
+ };
17
+ const setPrivacyPolicy = (policy) => {
18
+ const next = new Map();
19
+ for (const entry of policy.entries) {
20
+ const key = policyKey({
21
+ method: entry.method,
22
+ pathTemplate: entry.path_template,
23
+ direction: entry.direction,
24
+ });
25
+ const paths = next.get(key);
26
+ if (paths) {
27
+ paths.push(entry.field_path);
28
+ continue;
29
+ }
30
+ next.set(key, [entry.field_path]);
31
+ }
32
+ allowedPathsByEntrance = next;
33
+ appliedToken = policy.token;
34
+ };
35
+ /** まだ何も適用していない状態へ戻す。= 全部伏せる。 */
36
+ export const resetPrivacyPolicy = () => {
37
+ allowedPathsByEntrance = new Map();
38
+ appliedToken = null;
39
+ };
40
+ /** 塊の要求に載せる合印。持っていなければ載せない。 */
41
+ export const appliedPrivacyPolicyToken = () => appliedToken ?? undefined;
42
+ /**
43
+ * この入口・この向きで、顧客が平文にした項目名。
44
+ *
45
+ * **入口が決まらない時は空を返します。**曖昧に当てると、顧客が 1 つの入口について
46
+ * 決めたことが、関係のない通信まで平文にします。
47
+ */
48
+ export const allowedValuePathsFor = (input) => {
49
+ if (!input.method || !input.pathTemplate)
50
+ return [];
51
+ return (allowedPathsByEntrance.get(policyKey({
52
+ method: input.method,
53
+ pathTemplate: input.pathTemplate,
54
+ direction: input.direction,
55
+ })) ?? []);
56
+ };
@@ -0,0 +1,2 @@
1
+ export declare const secureRandomFloat: () => number;
2
+ export declare const shouldSampleSession: (rate: number) => boolean;
@@ -0,0 +1,17 @@
1
+ const UINT32_RANGE = 4294967296;
2
+ export const secureRandomFloat = () => {
3
+ if (typeof globalThis.crypto === "undefined" ||
4
+ typeof globalThis.crypto.getRandomValues !== "function") {
5
+ throw new Error("Cluebase SDK requires crypto.getRandomValues for sampling");
6
+ }
7
+ const values = new Uint32Array(1);
8
+ globalThis.crypto.getRandomValues(values);
9
+ return values[0] / UINT32_RANGE;
10
+ };
11
+ export const shouldSampleSession = (rate) => {
12
+ if (rate >= 1)
13
+ return true;
14
+ if (rate <= 0)
15
+ return false;
16
+ return secureRandomFloat() < rate;
17
+ };
@@ -0,0 +1,60 @@
1
+ import { TraceManager } from "../context/trace.js";
2
+ import type { EnvironmentManager } from "../context/environment.js";
3
+ import type { IdentityManager } from "../context/identity.js";
4
+ import type { OrganizationManager } from "../context/organization.js";
5
+ import type { SessionManager } from "../context/session.js";
6
+ import type { TabManager } from "../context/tab.js";
7
+ import type { BatchTransport } from "../exporter/batch.js";
8
+ import type { InternalMetricsStore } from "../internal/metrics.js";
9
+ import type { CluebaseConsentState, CluebaseContext, CluebaseInternalLogger, IngestUsageGovernorFeedback, RawCapturedEvent, ResolvedCluebaseConfig } from "./types.js";
10
+ export declare const OFFICIAL_CUSTOM_EVENT_NAME: {
11
+ readonly identityIdentified: "identity_identified";
12
+ readonly identityReset: "identity_reset";
13
+ readonly organizationAssociated: "organization_associated";
14
+ readonly sdkInitialized: "sdk_initialized";
15
+ };
16
+ export type OfficialCustomEventProperties = Record<string, string | string[] | number>;
17
+ export declare const readCurrentLifecycleUrl: () => string | null;
18
+ type ProviderEventPipelineRuntime = {
19
+ config: ResolvedCluebaseConfig;
20
+ identityManager: IdentityManager;
21
+ sessionManager: SessionManager;
22
+ tabManager: TabManager;
23
+ organizationManager: OrganizationManager;
24
+ environmentManager: EnvironmentManager;
25
+ traceManager: TraceManager;
26
+ transport: BatchTransport;
27
+ internalMetrics: InternalMetricsStore;
28
+ logger: CluebaseInternalLogger;
29
+ getConsent: () => CluebaseConsentState;
30
+ };
31
+ export declare const getSessionCaptureEligibility: (transport: BatchTransport) => boolean | undefined;
32
+ export declare class ProviderEventPipeline {
33
+ private runtime;
34
+ private currentViewId;
35
+ private usageGovernorFeedback;
36
+ private sessionCaptureEligible;
37
+ private unregisterSessionCaptureEligibility;
38
+ configure(runtime: ProviderEventPipelineRuntime): void;
39
+ clearRuntime(): void;
40
+ resetViewId(): void;
41
+ getViewId(): string | null;
42
+ getObservationContext(): {
43
+ anonymousId: string | null;
44
+ userId: string | null;
45
+ organizationId?: string | null;
46
+ sessionId: string | null;
47
+ tabId: string | null;
48
+ };
49
+ enqueueRawEvent(event: RawCapturedEvent, skipSessionTouch?: boolean): void;
50
+ applyUsageGovernorFeedback(feedback: IngestUsageGovernorFeedback | null | undefined): void;
51
+ refreshSessionCaptureEligibility(): void;
52
+ buildContext(): CluebaseContext;
53
+ private attachInteractionContext;
54
+ private attachCurrentLocation;
55
+ private handleSessionLifecycle;
56
+ private shouldCaptureEvent;
57
+ private measureSerializedBytes;
58
+ private updateActiveTraceSpan;
59
+ }
60
+ export {};