@logbrew/react-native 0.1.20 → 0.1.22

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.
package/metadata.js CHANGED
@@ -1,181 +1,10 @@
1
- const SENSITIVE_METADATA_FACTORY_KEY_RE = new RegExp([
2
- "body",
3
- "payload",
4
- "variable",
5
- "header",
6
- "authorization",
7
- "cookie",
8
- "to\u006ben",
9
- "sec\u0072et",
10
- "pass\u0077ord"
11
- ].join("|"), "u");
12
- const REACT_NATIVE_DEBUG_ID_REGISTRY = Symbol.for("@logbrew/react-native/debug-ids");
13
- const SAFE_RELEASE_ARTIFACT_DEBUG_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
14
- const MAX_REACT_NATIVE_DEBUG_ID_REGISTRY_ENTRIES = 64;
15
- const MAX_REACT_NATIVE_DEBUG_ID_REGISTRY_FRAMES = 128;
16
-
17
- export function createSafeReactNativeMetadata(metadata, metadataFactory, context) {
18
- if (typeof metadataFactory !== "function") {
19
- return metadata;
20
- }
21
- return {
22
- ...metadata,
23
- ...safeReactNativeMetadataFactoryResult(metadataFactory(context))
24
- };
25
- }
26
-
27
- export function safeReactNativeMetadataFactoryResult(candidate) {
28
- if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
29
- return {};
30
- }
31
- const metadata = {};
32
- for (const [key, value] of Object.entries(candidate)) {
33
- if (isSensitiveMetadataKey(key)) {
34
- continue;
35
- }
36
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
37
- metadata[key] = value;
38
- }
39
- }
40
- return metadata;
41
- }
42
-
43
- export function sanitizeReactNativeIssueMetadata(metadata, compactMetadata) {
44
- const next = { ...metadata };
45
- for (const key of ["errorFrameFile", "releaseArtifactCodeFile"]) {
46
- const path = reactNativeCodePath(next[key]);
47
- if (path) {
48
- next[key] = path;
49
- }
50
- }
51
- const match = typeof next.issueGroupingKey === "string" ? next.issueGroupingKey.match(/^([^:]+):([^:]+):(.+)$/u) : null;
52
- const path = match ? reactNativeCodePath(match[3]) : undefined;
53
- if (path) {
54
- next.issueGroupingKey = `${match[1]}:${match[2]}:${path}`;
55
- }
56
- return compactMetadata(next);
57
- }
58
-
59
- export function sanitizeReactNativeIssueStackFrames(stackFrames) {
60
- if (!Array.isArray(stackFrames)) {
61
- return undefined;
62
- }
63
- return stackFrames.map((frame) => ({
64
- ...frame,
65
- filename: reactNativeCodePath(frame.filename) ?? frame.filename
66
- }));
67
- }
68
-
69
- export function sanitizeReactNativeIssueExceptionChain(exceptionChain) {
70
- if (!exceptionChain || !Array.isArray(exceptionChain.entries)) {
71
- return undefined;
72
- }
73
- return {
74
- ...exceptionChain,
75
- entries: exceptionChain.entries.map((entry) => {
76
- const stackFrames = sanitizeReactNativeIssueStackFrames(entry.stackFrames);
77
- return {
78
- ...entry,
79
- ...(stackFrames ? { stackFrames } : {})
80
- };
81
- })
82
- };
83
- }
84
-
85
- export function runtimeReactNativeDebugIdMap() {
86
- try {
87
- const registry = globalThis?.[REACT_NATIVE_DEBUG_ID_REGISTRY];
88
- if (!registry || Array.isArray(registry) || typeof registry !== "object") {
89
- return undefined;
90
- }
91
- const entries = Object.entries(registry);
92
- if (entries.length === 0 || entries.length > MAX_REACT_NATIVE_DEBUG_ID_REGISTRY_ENTRIES) {
93
- return undefined;
94
- }
95
- const debugIdMap = Object.create(null);
96
- let frameCount = 0;
97
- for (const [stack, debugId] of entries) {
98
- if (typeof debugId !== "string" || !SAFE_RELEASE_ARTIFACT_DEBUG_ID.test(debugId)) {
99
- return undefined;
100
- }
101
- const normalizedDebugId = debugId.toLowerCase();
102
- let stackFrameCount = 0;
103
- for (const line of stack.split(/\r?\n/u)) {
104
- const filename = runtimeStackFrameFilename(line);
105
- if (!filename) {
106
- continue;
107
- }
108
- frameCount += 1;
109
- stackFrameCount += 1;
110
- if (frameCount > MAX_REACT_NATIVE_DEBUG_ID_REGISTRY_FRAMES) {
111
- return undefined;
112
- }
113
- const existingDebugId = debugIdMap[filename];
114
- if (existingDebugId && existingDebugId !== normalizedDebugId) {
115
- return undefined;
116
- }
117
- debugIdMap[filename] = normalizedDebugId;
118
- }
119
- if (stackFrameCount === 0) {
120
- return undefined;
121
- }
122
- }
123
- return frameCount > 0 ? debugIdMap : undefined;
124
- } catch {
125
- return undefined;
126
- }
127
- }
128
-
129
- function isSensitiveMetadataKey(key) {
130
- return SENSITIVE_METADATA_FACTORY_KEY_RE.test(String(key).toLowerCase());
131
- }
132
-
133
- function runtimeStackFrameFilename(rawLine) {
134
- let location = typeof rawLine === "string" ? rawLine.trim() : "";
135
- if (!location) {
136
- return undefined;
137
- }
138
- if (location.startsWith("at ")) {
139
- location = location.slice(3).trim();
140
- if (location.endsWith(")") && location.includes("(")) {
141
- location = location.slice(location.lastIndexOf("(") + 1, -1);
142
- }
143
- } else if (location.includes("@")) {
144
- location = location.slice(location.lastIndexOf("@") + 1);
145
- }
146
- const parts = location.split(":");
147
- if (parts.length < 3) {
148
- return undefined;
149
- }
150
- const columnText = parts.pop();
151
- const lineText = parts.pop();
152
- const filename = parts.join(":").trim();
153
- if (!/^[1-9]\d*$/u.test(lineText) || !/^[1-9]\d*$/u.test(columnText)) {
154
- return undefined;
155
- }
156
- const line = Number(lineText);
157
- const column = Number(columnText);
158
- return Number.isSafeInteger(line) && Number.isSafeInteger(column) && filename ? filename : undefined;
159
- }
160
-
161
- function reactNativeCodePath(value) {
162
- if (typeof value !== "string" || value.trim() === "") {
163
- return undefined;
164
- }
165
- let path = value.trim();
166
- const URLConstructor = globalThis.URL;
167
- if (typeof URLConstructor === "function") {
168
- try {
169
- path = new URLConstructor(path).pathname || path;
170
- } catch {
171
- path = path.split(/[?#]/u, 1)[0].replace(/\\/g, "/");
172
- }
173
- } else {
174
- path = path.split(/[?#]/u, 1)[0].replace(/\\/g, "/");
175
- }
176
- if (/^[A-Za-z]:\//u.test(path) || /^\/(?:Users|home|private|tmp|var)\//u.test(path)) {
177
- path = path.replace(/\/+$/u, "");
178
- return path.slice(path.lastIndexOf("/") + 1) || undefined;
179
- }
180
- return path || undefined;
181
- }
1
+ import implementation from "./metadata.cjs";
2
+
3
+ export const {
4
+ createSafeReactNativeMetadata,
5
+ runtimeReactNativeDebugIdMap,
6
+ safeReactNativeMetadataFactoryResult,
7
+ sanitizeReactNativeIssueExceptionChain,
8
+ sanitizeReactNativeIssueMetadata,
9
+ sanitizeReactNativeIssueStackFrames
10
+ } = implementation;
package/native-bridge.js CHANGED
@@ -1,125 +1,10 @@
1
- import { SdkError } from "@logbrew/sdk";
2
- import {
3
- getActiveLogBrewTrace,
4
- getReactNativeTraceMetadata
5
- } from "./index.js";
1
+ import runtime from "./native-bridge.cjs";
6
2
 
7
- const DEFAULT_SCOPE_SOURCE = "react-native.native_bridge";
8
- const RESERVED_TRACE_METADATA_KEYS = new Set([
9
- "parentSpanId",
10
- "spanId",
11
- "traceFlags",
12
- "traceId",
13
- "traceSampled",
14
- "traceparent"
15
- ]);
16
-
17
- export function createLogBrewNativeBridgeScope({
18
- logger,
19
- metadata = {},
20
- screen,
21
- sessionId,
22
- source = DEFAULT_SCOPE_SOURCE,
23
- trace = getActiveLogBrewTrace()
24
- } = {}) {
25
- const traceMetadata = getReactNativeTraceMetadata(trace);
26
- return {
27
- trace: Object.keys(traceMetadata).length === 0 ? undefined : {
28
- parentSpanId: traceMetadata.parentSpanId,
29
- spanId: traceMetadata.spanId,
30
- traceFlags: traceMetadata.traceFlags,
31
- traceId: traceMetadata.traceId,
32
- traceSampled: traceMetadata.traceSampled
33
- },
34
- metadata: compactMetadata({
35
- ...metadata,
36
- logger,
37
- screen,
38
- sessionId,
39
- source
40
- })
41
- };
42
- }
43
-
44
- export function syncLogBrewNativeBridgeScope(nativeBridge, options = {}) {
45
- const payload = createLogBrewNativeBridgeScope(options);
46
- bridgeSync(nativeBridge)(payload);
47
- return payload;
48
- }
49
-
50
- export function clearLogBrewNativeBridgeScope(nativeBridge) {
51
- bridgeClear(nativeBridge)();
52
- }
53
-
54
- export function withLogBrewNativeBridgeScope(nativeBridge, options, callback) {
55
- const resolvedOptions = typeof options === "function" ? {} : options ?? {};
56
- const resolvedCallback = typeof options === "function" ? options : callback;
57
- if (typeof resolvedCallback !== "function") {
58
- throw new SdkError("configuration_error", "withLogBrewNativeBridgeScope requires a callback");
59
- }
60
-
61
- const payload = syncLogBrewNativeBridgeScope(nativeBridge, resolvedOptions);
62
- try {
63
- const result = resolvedCallback(payload);
64
- if (result && typeof result.then === "function") {
65
- return Promise.resolve(result).finally(() => clearLogBrewNativeBridgeScope(nativeBridge));
66
- }
67
- clearLogBrewNativeBridgeScope(nativeBridge);
68
- return result;
69
- } catch (error) {
70
- clearLogBrewNativeBridgeScope(nativeBridge);
71
- throw error;
72
- }
73
- }
74
-
75
- function bridgeSync(nativeBridge) {
76
- if (typeof nativeBridge === "function") {
77
- return nativeBridge;
78
- }
79
- if (nativeBridge && typeof nativeBridge.setLogBrewScope === "function") {
80
- return nativeBridge.setLogBrewScope.bind(nativeBridge);
81
- }
82
- if (nativeBridge && typeof nativeBridge.syncLogBrewScope === "function") {
83
- return nativeBridge.syncLogBrewScope.bind(nativeBridge);
84
- }
85
- throw new SdkError(
86
- "configuration_error",
87
- "LogBrew native bridge scope sync requires a function, setLogBrewScope, or syncLogBrewScope"
88
- );
89
- }
90
-
91
- function bridgeClear(nativeBridge) {
92
- if (nativeBridge && typeof nativeBridge.clearLogBrewScope === "function") {
93
- return nativeBridge.clearLogBrewScope.bind(nativeBridge);
94
- }
95
- if (nativeBridge && typeof nativeBridge.clearLogBrewTraceContext === "function") {
96
- return nativeBridge.clearLogBrewTraceContext.bind(nativeBridge);
97
- }
98
- if (typeof nativeBridge === "function") {
99
- return () => nativeBridge(undefined);
100
- }
101
- return () => {};
102
- }
103
-
104
- function compactMetadata(metadata) {
105
- const compacted = {};
106
- for (const [key, value] of Object.entries(metadata)) {
107
- if (value === undefined) {
108
- continue;
109
- }
110
- if (RESERVED_TRACE_METADATA_KEYS.has(key)) {
111
- continue;
112
- }
113
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
114
- compacted[key] = value;
115
- }
116
- }
117
- return compacted;
118
- }
119
-
120
- export default {
3
+ export const {
121
4
  clearLogBrewNativeBridgeScope,
122
5
  createLogBrewNativeBridgeScope,
123
6
  syncLogBrewNativeBridgeScope,
124
7
  withLogBrewNativeBridgeScope
125
- };
8
+ } = runtime;
9
+
10
+ export default runtime.default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logbrew/react-native",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "React Native offline delivery, Apple native diagnostics, screen, error, trace, action, and network timeline helpers for LogBrew.",
5
5
  "type": "module",
6
6
  "main": "./index.cjs",
@@ -240,7 +240,7 @@
240
240
  "url": "git+https://github.com/LogBrewCo/sdk.git"
241
241
  },
242
242
  "peerDependencies": {
243
- "@logbrew/sdk": "^0.1.12",
243
+ "@logbrew/sdk": "^0.1.15",
244
244
  "expo": ">=49",
245
245
  "react": ">=18",
246
246
  "react-native": ">=0.72"
@@ -9,6 +9,9 @@ export interface Spec extends TurboModule {
9
9
  setNativeDiagnosticsContext(
10
10
  context: CodegenTypes.UnsafeObject | null
11
11
  ): CodegenTypes.UnsafeObject;
12
+ setNativeDiagnosticsBreadcrumbs(
13
+ snapshot: CodegenTypes.UnsafeObject | null
14
+ ): CodegenTypes.UnsafeObject;
12
15
  nativeDiagnosticsStatus(): CodegenTypes.UnsafeObject;
13
16
  }
14
17