@logbrew/react-native 0.1.0 → 0.1.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 (47) hide show
  1. package/README.md +407 -18
  2. package/apollo.cjs +301 -0
  3. package/apollo.d.cts +70 -0
  4. package/apollo.d.ts +70 -0
  5. package/apollo.js +294 -0
  6. package/examples/apollo-link-spans.mjs +149 -0
  7. package/examples/index.mjs +29 -1
  8. package/examples/instrumentation-kit.mjs +304 -0
  9. package/examples/lifecycle-spans.mjs +131 -0
  10. package/examples/native-bridge-scope.mjs +107 -0
  11. package/examples/navigation-resource-spans.mjs +156 -0
  12. package/examples/package.json +8 -1
  13. package/examples/real-user-smoke.mjs +106 -9
  14. package/examples/resource-fetch-spans.mjs +133 -0
  15. package/examples/trace-correlation.mjs +135 -0
  16. package/index.cjs +625 -111
  17. package/index.d.cts +236 -0
  18. package/index.d.ts +236 -0
  19. package/index.js +613 -95
  20. package/index.native.js +18 -0
  21. package/instrumentation.cjs +639 -0
  22. package/instrumentation.d.cts +84 -0
  23. package/instrumentation.d.ts +84 -0
  24. package/instrumentation.js +634 -0
  25. package/lifecycle.cjs +129 -0
  26. package/lifecycle.d.cts +50 -0
  27. package/lifecycle.d.ts +50 -0
  28. package/lifecycle.js +121 -0
  29. package/metadata.cjs +175 -0
  30. package/metadata.js +165 -0
  31. package/metro.cjs +310 -0
  32. package/metro.d.cts +37 -0
  33. package/metro.d.ts +37 -0
  34. package/metro.js +6 -0
  35. package/native-bridge.cjs +127 -0
  36. package/native-bridge.d.cts +60 -0
  37. package/native-bridge.d.ts +60 -0
  38. package/native-bridge.js +125 -0
  39. package/package.json +113 -4
  40. package/release-artifacts.cjs +344 -0
  41. package/release-artifacts.d.cts +55 -0
  42. package/release-artifacts.d.ts +53 -0
  43. package/release-artifacts.js +8 -0
  44. package/resource-fetch.cjs +469 -0
  45. package/resource-fetch.d.cts +60 -0
  46. package/resource-fetch.d.ts +60 -0
  47. package/resource-fetch.js +464 -0
