@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,285 @@
1
+ import { buildLocalQuerySummary, buildLocalStructuredSummary, } from "../exporter/local-minimization.js";
2
+ import { buildParameterSnapshotBundle, } from "../exporter/parameter-snapshot.js";
3
+ import { allowedValuePathsFor } from "../provider/privacy-policy-store.js";
4
+ import { toPathTemplate } from "../exporter/path-template.js";
5
+ import { maybeParseJson, sanitizeHeaders } from "../exporter/sanitize.js";
6
+ import { buildSchemaEvidence, deriveOutcomeClass, extractErrorCodeCandidate, extractGraphqlEvidence, } from "../exporter/schema-evidence.js";
7
+ import { currentPageUrl, resolveUrlAgainstCurrentLocation, stripQueryFromUrl, } from "./common.js";
8
+ const MAX_CAPTURED_RESPONSE_BODY_BYTES = 16 * 1024;
9
+ export const headersToRecord = (headers) => {
10
+ if (!headers)
11
+ return {};
12
+ if (headers instanceof Headers) {
13
+ const record = {};
14
+ headers.forEach((value, key) => {
15
+ record[key] = value;
16
+ });
17
+ return record;
18
+ }
19
+ if (Array.isArray(headers)) {
20
+ const record = {};
21
+ for (const [key, value] of headers) {
22
+ record[key] = value;
23
+ }
24
+ return record;
25
+ }
26
+ const record = {};
27
+ for (const [key, value] of Object.entries(headers)) {
28
+ record[key] = String(value);
29
+ }
30
+ return record;
31
+ };
32
+ const normalizeBody = (body) => {
33
+ if (body == null)
34
+ return null;
35
+ if (typeof body === "string")
36
+ return maybeParseJson(body) ?? body;
37
+ if (body instanceof URLSearchParams) {
38
+ const asObject = {};
39
+ body.forEach((value, key) => {
40
+ const current = asObject[key];
41
+ if (Array.isArray(current)) {
42
+ current.push(value);
43
+ }
44
+ else if (typeof current === "string") {
45
+ asObject[key] = [current, value];
46
+ }
47
+ else {
48
+ asObject[key] = value;
49
+ }
50
+ });
51
+ return asObject;
52
+ }
53
+ if (typeof FormData !== "undefined" && body instanceof FormData) {
54
+ const asObject = {};
55
+ body.forEach((value, key) => {
56
+ const normalizedValue = typeof value === "string"
57
+ ? value
58
+ : {
59
+ _type: "file",
60
+ mime_type: value.type || null,
61
+ size: value.size,
62
+ };
63
+ const current = asObject[key];
64
+ if (Array.isArray(current)) {
65
+ current.push(normalizedValue);
66
+ }
67
+ else if (current !== undefined) {
68
+ asObject[key] = [current, normalizedValue];
69
+ }
70
+ else {
71
+ asObject[key] = normalizedValue;
72
+ }
73
+ });
74
+ return asObject;
75
+ }
76
+ if (typeof Blob !== "undefined" && body instanceof Blob) {
77
+ return { _type: "blob", mime_type: body.type || null, size: body.size };
78
+ }
79
+ if (typeof ArrayBuffer !== "undefined" && body instanceof ArrayBuffer) {
80
+ return { _type: "array_buffer", byte_length: body.byteLength };
81
+ }
82
+ if (ArrayBuffer.isView(body)) {
83
+ return { _type: "array_buffer_view", byte_length: body.byteLength };
84
+ }
85
+ if (typeof body === "object")
86
+ return body;
87
+ return null;
88
+ };
89
+ export const buildParameterSource = (input) => {
90
+ const source = {};
91
+ if (input.headers && Object.keys(input.headers).length > 0) {
92
+ source.headers = input.headers;
93
+ }
94
+ if (input.query && Object.keys(input.query).length > 0) {
95
+ source.query = input.query;
96
+ }
97
+ if (input.body !== null && input.body !== undefined) {
98
+ source.body = input.body;
99
+ }
100
+ return source;
101
+ };
102
+ const readQueryValues = (rawUrl) => {
103
+ try {
104
+ const resolvedUrl = resolveUrlAgainstCurrentLocation(rawUrl);
105
+ if (!resolvedUrl)
106
+ return {};
107
+ const queryValues = {};
108
+ resolvedUrl.searchParams.forEach((value, key) => {
109
+ const current = queryValues[key];
110
+ if (current === undefined) {
111
+ queryValues[key] = value;
112
+ }
113
+ else if (Array.isArray(current)) {
114
+ queryValues[key] = [...current, value];
115
+ }
116
+ else {
117
+ queryValues[key] = [current, value];
118
+ }
119
+ });
120
+ return queryValues;
121
+ }
122
+ catch {
123
+ return {};
124
+ }
125
+ };
126
+ const resolveRequestShape = (rawUrl) => {
127
+ const resolvedUrl = resolveUrlAgainstCurrentLocation(rawUrl);
128
+ const path = resolvedUrl?.pathname ?? "/";
129
+ return {
130
+ path,
131
+ pathTemplate: toPathTemplate(path),
132
+ targetService: resolvedUrl?.host ?? null,
133
+ };
134
+ };
135
+ const buildRequestShapeEvidence = (input) => {
136
+ const requestSchemaSource = buildParameterSource({
137
+ query: input.queryValues,
138
+ body: input.normalizedRequestBody,
139
+ });
140
+ const requestSchemaEvidence = buildSchemaEvidence(Object.keys(requestSchemaSource).length > 0 ? requestSchemaSource : null);
141
+ const graphqlEvidence = extractGraphqlEvidence(input.normalizedRequestBody);
142
+ return {
143
+ requestKind: graphqlEvidence.requestKind ?? input.defaultRequestKind,
144
+ requestSchemaHash: requestSchemaEvidence.schemaHash,
145
+ requestFieldPaths: requestSchemaEvidence.fieldPaths,
146
+ requestShapeSize: requestSchemaEvidence.shapeSize,
147
+ graphqlOperationName: graphqlEvidence.graphqlOperationName,
148
+ graphqlOperationType: graphqlEvidence.graphqlOperationType,
149
+ graphqlTopLevelFields: graphqlEvidence.graphqlTopLevelFields,
150
+ };
151
+ };
152
+ export const buildResponseShapeEvidence = (input) => {
153
+ const responseSchemaEvidence = buildSchemaEvidence(input.normalizedResponseBody);
154
+ return {
155
+ outcomeClass: deriveOutcomeClass({
156
+ statusCode: input.statusCode,
157
+ failureType: input.failureType,
158
+ responseBody: input.normalizedResponseBody,
159
+ }),
160
+ errorCodeCandidate: extractErrorCodeCandidate(input.normalizedResponseBody),
161
+ responseSchemaHash: responseSchemaEvidence.schemaHash,
162
+ responseFieldPaths: responseSchemaEvidence.fieldPaths,
163
+ responseShapeSize: responseSchemaEvidence.shapeSize,
164
+ };
165
+ };
166
+ const estimateBodySizeBytes = (body) => {
167
+ if (body == null)
168
+ return undefined;
169
+ if (typeof body === "string")
170
+ return new TextEncoder().encode(body).byteLength;
171
+ if (typeof Blob !== "undefined" && body instanceof Blob)
172
+ return body.size;
173
+ if (typeof ArrayBuffer !== "undefined" && body instanceof ArrayBuffer) {
174
+ return body.byteLength;
175
+ }
176
+ if (ArrayBuffer.isView(body))
177
+ return body.byteLength;
178
+ try {
179
+ return new TextEncoder().encode(JSON.stringify(body)).byteLength;
180
+ }
181
+ catch {
182
+ return undefined;
183
+ }
184
+ };
185
+ export const deriveFailureTypeFromStatus = (status) => {
186
+ if (status >= 500)
187
+ return "server_error";
188
+ if (status >= 400)
189
+ return "client_error";
190
+ return null;
191
+ };
192
+ export const getXhrResponseBody = (xhr) => {
193
+ try {
194
+ const contentLength = xhr.getResponseHeader("content-length");
195
+ if (contentLength &&
196
+ !Number.isNaN(Number(contentLength)) &&
197
+ Number(contentLength) > MAX_CAPTURED_RESPONSE_BODY_BYTES) {
198
+ return null;
199
+ }
200
+ if (xhr.responseType === "" || xhr.responseType === "text") {
201
+ if (new TextEncoder().encode(xhr.responseText).length >
202
+ MAX_CAPTURED_RESPONSE_BODY_BYTES) {
203
+ return null;
204
+ }
205
+ return maybeParseJson(xhr.responseText);
206
+ }
207
+ if (xhr.responseType === "json")
208
+ return xhr.response;
209
+ return null;
210
+ }
211
+ catch {
212
+ return null;
213
+ }
214
+ };
215
+ export const parseXhrHeaders = (rawHeaders) => {
216
+ const headers = {};
217
+ rawHeaders
218
+ .trim()
219
+ .split(/\r?\n/)
220
+ .forEach((line) => {
221
+ const separatorIndex = line.indexOf(":");
222
+ if (separatorIndex <= 0)
223
+ return;
224
+ const key = line.slice(0, separatorIndex).trim();
225
+ const value = line.slice(separatorIndex + 1).trim();
226
+ if (key)
227
+ headers[key] = value;
228
+ });
229
+ return headers;
230
+ };
231
+ export const createRequestMeta = (input) => {
232
+ const traceContext = input.traceContext;
233
+ const queryValues = readQueryValues(input.rawUrl);
234
+ const queryKeys = Object.keys(queryValues);
235
+ const querySummary = buildLocalQuerySummary(queryValues);
236
+ const sanitizedRequestHeaders = sanitizeHeaders(input.requestHeaders);
237
+ const normalizedRequestBody = normalizeBody(input.body);
238
+ const requestShape = resolveRequestShape(input.rawUrl);
239
+ // 顧客の値が入る面。**既定は全部伏せる。**顧客が画面で名指しした項目だけを
240
+ // `allowedPaths` が平文にする。方針がまだ届いていない間は空なので、伏せた側へ倒れる。
241
+ const requestParametersSnapshot = buildParameterSnapshotBundle({ query: queryValues, body: normalizedRequestBody }, {
242
+ allowedPaths: allowedValuePathsFor({
243
+ method: input.method,
244
+ pathTemplate: requestShape.pathTemplate,
245
+ direction: "request",
246
+ }),
247
+ deniedKeys: input.deniedKeys ?? [],
248
+ });
249
+ const requestShapeEvidence = buildRequestShapeEvidence({
250
+ queryValues,
251
+ normalizedRequestBody,
252
+ defaultRequestKind: input.defaultRequestKind,
253
+ });
254
+ const interactionId = traceContext.interactionId;
255
+ return {
256
+ method: input.method,
257
+ rawUrl: input.rawUrl,
258
+ url: stripQueryFromUrl(input.rawUrl),
259
+ path: requestShape.path,
260
+ pathTemplate: requestShape.pathTemplate,
261
+ targetService: requestShape.targetService,
262
+ startedAtMs: input.startedAtMs ?? Date.now(),
263
+ traceId: traceContext.traceId,
264
+ parentSpanId: traceContext.parentSpanId,
265
+ requestId: traceContext.requestId,
266
+ requestSpanId: traceContext.requestSpanId,
267
+ interactionId,
268
+ interactionInitiator: traceContext.interactionInitiator ??
269
+ (interactionId ? null : "background"),
270
+ interactionStartEventKind: traceContext.interactionStartEventKind ??
271
+ (interactionId ? null : "background_request"),
272
+ originFrontendInteractionId: traceContext.originFrontendInteractionId ?? null,
273
+ viewId: input.context.getViewId(),
274
+ requestHeaders: sanitizedRequestHeaders,
275
+ queryKeys,
276
+ requestParametersSnapshot,
277
+ requestMetadataSummary: buildLocalStructuredSummary({
278
+ headers: sanitizedRequestHeaders,
279
+ query: querySummary,
280
+ }) ?? {},
281
+ requestShapeEvidence,
282
+ requestSizeBytes: estimateBodySizeBytes(input.body),
283
+ pageUrl: currentPageUrl(),
284
+ };
285
+ };
@@ -0,0 +1,25 @@
1
+ import { type Span } from "@opentelemetry/api";
2
+ /**
3
+ * 属性を書き込む側が、span に実際に求めるもの。
4
+ *
5
+ * **`Span` 全体(11 member)を求めていました。** ここで使うのは 2 つだけなので、
6
+ * 試験の作り物は残りを持てず `as unknown as Span` で嘘をつくしかない状態でした
7
+ * (`tests/README.md` §10)。3 つ目を使い始めたら、ここが変わって作り物が落ちます。
8
+ */
9
+ export type AttributeWritableSpan = {
10
+ [K in "setAttribute" | "setStatus"]: (...args: Parameters<Span[K]>) => unknown;
11
+ };
12
+ import type { CaptureModuleContext } from "../provider/types.js";
13
+ import { type HeaderRecord, type NetworkRequestMeta } from "./network-request.js";
14
+ type ObservationContext = ReturnType<NonNullable<CaptureModuleContext["getObservationContext"]>> | null;
15
+ export declare const setResponseSpanAttributes: (span: AttributeWritableSpan, meta: NetworkRequestMeta, input: {
16
+ statusCode?: number | null;
17
+ responseHeaders?: HeaderRecord;
18
+ normalizedResponseBody: unknown;
19
+ responseSizeBytes?: number;
20
+ failureType?: string | null;
21
+ durationMs: number;
22
+ observationContext: ObservationContext;
23
+ deniedKeys?: readonly string[];
24
+ }) => void;
25
+ export {};
@@ -0,0 +1,153 @@
1
+ import { SpanStatusCode } from "@opentelemetry/api";
2
+ import { buildLocalStructuredSummary } from "../exporter/local-minimization.js";
3
+ import { cluebaseAuthoredMetricAttribute, cluebaseAuthoredPropertyAttribute, } from "./cluebase-authored-attributes.js";
4
+ import { sanitizeHeaders } from "../exporter/sanitize.js";
5
+ import { FRONTEND_EVENT_CATEGORY, FRONTEND_EVENT_NAME, FRONTEND_EVENT_STATUS, } from "../provider/contracts.js";
6
+ import { buildParameterSnapshotBundle } from "../exporter/parameter-snapshot.js";
7
+ import { allowedValuePathsFor } from "../provider/privacy-policy-store.js";
8
+ import { buildResponseShapeEvidence, } from "./network-request.js";
9
+ const PRIVACY_POLICY_VERSION = 1;
10
+ const setOptionalString = (span, key, value) => {
11
+ if (value !== null && value !== undefined && value !== "") {
12
+ span.setAttribute(key, value);
13
+ }
14
+ };
15
+ const setOptionalNumber = (span, key, value) => {
16
+ if (typeof value === "number" && Number.isFinite(value)) {
17
+ span.setAttribute(key, value);
18
+ }
19
+ };
20
+ const setOptionalStringArray = (span, key, value) => {
21
+ if (value && value.length > 0) {
22
+ span.setAttribute(key, value);
23
+ }
24
+ };
25
+ const stringifyNonEmptyObject = (value) => {
26
+ if (Object.keys(value).length === 0)
27
+ return null;
28
+ try {
29
+ return JSON.stringify(value);
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ };
35
+ const setJsonRecordAttribute = (span, key, value) => {
36
+ const json = stringifyNonEmptyObject(value);
37
+ if (json)
38
+ span.setAttribute(key, json);
39
+ };
40
+ const setRequestSpanAttributes = (span, meta, eventName, status, observationContext) => {
41
+ span.setAttribute("cluebase.event_category", FRONTEND_EVENT_CATEGORY.request);
42
+ span.setAttribute("cluebase.event_name", eventName);
43
+ span.setAttribute("cluebase.status", status);
44
+ span.setAttribute("cluebase.event_source", "frontend_sdk");
45
+ span.setAttribute("cluebase.occurrence.kind", "frontend_action");
46
+ span.setAttribute("cluebase.trace_id", meta.traceId);
47
+ span.setAttribute("cluebase.request.id", meta.requestId);
48
+ span.setAttribute("cluebase.request_span_id", meta.requestSpanId);
49
+ span.setAttribute("cluebase.privacy.level", "default_masked");
50
+ span.setAttribute("cluebase.privacy.masking_state", "masked");
51
+ span.setAttribute("cluebase.privacy.policy_version", PRIVACY_POLICY_VERSION);
52
+ span.setAttribute("http.method", meta.method);
53
+ span.setAttribute("http.request.method", meta.method);
54
+ span.setAttribute("http.route", meta.pathTemplate);
55
+ span.setAttribute("cluebase.path", meta.path);
56
+ span.setAttribute("cluebase.path_template", meta.pathTemplate);
57
+ setOptionalString(span, "cluebase.target_service", meta.targetService);
58
+ setOptionalString(span, "cluebase.parent_span_id", meta.parentSpanId);
59
+ setOptionalString(span, "cluebase.interaction.id", meta.interactionId);
60
+ setOptionalString(span, "cluebase.interaction.initiator", meta.interactionInitiator);
61
+ setOptionalString(span, "cluebase.interaction.start_event_kind", meta.interactionStartEventKind);
62
+ setOptionalString(span, "cluebase.view.id", meta.viewId);
63
+ setOptionalString(span, "cluebase.origin_frontend_interaction.id", meta.originFrontendInteractionId);
64
+ setOptionalString(span, "cluebase.page.url", meta.pageUrl);
65
+ setOptionalString(span, "cluebase.actor.anonymous_id", observationContext?.anonymousId);
66
+ setOptionalString(span, "cluebase.actor.user_id", observationContext?.userId);
67
+ setOptionalString(span, "cluebase.subject.organization_id", observationContext?.organizationId);
68
+ setOptionalString(span, "cluebase.session.id", observationContext?.sessionId);
69
+ setOptionalString(span, "cluebase.tab.id", observationContext?.tabId);
70
+ const requestEvidence = meta.requestShapeEvidence;
71
+ setOptionalString(span, "cluebase.request_kind", requestEvidence.requestKind);
72
+ setOptionalString(span, "cluebase.request_schema_hash", requestEvidence.requestSchemaHash);
73
+ setOptionalStringArray(span, "cluebase.request_field_paths", requestEvidence.requestFieldPaths);
74
+ span.setAttribute("cluebase.request_shape_size", requestEvidence.requestShapeSize);
75
+ span.setAttribute(cluebaseAuthoredMetricAttribute("request_shape_size"), requestEvidence.requestShapeSize);
76
+ setOptionalString(span, cluebaseAuthoredPropertyAttribute("request_kind"), requestEvidence.requestKind);
77
+ setOptionalString(span, cluebaseAuthoredPropertyAttribute("graphql_operation_name"), requestEvidence.graphqlOperationName);
78
+ setOptionalString(span, cluebaseAuthoredPropertyAttribute("graphql_operation_type"), requestEvidence.graphqlOperationType);
79
+ setOptionalStringArray(span, cluebaseAuthoredPropertyAttribute("graphql_top_level_fields"), requestEvidence.graphqlTopLevelFields);
80
+ setOptionalNumber(span, cluebaseAuthoredMetricAttribute("request_size_bytes"), meta.requestSizeBytes);
81
+ span.setAttribute(cluebaseAuthoredPropertyAttribute("request_field_paths"), meta.queryKeys);
82
+ // 顧客の値が入る面。**値は入れ物の中で伏せられている。**顧客が画面で名指しした
83
+ // 項目だけが値を持ち、その項目名は下の控えに並ぶ。
84
+ setJsonRecordAttribute(span, "cluebase.request_parameters_snapshot_json", meta.requestParametersSnapshot.snapshot);
85
+ setJsonRecordAttribute(span, "cluebase.request_metadata_json", {
86
+ ...meta.requestMetadataSummary,
87
+ path_template: meta.pathTemplate,
88
+ ...(meta.queryKeys.length > 0 ? { query_keys: meta.queryKeys } : {}),
89
+ });
90
+ };
91
+ export const setResponseSpanAttributes = (span, meta, input) => {
92
+ const responseShapeEvidence = buildResponseShapeEvidence({
93
+ normalizedResponseBody: input.normalizedResponseBody,
94
+ statusCode: input.statusCode,
95
+ failureType: input.failureType,
96
+ });
97
+ const eventName = input.failureType && input.statusCode == null
98
+ ? FRONTEND_EVENT_NAME.requestFailed
99
+ : FRONTEND_EVENT_NAME.requestFinished;
100
+ const status = eventName === FRONTEND_EVENT_NAME.requestFailed
101
+ ? FRONTEND_EVENT_STATUS.failed
102
+ : FRONTEND_EVENT_STATUS.finished;
103
+ setRequestSpanAttributes(span, meta, eventName, status, input.observationContext);
104
+ if (eventName === FRONTEND_EVENT_NAME.requestFailed) {
105
+ span.setStatus({
106
+ code: SpanStatusCode.ERROR,
107
+ message: input.failureType ?? "network_error",
108
+ });
109
+ }
110
+ if (input.statusCode !== undefined && input.statusCode !== null) {
111
+ span.setAttribute("http.status_code", input.statusCode);
112
+ span.setAttribute("http.response.status_code", input.statusCode);
113
+ if (input.statusCode >= 400) {
114
+ span.setStatus({ code: SpanStatusCode.ERROR });
115
+ }
116
+ }
117
+ span.setAttribute("cluebase.duration_ms", input.durationMs);
118
+ setOptionalString(span, "cluebase.outcome_class", responseShapeEvidence.outcomeClass);
119
+ setOptionalString(span, "cluebase.error_code_candidate", responseShapeEvidence.errorCodeCandidate);
120
+ setOptionalString(span, "cluebase.response_schema_hash", responseShapeEvidence.responseSchemaHash);
121
+ setOptionalStringArray(span, "cluebase.response_field_paths", responseShapeEvidence.responseFieldPaths);
122
+ span.setAttribute("cluebase.response_shape_size", responseShapeEvidence.responseShapeSize);
123
+ span.setAttribute(cluebaseAuthoredMetricAttribute("response_shape_size"), responseShapeEvidence.responseShapeSize);
124
+ setOptionalNumber(span, cluebaseAuthoredMetricAttribute("response_size_bytes"), input.responseSizeBytes);
125
+ setOptionalString(span, cluebaseAuthoredPropertyAttribute("failure_type"), input.failureType);
126
+ setOptionalString(span, cluebaseAuthoredPropertyAttribute("outcome_class"), responseShapeEvidence.outcomeClass);
127
+ setOptionalString(span, cluebaseAuthoredPropertyAttribute("error_code_candidate"), responseShapeEvidence.errorCodeCandidate);
128
+ const attemptStatus = eventName === FRONTEND_EVENT_NAME.requestFailed
129
+ ? "failed"
130
+ : input.failureType
131
+ ? "failed"
132
+ : "completed";
133
+ span.setAttribute("cluebase.attempt_status", attemptStatus);
134
+ span.setAttribute(cluebaseAuthoredPropertyAttribute("request_lifecycle_outcome"), attemptStatus);
135
+ const sanitizedResponseHeaders = sanitizeHeaders(input.responseHeaders ?? {});
136
+ const responseParametersSnapshot = buildParameterSnapshotBundle({ body: input.normalizedResponseBody }, {
137
+ allowedPaths: allowedValuePathsFor({
138
+ method: meta.method,
139
+ pathTemplate: meta.pathTemplate,
140
+ direction: "response",
141
+ }),
142
+ deniedKeys: input.deniedKeys ?? [],
143
+ });
144
+ setJsonRecordAttribute(span, "cluebase.response_parameters_snapshot_json", responseParametersSnapshot.snapshot);
145
+ // 平文で残した項目の控え。面をまたいで 1 本にする。空なら属性を出さない。
146
+ setOptionalStringArray(span, "cluebase.privacy.allowed_keys", [
147
+ ...new Set([
148
+ ...meta.requestParametersSnapshot.allowedValueKeys,
149
+ ...responseParametersSnapshot.allowedValueKeys,
150
+ ]),
151
+ ]);
152
+ setJsonRecordAttribute(span, "cluebase.response_metadata_json", buildLocalStructuredSummary({ headers: sanitizedResponseHeaders }) ?? {});
153
+ };
@@ -0,0 +1,16 @@
1
+ import { type TextMapPropagator, type TracerProvider } from "@opentelemetry/api";
2
+ import type { CaptureModuleContext, CaptureStopHandle } from "../provider/types.js";
3
+ import { type RequestTraceContext } from "./network-request.js";
4
+ type RequestTraceContextInput = {
5
+ method: string | null;
6
+ urlKey: string | null;
7
+ };
8
+ export declare const createCluebaseNetworkPropagator: () => TextMapPropagator;
9
+ export declare const startNetworkCapture: (context: CaptureModuleContext, options?: {
10
+ tracerProvider?: TracerProvider;
11
+ deniedKeys?: readonly string[];
12
+ ignoreUrlPrefixes?: string[];
13
+ tracePropagationOrigins?: string[];
14
+ createRequestTraceContext?: (input?: RequestTraceContextInput) => RequestTraceContext;
15
+ }) => CaptureStopHandle;
16
+ export {};