react-native-test-adx 0.1.17 → 0.1.19

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,4 +1,4 @@
1
1
  "use strict";
2
2
 
3
3
  /** Keep in sync with package.json "version" (published npm packages ship lib/ only). */
4
- export const SDK_VERSION = '0.1.17';
4
+ export const SDK_VERSION = '0.1.19';
@@ -5,12 +5,19 @@ import { AdaxonSdkError } from "../../errors/AdaxonSdkError.js";
5
5
  import { sanitizeLogMetadata } from "./sanitizeLogMetadata.js";
6
6
  const DEFAULT_LEVEL = 'info';
7
7
 
8
- /** Milestone events that fall back to captureMessage when structured logs are unavailable. */
9
- const MESSAGE_FALLBACK_EVENTS = new Set(['sdk.initialize.completed', 'sdk.initialize.failed', 'recommendation.response.received', 'ad.displayed', 'ad.show.failed']);
10
- /** undefined = not loaded yet; null = unavailable in this app bundle */
8
+ /** Milestones + native dev warn/error → Sentry Message when Logs are off. */
9
+ const SENTRY_MESSAGE_EVENTS = new Set(['sdk.initialize.completed', 'sdk.initialize.failed', 'recommendation.response.received', 'ad.displayed', 'ad.dismissed', 'ad.show.failed']);
10
+
11
+ /** Native dev: avoid captureMessage during interstitial UI (fake Sentry Issues + bundle stacks). */
12
+ const NATIVE_DEV_BREADCRUMB_ONLY_EVENTS = new Set(['ad.show.started', 'ad.displayed', 'ad.dismissed']);
11
13
  let cachedSentryModule;
12
14
  let missingSentryPeerWarningShown = false;