package/apollo.cjs ADDED
@@ -0,0 +1,301 @@
1
+ "use strict";
2
+
3
+ const {
4
+ SdkError
5
+ } = require("@logbrew/sdk");
6
+ const {
7
+ captureReactNativeResourceSpan,
8
+ createReactNativeTraceContext,
9
+ createReactNativeTraceHeaders,
10
+ getActiveLogBrewTrace
11
+ } = require("./index.cjs");
12
+ const {
13
+ safeReactNativeMetadataFactoryResult
14
+ } = require("./metadata.cjs");
15
+
16
+ const MAX_GRAPHQL_OPERATION_NAME_CHARS = 128;
17
+ const GRAPHQL_OPERATION_NAME_RE = /^[_A-Za-z][_0-9A-Za-z]*$/u;
18
+ const GRAPHQL_OPERATION_TYPES = new Set(["query", "mutation", "subscription"]);
19
+
20
+ function createReactNativeApolloLink(client, {
21
+ ApolloLink,
22
+ appState,
23
+ metadata = {},
24
+ metadataFactory,
25
+ now = () => new Date().toISOString(),
26
+ nowMs = () => Date.now(),
27
+ platform,
28
+ propagateTraceparent = true,
29
+ randomValues,
30
+ screen,
31
+ sessionId,
32
+ trace,
33
+ traceFlags = "01"
34
+ } = {}) {
35
+ if (typeof ApolloLink !== "function") {
36
+ throw new SdkError("configuration_error", "createReactNativeApolloLink requires an app-provided ApolloLink constructor");
37
+ }
38
+ if (metadataFactory !== undefined && typeof metadataFactory !== "function") {
39
+ throw new SdkError("configuration_error", "metadataFactory must be a function");
40
+ }
41
+
42
+ return new ApolloLink((operation, forward) => {
43
+ if (typeof forward !== "function") {
44
+ throw new SdkError("configuration_error", "createReactNativeApolloLink requires Apollo forward");
45
+ }
46
+
47
+ const startedAtMs = nowMs();
48
+ const timestamp = now();
49
+ const activeTrace = apolloTraceContext({ randomValues, trace, traceFlags });
50
+ const details = apolloOperationDetails(operation);
51
+ if (propagateTraceparent) {
52
+ setOperationTraceparent(operation, activeTrace);
53
+ }
54
+
55
+ let forwarded;
56
+ try {
57
+ forwarded = forward(operation);
58
+ } catch (error) {
59
+ captureApolloSpan(client, {
60
+ activeTrace,
61
+ appState,
62
+ details,
63
+ durationMs: elapsedMs(startedAtMs, nowMs),
64
+ error,
65
+ metadata,
66
+ metadataFactory,
67
+ now,
68
+ platform,
69
+ screen,
70
+ sessionId,
71
+ status: "error",
72
+ timestamp
73
+ });
74
+ throw error;
75
+ }
76
+
77
+ if (!forwarded || typeof forwarded.subscribe !== "function") {
78
+ throw new SdkError("configuration_error", "Apollo forward must return an observable-like value");
79
+ }
80
+
81
+ return apolloObservable(forwarded, {
82
+ activeTrace,
83
+ appState,
84
+ client,
85
+ details,
86
+ metadata,
87
+ metadataFactory,
88
+ now,
89
+ nowMs,
90
+ platform,
91
+ screen,
92
+ sessionId,
93
+ startedAtMs,
94
+ timestamp
95
+ });
96
+ });
97
+ }
98
+
99
+ function apolloObservable(forwarded, options) {
100
+ return {
101
+ subscribe(observerOrNext, onError, onComplete) {
102
+ const observer = apolloObserver(observerOrNext, onError, onComplete);
103
+ let finished = false;
104
+ let graphqlErrorCount = 0;
105
+
106
+ const finish = ({ error, status }) => {
107
+ if (finished) {
108
+ return;
109
+ }
110
+ finished = true;
111
+ captureApolloSpan(options.client, {
112
+ activeTrace: options.activeTrace,
113
+ appState: options.appState,
114
+ details: options.details,
115
+ durationMs: elapsedMs(options.startedAtMs, options.nowMs),
116
+ error,
117
+ graphqlErrorCount,
118
+ metadata: options.metadata,
119
+ metadataFactory: options.metadataFactory,
120
+ now: options.now,
121
+ platform: options.platform,
122
+ screen: options.screen,
123
+ sessionId: options.sessionId,
124
+ status,
125
+ timestamp: options.timestamp
126
+ });
127
+ };
128
+
129
+ try {
130
+ return forwarded.subscribe({
131
+ next(value) {
132
+ graphqlErrorCount += graphqlErrorCountFromResult(value);
133
+ observer.next?.(value);
134
+ },
135
+ error(error) {
136
+ finish({ error, status: "error" });
137
+ observer.error?.(error);
138
+ },
139
+ complete() {
140
+ finish({ status: graphqlErrorCount > 0 ? "error" : "ok" });
141
+ observer.complete?.();
142
+ }
143
+ });
144
+ } catch (error) {
145
+ finish({ error, status: "error" });
146
+ throw error;
147
+ }
148
+ }
149
+ };
150
+ }
151
+
152
+ function captureApolloSpan(client, {
153
+ activeTrace,
154
+ appState,
155
+ details,
156
+ durationMs,
157
+ error,
158
+ graphqlErrorCount,
159
+ metadata,
160
+ metadataFactory,
161
+ now,
162
+ platform,
163
+ screen,
164
+ sessionId,
165
+ status,
166
+ timestamp
167
+ }) {
168
+ const context = {
169
+ durationMs,
170
+ error,
171
+ operationName: details.operationName,
172
+ operationType: details.operationType,
173
+ screen,
174
+ status
175
+ };
176
+ captureReactNativeResourceSpan(client, {
177
+ appState,
178
+ durationMs,
179
+ id: defaultApolloSpanId({ operationName: details.operationName, operationType: details.operationType, screen }),
180
+ kind: "graphql",
181
+ metadata: {
182
+ ...metadata,
183
+ ...safeReactNativeMetadataFactoryResult(typeof metadataFactory === "function" ? metadataFactory(context) : undefined),
184
+ errorName: errorName(error),
185
+ errorValueType: error === undefined ? undefined : typeof error,
186
+ framework: "apollo-client",
187
+ graphqlErrorCount: graphqlErrorCount && graphqlErrorCount > 0 ? graphqlErrorCount : undefined,
188
+ graphqlOperationName: details.operationName,
189
+ graphqlOperationType: details.operationType,
190
+ source: "react-native.apollo"
191
+ },
192
+ name: apolloSpanName(details),
193
+ now,
194
+ platform,
195
+ screen,
196
+ sessionId,
197
+ status,
198
+ timestamp,
199
+ trace: activeTrace
200
+ });
201
+ }
202
+
203
+ function setOperationTraceparent(operation, trace) {
204
+ if (!operation || typeof operation.setContext !== "function") {
205
+ return;
206
+ }
207
+ const traceparent = createReactNativeTraceHeaders(trace).traceparent;
208
+ operation.setContext(({ headers = {} } = {}) => ({
209
+ headers: {
210
+ ...headers,
211
+ traceparent
212
+ }
213
+ }));
214
+ }
215
+
216
+ function apolloOperationDetails(operation) {
217
+ const definition = operation?.query?.definitions?.find?.((candidate) => (
218
+ candidate?.kind === "OperationDefinition" && GRAPHQL_OPERATION_TYPES.has(candidate?.operation)
219
+ ));
220
+ return {
221
+ operationName: safeGraphqlOperationName(operation?.operationName) ?? safeGraphqlOperationName(definition?.name?.value),
222
+ operationType: GRAPHQL_OPERATION_TYPES.has(definition?.operation) ? definition.operation : undefined
223
+ };
224
+ }
225
+
226
+ function apolloSpanName({ operationName, operationType }) {
227
+ const base = `graphql.${operationType ?? "operation"}`;
228
+ return operationName ? `${base} ${operationName}` : base;
229
+ }
230
+
231
+ function apolloTraceContext({ randomValues, trace, traceFlags }) {
232
+ if (typeof trace === "string") {
233
+ return createReactNativeTraceContext({ randomValues, traceFlags, traceparent: trace });
234
+ }
235
+ return trace ?? getActiveLogBrewTrace() ?? createReactNativeTraceContext({ randomValues, traceFlags });
236
+ }
237
+
238
+ function apolloObserver(observerOrNext, onError, onComplete) {
239
+ if (typeof observerOrNext === "function") {
240
+ return {
241
+ next: observerOrNext,
242
+ error: onError,
243
+ complete: onComplete
244
+ };
245
+ }
246
+ return observerOrNext && typeof observerOrNext === "object" ? observerOrNext : {};
247
+ }
248
+
249
+ function graphqlErrorCountFromResult(value) {
250
+ return Array.isArray(value?.errors) ? value.errors.length : 0;
251
+ }
252
+
253
+ function safeGraphqlOperationName(value) {
254
+ if (typeof value !== "string") {
255
+ return undefined;
256
+ }
257
+ const name = value.trim();
258
+ if (
259
+ name.length === 0 ||
260
+ name.length > MAX_GRAPHQL_OPERATION_NAME_CHARS ||
261
+ !GRAPHQL_OPERATION_NAME_RE.test(name)
262
+ ) {
263
+ return undefined;
264
+ }
265
+ return name;
266
+ }
267
+
268
+ function elapsedMs(startedAtMs, nowMs) {
269
+ const durationMs = nowMs() - startedAtMs;
270
+ return Number.isFinite(durationMs) ? Math.max(0, durationMs) : undefined;
271
+ }
272
+
273
+ function errorName(error) {
274
+ if (error instanceof Error && typeof error.name === "string" && error.name.trim() !== "") {
275
+ return error.name;
276
+ }
277
+ if (typeof error?.name === "string" && error.name.trim() !== "") {
278
+ return error.name;
279
+ }
280
+ return undefined;
281
+ }
282
+
283
+ function defaultApolloSpanId({ operationName, operationType, screen }) {
284
+ return `evt_native_apollo_${slugify([screen, operationType, operationName].filter(Boolean).join("_") || "operation")}`;
285
+ }
286
+
287
+ function slugify(value) {
288
+ return String(value)
289
+ .trim()
290
+ .toLowerCase()
291
+ .replace(/[^a-z0-9]+/gu, "_")
292
+ .replace(/^_+|_+$/gu, "")
293
+ .slice(0, 96) || "event";
294
+ }
295
+
296
+ module.exports = {
297
+ createReactNativeApolloLink,
298
+ default: {
299
+ createReactNativeApolloLink
300
+ }
301
+ };
package/apollo.d.cts ADDED
@@ -0,0 +1,70 @@
1
+ import type { Metadata } from "@logbrew/sdk";
2
+ import type { ReactNativeTraceContext } from "./index.cjs";
3
+
4
+ export type ApolloOperationLike = {
5
+ operationName?: string;
6
+ query?: {
7
+ definitions?: ReadonlyArray<{
8
+ kind?: string;
9
+ operation?: string;
10
+ name?: { value?: string };
11
+ }>;
12
+ };
13
+ setContext?: (context: ((previous: { headers?: Record<string, string> }) => { headers?: Record<string, string> }) | { headers?: Record<string, string> }) => void;
14
+ };
15
+
16
+ export type ApolloObserverLike<TValue = unknown> = {
17
+ next?: (value: TValue) => void;
18
+ error?: (error: unknown) => void;
19
+ complete?: () => void;
20
+ };
21
+
22
+ export type ApolloObservableLike<TValue = unknown> = {
23
+ subscribe: (
24
+ observerOrNext?: ApolloObserverLike<TValue> | ((value: TValue) => void),
25
+ onError?: (error: unknown) => void,
26
+ onComplete?: () => void
27
+ ) => unknown;
28
+ };
29
+
30
+ export type ApolloForwardLike<TValue = unknown> = (operation: ApolloOperationLike) => ApolloObservableLike<TValue>;
31
+
32
+ export type ApolloLinkConstructor<TLink = unknown, TValue = unknown> = new (
33
+ request?: (...args: any[]) => any
34
+ ) => TLink;
35
+
36
+ export type ReactNativeApolloMetadataFactoryContext = {
37
+ durationMs?: number;
38
+ error?: unknown;
39
+ operationName?: string;
40
+ operationType?: string;
41
+ screen?: string;
42
+ status: "ok" | "error";
43
+ };
44
+
45
+ export type ReactNativeApolloLinkOptions<TLink = unknown, TValue = unknown> = {
46
+ ApolloLink: ApolloLinkConstructor<TLink, TValue>;
47
+ appState?: { currentState?: string };
48
+ metadata?: Metadata;
49
+ metadataFactory?: (context: ReactNativeApolloMetadataFactoryContext) => Metadata | undefined;
50
+ now?: () => string;
51
+ nowMs?: () => number;
52
+ platform?: { OS?: string; Version?: string | number; isPad?: boolean; constants?: { isTesting?: boolean } };
53
+ propagateTraceparent?: boolean;
54
+ randomValues?: (length: number) => Uint8Array;
55
+ screen?: string;
56
+ sessionId?: string;
57
+ trace?: ReactNativeTraceContext | string;
58
+ traceFlags?: string;
59
+ };
60
+
61
+ export function createReactNativeApolloLink<TLink = unknown, TValue = unknown>(
62
+ client: { span: (id: string, timestamp: string, attributes: any) => unknown },
63
+ options: ReactNativeApolloLinkOptions<TLink, TValue>
64
+ ): TLink;
65
+
66
+ declare const _default: {
67
+ createReactNativeApolloLink: typeof createReactNativeApolloLink;
68
+ };
69
+
70
+ export default _default;
package/apollo.d.ts ADDED
@@ -0,0 +1,70 @@
1
+ import type { Metadata } from "@logbrew/sdk";
2
+ import type { ReactNativeTraceContext } from "./index.js";
3
+
4
+ export type ApolloOperationLike = {
5
+ operationName?: string;
6
+ query?: {
7
+ definitions?: ReadonlyArray<{
8
+ kind?: string;
9
+ operation?: string;
10
+ name?: { value?: string };
11
+ }>;
12
+ };
13
+ setContext?: (context: ((previous: { headers?: Record<string, string> }) => { headers?: Record<string, string> }) | { headers?: Record<string, string> }) => void;
14
+ };
15
+
16
+ export type ApolloObserverLike<TValue = unknown> = {
17
+ next?: (value: TValue) => void;
18
+ error?: (error: unknown) => void;
19
+ complete?: () => void;
20
+ };
21
+
22
+ export type ApolloObservableLike<TValue = unknown> = {
23
+ subscribe: (
24
+ observerOrNext?: ApolloObserverLike<TValue> | ((value: TValue) => void),
25
+ onError?: (error: unknown) => void,
26
+ onComplete?: () => void
27
+ ) => unknown;
28
+ };
29
+
30
+ export type ApolloForwardLike<TValue = unknown> = (operation: ApolloOperationLike) => ApolloObservableLike<TValue>;
31
+
32
+ export type ApolloLinkConstructor<TLink = unknown, TValue = unknown> = new (
33
+ request?: (...args: any[]) => any
34
+ ) => TLink;
35
+
36
+ export type ReactNativeApolloMetadataFactoryContext = {
37
+ durationMs?: number;
38
+ error?: unknown;
39
+ operationName?: string;
40
+ operationType?: string;
41
+ screen?: string;
42
+ status: "ok" | "error";
43
+ };
44
+
45
+ export type ReactNativeApolloLinkOptions<TLink = unknown, TValue = unknown> = {
46
+ ApolloLink: ApolloLinkConstructor<TLink, TValue>;
47
+ appState?: { currentState?: string };
48
+ metadata?: Metadata;
49
+ metadataFactory?: (context: ReactNativeApolloMetadataFactoryContext) => Metadata | undefined;
50
+ now?: () => string;
51
+ nowMs?: () => number;
52
+ platform?: { OS?: string; Version?: string | number; isPad?: boolean; constants?: { isTesting?: boolean } };
53
+ propagateTraceparent?: boolean;
54
+ randomValues?: (length: number) => Uint8Array;
55
+ screen?: string;
56
+ sessionId?: string;
57
+ trace?: ReactNativeTraceContext | string;
58
+ traceFlags?: string;
59
+ };
60
+
61
+ export function createReactNativeApolloLink<TLink = unknown, TValue = unknown>(
62
+ client: { span: (id: string, timestamp: string, attributes: any) => unknown },
63
+ options: ReactNativeApolloLinkOptions<TLink, TValue>
64
+ ): TLink;
65
+
66
+ declare const _default: {
67
+ createReactNativeApolloLink: typeof createReactNativeApolloLink;
68
+ };
69
+
70
+ export default _default;