13
- const SENTRY_UNAVAILABLE_HINT = 'Bundled @sentry/react-native could not be loaded. Reinstall react-native-adaxon dependencies and rebuild the native app.';
15
+ function isDevBuild() {
16
+ return typeof __DEV__ !== 'undefined' && __DEV__;
17
+ }
18
+ function usesSentryLogs(platform) {
19
+ return platform === 'web' || !isDevBuild();
20
+ }
14
21
  function loadSentryModule() {
15
22
  if (cachedSentryModule !== undefined) {
16
23
  return cachedSentryModule;
@@ -24,14 +31,14 @@ function loadSentryModule() {
24
31
  return cachedSentryModule;
25
32
  }
26
33
  function warnSentryUnavailableOnce(hasDsn) {
27
- if (!hasDsn || missingSentryPeerWarningShown || typeof __DEV__ === 'undefined' || !__DEV__) {
34
+ if (!hasDsn || missingSentryPeerWarningShown || !isDevBuild()) {
28
35
  return;
29
36
  }
30
37
  if (loadSentryModule()) {
31
38
  return;
32
39
  }
33
40
  missingSentryPeerWarningShown = true;
34
- console.warn(`[Adaxon] Activity logging is on and Sentry DSN is set, but ${SENTRY_UNAVAILABLE_HINT} ` + 'Until then, events appear locally as [Adaxon activity] in Metro/logcat only.');
41
+ console.warn('[Adaxon] Activity logging is on and Sentry DSN is set, but @sentry/react-native could not be loaded. ' + 'Reinstall dependencies and rebuild. Until then, use [Adaxon activity] in Metro/logcat.');
35
42
  }
36
43
  function sentryLevel(level, target) {
37
44
  if (level === 'warn') {
@@ -46,34 +53,59 @@ function sentryLevel(level, target) {
46
53
  return 'info';
47
54
  }
48
55
 
49
- /** Sentry Logs (`logger.*`) break Metro on native dev; release + web use Logs. */
50
- function usesStructuredSentryLogs(platform) {
51
- if (platform === 'web') {
52
- return true;
56
+ /** What to send after structured Logs (if any) are attempted. */
57
+ function planSentryDelivery(input, level, platform, sentStructuredLog, ownsSentryClient) {
58
+ if (level === 'debug') {
59
+ return {
60
+ breadcrumb: null,
61
+ sendMessage: false
62
+ };
53
63
  }
54
- return typeof __DEV__ !== 'undefined' && !__DEV__;
55
- }
56
- function shouldCaptureMessage(input, level, structuredLogSent, platform) {
57
- if (level === 'debug' || structuredLogSent) {
58
- return false;
64
+ const nativeDev = platform !== 'web' && isDevBuild();
65
+ let sendMessage = false;
66
+ if (ownsSentryClient && !sentStructuredLog) {
67
+ if (input.captureAsEvent && input.error === undefined) {
68
+ sendMessage = true;
69
+ } else if (SENTRY_MESSAGE_EVENTS.has(input.eventName)) {
70
+ const skipMessage = nativeDev && NATIVE_DEV_BREADCRUMB_ONLY_EVENTS.has(input.eventName);
71
+ if (!skipMessage) {
72
+ sendMessage = true;
73
+ }
74
+ } else if (nativeDev && (level === 'warn' || level === 'error')) {
75
+ sendMessage = true;
76
+ }
59
77
  }
60
- if (input.captureAsEvent && input.error === undefined) {
61
- return true;
78
+ let breadcrumb = null;
79
+ if (level === 'warn' || level === 'error') {
80
+ breadcrumb = 'adaxon.sdk';
81
+ } else if (nativeDev && !sentStructuredLog && !sendMessage) {
82
+ breadcrumb = 'adaxon.activity';
62
83
  }
63
- if (platform === 'web') {
64
- return MESSAGE_FALLBACK_EVENTS.has(input.eventName);
84
+ return {
85
+ breadcrumb,
86
+ sendMessage
87
+ };
88
+ }
89
+
90
+ /** Never throw or leave a rejected Sentry promise unhandled. */
91
+ function neverBreakSdk(run) {
92
+ try {
93
+ const result = run();
94
+ if (result && typeof result.then === 'function') {
95
+ Promise.resolve(result).catch(() => undefined);
96
+ }
97
+ } catch {
98
+ // Activity logging must not affect the SDK.
65
99
  }
66
- // Native dev: primary Sentry channel (Messages). Release: fallback when Logs fail.
67
- return true;
68
100
  }
69
- function emitStructuredLog(sentry, level, eventName, metadata) {
101
+ function tryStructuredLog(sentry, level, eventName, attributes) {
70
102
  try {
71
103
  const loggerFn = sentry.logger?.[level];
72
104
  if (typeof loggerFn !== 'function') {
73
105
  return false;
74
106
  }
75
107
  loggerFn.call(sentry.logger, eventName, {
76
- ...metadata,
108
+ ...attributes,
77
109
  event_name: eventName
78
110
  });
79
111
  return true;
@@ -81,52 +113,30 @@ function emitStructuredLog(sentry, level, eventName, metadata) {
81
113
  return false;
82
114
  }
83
115
  }
84
- function sentryInitOptions(dsn, environment, platform) {
85
- const base = {
86
- dsn,
87
- environment,
88
- enableAutoConsoleLogs: false,
89
- tracesSampleRate: 0,
90
- enableAutoSessionTracking: false,
91
- sendDefaultPii: false
92
- };
93
- if (usesStructuredSentryLogs(platform)) {
94
- return {
95
- ...base,
96
- enableLogs: true
97
- };
116
+ function isUnitTestRun() {
117
+ const env = globalThis.process?.env;
118
+ return env?.NODE_ENV === 'test';
119
+ }
120
+ function deferOnNative(platform, run) {
121
+ const sync = platform === 'web' || isUnitTestRun();
122
+ if (sync) {
123
+ run();
124
+ } else {
125
+ setTimeout(run, 0);
98
126
  }
99
-
100
- // Native dev: JS-only client — captureMessage without Sentry Logs (Metro-safe).
101
- return {
102
- ...base,
103
- enableLogs: false,
104
- enableNative: false,
105
- autoInitializeNativeSdk: false,
106
- defaultIntegrations: false,
107
- integrations: []
108
- };
109
127
  }
110
- function emitDevActivityMirror(level, eventName, metadata) {
111
- if (typeof __DEV__ === 'undefined' || !__DEV__) {
128
+ function mirrorToDevConsole(level, eventName, metadata) {
129
+ if (!isDevBuild()) {
112
130
  return;
113
131
  }
114
- const consoleFn = level === 'error' ? console.error : level === 'warn' ? console.warn : console.info;
115
- // Direct console in dev avoids a one-time Metro "unknown module undefined" glitch
116
- // when Sentry integrations run alongside consoleSandbox after captureMessage.
117
- consoleFn(`[Adaxon activity] ${eventName}`, metadata);
118
- }
119
- function activityTags(eventName, platform) {
120
- return {
121
- adaxon_event: eventName,
122
- adaxon_platform: platform,
123
- adaxon_sdk_version: SDK_VERSION
124
- };
132
+ const log = level === 'error' ? console.error : level === 'warn' ? console.warn : console.info;
133
+ log(`[Adaxon activity] ${eventName}`, metadata);
125
134
  }
126
135
  class AdaxonLoggerService {
127
136
  enabled = false;
128
137
  platform = 'web';
129
138
  sentryReady = false;
139
+ ownsSentryClient = false;
130
140
  configure(input) {
131
141
  this.enabled = Boolean(input.enabled);
132
142
  this.platform = input.platform;
@@ -141,6 +151,7 @@ class AdaxonLoggerService {
141
151
  this.enabled = false;
142
152
  this.sentryReady = false;
143
153
  this.platform = 'web';
154
+ this.ownsSentryClient = false;
144
155
  cachedSentryModule = undefined;
145
156
  missingSentryPeerWarningShown = false;
146
157
  }
@@ -149,71 +160,90 @@ class AdaxonLoggerService {
149
160
  return;
150
161
  }
151
162
  const level = input.level ?? DEFAULT_LEVEL;
152
- const metadata = sanitizeLogMetadata({
163
+ const baseMetadata = {
153
164
  ...input.metadata,
154
165
  sdk_version: SDK_VERSION,
155
166
  platform: this.platform,
156
167
  timestamp: new Date().toISOString()
168
+ };
169
+ mirrorToDevConsole(level, input.eventName, sanitizeLogMetadata(baseMetadata));
170
+ const sentryAttributes = sanitizeLogMetadata({
171
+ ...baseMetadata,
172
+ event_name: input.eventName
173
+ }, {
174
+ forSentry: true
157
175
  });
158
- const tags = activityTags(input.eventName, this.platform);
159
- this.safe(() => {
160
- const sentry = loadSentryModule();
161
- if (!sentry || !this.sentryReady) {
162
- return;
163
- }
164
- const structuredLogSent = usesStructuredSentryLogs(this.platform) ? emitStructuredLog(sentry, level, input.eventName, metadata) : false;
165
- if (level === 'warn' || level === 'error') {
166
- sentry.addBreadcrumb({
167
- category: 'adaxon.sdk',
168
- message: input.eventName,
169
- level: sentryLevel(level, 'breadcrumb'),
170
- data: metadata,
171
- timestamp: Date.now() / 1000
172
- });
173
- }
174
- if (shouldCaptureMessage(input, level, structuredLogSent, this.platform) && typeof sentry.captureMessage === 'function') {
175
- sentry.captureMessage(input.eventName, {
176
- level: sentryLevel(level, 'message'),
177
- tags,
178
- extra: metadata,
179
- fingerprint: ['adaxon.activity', input.eventName]
180
- });
181
- }
182
- if (input.captureAsEvent && input.error !== undefined) {
183
- const captureError = input.error instanceof Error ? input.error : new Error(AdaxonSdkError.messageOf(input.error, `Adaxon SDK activity: ${input.eventName}`));
184
- sentry.captureException(captureError, {
185
- tags,
186
- extra: metadata
187
- });
188
- }
189
- });
190
- emitDevActivityMirror(level, input.eventName, metadata);
176
+ deferOnNative(this.platform, () => neverBreakSdk(() => this.pushToSentry(input, level, sentryAttributes)));
177
+ }
178
+ pushToSentry(input, level, sentryAttributes) {
179
+ const sentry = loadSentryModule();
180
+ if (!sentry || !this.sentryReady) {
181
+ return;
182
+ }
183
+ const sentStructuredLog = usesSentryLogs(this.platform) && tryStructuredLog(sentry, level, input.eventName, sentryAttributes);
184
+ const plan = planSentryDelivery(input, level, this.platform, sentStructuredLog, this.ownsSentryClient);
185
+ const tags = {
186
+ adaxon_event: input.eventName,
187
+ adaxon_platform: this.platform,
188
+ adaxon_sdk_version: SDK_VERSION
189
+ };
190
+ if (plan.breadcrumb) {
191
+ const category = plan.breadcrumb;
192
+ neverBreakSdk(() => sentry.addBreadcrumb({
193
+ category,
194
+ message: input.eventName,
195
+ level: sentryLevel(level, 'breadcrumb'),
196
+ data: sentryAttributes,
197
+ timestamp: Date.now() / 1000
198
+ }));
199
+ }
200
+ if (plan.sendMessage && typeof sentry.captureMessage === 'function') {
201
+ neverBreakSdk(() => sentry.captureMessage(input.eventName, {
202
+ level: sentryLevel(level, 'message'),
203
+ tags,
204
+ extra: sentryAttributes
205
+ }));
206
+ }
207
+ if (input.captureAsEvent && input.error !== undefined && this.ownsSentryClient) {
208
+ const err = input.error instanceof Error ? input.error : new Error(AdaxonSdkError.messageOf(input.error, `Adaxon SDK activity: ${input.eventName}`));
209
+ neverBreakSdk(() => sentry.captureException(err, {
210
+ tags,
211
+ extra: sentryAttributes
212
+ }));
213
+ }
191
214
  }
192
215
  ensureSentry(dsn, testMode) {
193
- this.safe(() => {
216
+ neverBreakSdk(() => {
194
217
  const sentry = loadSentryModule();
195
218
  if (!sentry) {
196
219
  return;
197
220
  }
221
+ this.ownsSentryClient = false;
198
222
  if (!sentry.getClient()) {
199
223
  if (!dsn?.trim()) {
200
224
  return;
201
225
  }
202
- const environment = testMode ? 'adaxon-sdk-test' : 'adaxon-sdk';
203
- sentry.init(sentryInitOptions(dsn.trim(), environment, this.platform));
226
+ sentry.init({
227
+ dsn: dsn.trim(),
228
+ environment: testMode ? 'adaxon-sdk-test' : 'adaxon-sdk',
229
+ enableLogs: usesSentryLogs(this.platform),
230
+ enableAutoConsoleLogs: false,
231
+ enableNative: false,
232
+ autoInitializeNativeSdk: false,
233
+ defaultIntegrations: false,
234
+ integrations: [],
235
+ tracesSampleRate: 0,
236
+ enableAutoSessionTracking: false,
237
+ sendDefaultPii: false
238
+ });
239
+ this.ownsSentryClient = true;
240
+ }
241
+ for (const [key, value] of [['adaxon_sdk_version', SDK_VERSION], ['adaxon_platform', this.platform]]) {
242
+ sentry.setTag(key, value);
204
243
  }
205
- sentry.setTag('adaxon_sdk_version', SDK_VERSION);
206
- sentry.setTag('adaxon_platform', this.platform);
207
244
  this.sentryReady = true;
208
245
  });
209
246
  }
210
- safe(run) {
211
- try {
212
- run();
213
- } catch {
214
- // Logging must never interrupt SDK behavior.
215
- }
216
- }
217
247
  }
218
248
  export const AdaxonLogger = new AdaxonLoggerService();
219
249
  export function logSdkActivity(input) {
@@ -6,19 +6,28 @@ const MAX_NESTED_KEYS = 40;
6
6
  function truncate(value) {
7
7
  return value.length <= MAX_STRING_LENGTH ? value : `${value.slice(0, MAX_STRING_LENGTH)}…`;
8
8
  }
9
- function sanitizeField(key, value) {
9
+ function sanitizeField(key, value, forSentry) {
10
10
  if (SENSITIVE_KEY.test(key)) {
11
11
  return '[redacted]';
12
12
  }
13
13
  if (value === null || value === undefined) {
14
- return value;
14
+ return forSentry ? undefined : value;
15
15
  }
16
16
  if (typeof value === 'string') {
17
17
  return truncate(value);
18
18
  }
19
- if (typeof value === 'number' || typeof value === 'boolean') {
19
+ if (typeof value === 'boolean') {
20
20
  return value;
21
21
  }
22
+ if (typeof value === 'number') {
23
+ if (Number.isFinite(value)) {
24
+ return value;
25
+ }
26
+ return forSentry ? undefined : value;
27
+ }
28
+ if (forSentry) {
29
+ return truncate(String(value));
30
+ }
22
31
  if (Array.isArray(value)) {
23
32
  return value.map(item => typeof item === 'string' ? truncate(item) : item);
24
33
  }
@@ -32,14 +41,19 @@ function sanitizeField(key, value) {
32
41
  return String(value);
33
42
  }
34
43
 
35
- /** Redact secrets and cap string size before activity logs leave the SDK. */
36
- export function sanitizeLogMetadata(metadata) {
44
+ /** Redact secrets, cap strings; optional Sentry flattening (primitives only). */
45
+ export function sanitizeLogMetadata(metadata, options = {}) {
37
46
  if (!metadata) {
38
47
  return {};
39
48
  }
49
+ const forSentry = Boolean(options.forSentry);
40
50
  const output = {};
41
51
  for (const [key, value] of Object.entries(metadata)) {
42
- output[key] = sanitizeField(key, value);
52
+ const sanitized = sanitizeField(key, value, forSentry);
53
+ if (forSentry && sanitized === undefined) {
54
+ continue;
55
+ }
56
+ output[key] = sanitized;
43
57
  }
44
58
  return output;
45
59
  }
@@ -1,3 +1,3 @@
1
1
  /** Keep in sync with package.json "version" (published npm packages ship lib/ only). */
2
- export declare const SDK_VERSION = "0.1.17";
2
+ export declare const SDK_VERSION = "0.1.19";
3
3
  //# sourceMappingURL=sdkVersion.d.ts.map
@@ -18,11 +18,12 @@ declare class AdaxonLoggerService {
18
18
  private enabled;
19
19
  private platform;
20
20
  private sentryReady;
21
+ private ownsSentryClient;
21
22
  configure(input: AdaxonLoggerConfigureInput): void;
22
23
  reset(): void;
23
24
  logActivity(input: AdaxonActivityLogInput): void;
25
+ private pushToSentry;
24
26
  private ensureSentry;
25
- private safe;
26
27
  }
27
28
  export declare const AdaxonLogger: AdaxonLoggerService;
28
29
  export declare function logSdkActivity(input: AdaxonActivityLogInput): void;
@@ -1,3 +1,7 @@
1
- /** Redact secrets and cap string size before activity logs leave the SDK. */
2
- export declare function sanitizeLogMetadata(metadata: Record<string, unknown> | undefined): Record<string, unknown>;
1
+ export type SanitizeLogMetadataOptions = {
2
+ /** Flatten to string/number/boolean for Sentry Logs and captureMessage (React Native). */
3
+ forSentry?: boolean;
4
+ };
5
+ /** Redact secrets, cap strings; optional Sentry flattening (primitives only). */
6
+ export declare function sanitizeLogMetadata(metadata: Record<string, unknown> | undefined, options?: SanitizeLogMetadataOptions): Record<string, unknown>;
3
7
  //# sourceMappingURL=sanitizeLogMetadata.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-test-adx",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Adaxon React Native / Expo SDK for event tracking and offer recommendations.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",