react-native-test-adx 0.1.17 → 0.1.18

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.18';
@@ -5,12 +5,16 @@ 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']);
11
10
  let cachedSentryModule;
12
11
  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.';
12
+ function isDevBuild() {
13
+ return typeof __DEV__ !== 'undefined' && __DEV__;
14
+ }
15
+ function usesSentryLogs(platform) {
16
+ return platform === 'web' || !isDevBuild();
17
+ }
14
18
  function loadSentryModule() {
15
19
  if (cachedSentryModule !== undefined) {
16
20
  return cachedSentryModule;
@@ -24,14 +28,14 @@ function loadSentryModule() {
24
28
  return cachedSentryModule;
25
29
  }
26
30
  function warnSentryUnavailableOnce(hasDsn) {
27
- if (!hasDsn || missingSentryPeerWarningShown || typeof __DEV__ === 'undefined' || !__DEV__) {
31
+ if (!hasDsn || missingSentryPeerWarningShown || !isDevBuild()) {
28
32
  return;
29
33
  }
30
34
  if (loadSentryModule()) {
31
35
  return;
32
36
  }
33
37
  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.');
38
+ 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
39
  }
36
40
  function sentryLevel(level, target) {
37
41
  if (level === 'warn') {
@@ -46,34 +50,56 @@ function sentryLevel(level, target) {
46
50
  return 'info';
47
51
  }
48
52
 
49
- /** Sentry Logs (`logger.*`) break Metro on native dev; release + web use Logs. */
50
- function usesStructuredSentryLogs(platform) {
51
- if (platform === 'web') {
52
- return true;
53
+ /** What to send after structured Logs (if any) are attempted. */
54
+ function planSentryDelivery(input, level, platform, sentStructuredLog, ownsSentryClient) {
55
+ if (level === 'debug') {
56
+ return {
57
+ breadcrumb: null,
58
+ sendMessage: false
59
+ };
53
60
  }
54
- return typeof __DEV__ !== 'undefined' && !__DEV__;
55
- }
56
- function shouldCaptureMessage(input, level, structuredLogSent, platform) {
57
- if (level === 'debug' || structuredLogSent) {
58
- return false;
61
+ const nativeDev = platform !== 'web' && isDevBuild();
62
+ let sendMessage = false;
63
+ if (ownsSentryClient && !sentStructuredLog) {
64
+ if (input.captureAsEvent && input.error === undefined) {
65
+ sendMessage = true;
66
+ } else if (SENTRY_MESSAGE_EVENTS.has(input.eventName)) {
67
+ sendMessage = true;
68
+ } else if (nativeDev && (level === 'warn' || level === 'error')) {
69
+ sendMessage = true;
70
+ }
59
71
  }
60
- if (input.captureAsEvent && input.error === undefined) {
61
- return true;
72
+ let breadcrumb = null;
73
+ if (level === 'warn' || level === 'error') {
74
+ breadcrumb = 'adaxon.sdk';
75
+ } else if (nativeDev && !sentStructuredLog && !sendMessage) {
76
+ breadcrumb = 'adaxon.activity';
62
77
  }
63
- if (platform === 'web') {
64
- return MESSAGE_FALLBACK_EVENTS.has(input.eventName);
78
+ return {
79
+ breadcrumb,
80
+ sendMessage
81
+ };
82
+ }
83
+
84
+ /** Never throw or leave a rejected Sentry promise unhandled. */
85
+ function neverBreakSdk(run) {
86
+ try {
87
+ const result = run();
88
+ if (result && typeof result.then === 'function') {
89
+ Promise.resolve(result).catch(() => undefined);
90
+ }
91
+ } catch {
92
+ // Activity logging must not affect the SDK.
65
93
  }
66
- // Native dev: primary Sentry channel (Messages). Release: fallback when Logs fail.
67
- return true;
68
94
  }
69
- function emitStructuredLog(sentry, level, eventName, metadata) {
95
+ function tryStructuredLog(sentry, level, eventName, attributes) {
70
96
  try {
71
97
  const loggerFn = sentry.logger?.[level];
72
98
  if (typeof loggerFn !== 'function') {
73
99
  return false;
74
100
  }
75
101
  loggerFn.call(sentry.logger, eventName, {
76
- ...metadata,
102
+ ...attributes,
77
103
  event_name: eventName
78
104
  });
79
105
  return true;
@@ -81,52 +107,30 @@ function emitStructuredLog(sentry, level, eventName, metadata) {
81
107
  return false;
82
108
  }
83
109
  }
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
- };
110
+ function isUnitTestRun() {
111
+ const env = globalThis.process?.env;
112
+ return env?.NODE_ENV === 'test';
113
+ }
114
+ function deferOnNative(platform, run) {
115
+ const sync = platform === 'web' || isUnitTestRun();
116
+ if (sync) {
117
+ run();
118
+ } else {
119
+ setTimeout(run, 0);
98
120
  }
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
121
  }
110
- function emitDevActivityMirror(level, eventName, metadata) {
111
- if (typeof __DEV__ === 'undefined' || !__DEV__) {
122
+ function mirrorToDevConsole(level, eventName, metadata) {
123
+ if (!isDevBuild()) {
112
124
  return;
113
125
  }
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
- };
126
+ const log = level === 'error' ? console.error : level === 'warn' ? console.warn : console.info;
127
+ log(`[Adaxon activity] ${eventName}`, metadata);
125
128
  }
126
129
  class AdaxonLoggerService {
127
130
  enabled = false;
128
131
  platform = 'web';
129
132
  sentryReady = false;
133
+ ownsSentryClient = false;
130
134
  configure(input) {
131
135
  this.enabled = Boolean(input.enabled);
132
136
  this.platform = input.platform;
@@ -141,6 +145,7 @@ class AdaxonLoggerService {
141
145
  this.enabled = false;
142
146
  this.sentryReady = false;
143
147
  this.platform = 'web';
148
+ this.ownsSentryClient = false;
144
149
  cachedSentryModule = undefined;
145
150
  missingSentryPeerWarningShown = false;
146
151
  }
@@ -149,71 +154,90 @@ class AdaxonLoggerService {
149
154
  return;
150
155
  }
151
156
  const level = input.level ?? DEFAULT_LEVEL;
152
- const metadata = sanitizeLogMetadata({
157
+ const baseMetadata = {
153
158
  ...input.metadata,
154
159
  sdk_version: SDK_VERSION,
155
160
  platform: this.platform,
156
161
  timestamp: new Date().toISOString()
162
+ };
163
+ mirrorToDevConsole(level, input.eventName, sanitizeLogMetadata(baseMetadata));
164
+ const sentryAttributes = sanitizeLogMetadata({
165
+ ...baseMetadata,
166
+ event_name: input.eventName
167
+ }, {
168
+ forSentry: true
157
169
  });
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);
170
+ deferOnNative(this.platform, () => neverBreakSdk(() => this.pushToSentry(input, level, sentryAttributes)));
171
+ }
172
+ pushToSentry(input, level, sentryAttributes) {
173
+ const sentry = loadSentryModule();
174
+ if (!sentry || !this.sentryReady) {
175
+ return;
176
+ }
177
+ const sentStructuredLog = usesSentryLogs(this.platform) && tryStructuredLog(sentry, level, input.eventName, sentryAttributes);
178
+ const plan = planSentryDelivery(input, level, this.platform, sentStructuredLog, this.ownsSentryClient);
179
+ const tags = {
180
+ adaxon_event: input.eventName,
181
+ adaxon_platform: this.platform,
182
+ adaxon_sdk_version: SDK_VERSION
183
+ };
184
+ if (plan.breadcrumb) {
185
+ const category = plan.breadcrumb;
186
+ neverBreakSdk(() => sentry.addBreadcrumb({
187
+ category,
188
+ message: input.eventName,
189
+ level: sentryLevel(level, 'breadcrumb'),
190
+ data: sentryAttributes,
191
+ timestamp: Date.now() / 1000
192
+ }));
193
+ }
194
+ if (plan.sendMessage && typeof sentry.captureMessage === 'function') {
195
+ neverBreakSdk(() => sentry.captureMessage(input.eventName, {
196
+ level: sentryLevel(level, 'message'),
197
+ tags,
198
+ extra: sentryAttributes
199
+ }));
200
+ }
201
+ if (input.captureAsEvent && input.error !== undefined && this.ownsSentryClient) {
202
+ const err = input.error instanceof Error ? input.error : new Error(AdaxonSdkError.messageOf(input.error, `Adaxon SDK activity: ${input.eventName}`));
203
+ neverBreakSdk(() => sentry.captureException(err, {
204
+ tags,
205
+ extra: sentryAttributes
206
+ }));
207
+ }
191
208
  }
192
209
  ensureSentry(dsn, testMode) {
193
- this.safe(() => {
210
+ neverBreakSdk(() => {
194
211
  const sentry = loadSentryModule();
195
212
  if (!sentry) {
196
213
  return;
197
214
  }
215
+ this.ownsSentryClient = false;
198
216
  if (!sentry.getClient()) {
199
217
  if (!dsn?.trim()) {
200
218
  return;
201
219
  }
202
- const environment = testMode ? 'adaxon-sdk-test' : 'adaxon-sdk';
203
- sentry.init(sentryInitOptions(dsn.trim(), environment, this.platform));
220
+ sentry.init({
221
+ dsn: dsn.trim(),
222
+ environment: testMode ? 'adaxon-sdk-test' : 'adaxon-sdk',
223
+ enableLogs: usesSentryLogs(this.platform),
224
+ enableAutoConsoleLogs: false,
225
+ enableNative: false,
226
+ autoInitializeNativeSdk: false,
227
+ defaultIntegrations: false,
228
+ integrations: [],
229
+ tracesSampleRate: 0,
230
+ enableAutoSessionTracking: false,
231
+ sendDefaultPii: false
232
+ });
233
+ this.ownsSentryClient = true;
234
+ }
235
+ for (const [key, value] of [['adaxon_sdk_version', SDK_VERSION], ['adaxon_platform', this.platform]]) {
236
+ sentry.setTag(key, value);
204
237
  }
205
- sentry.setTag('adaxon_sdk_version', SDK_VERSION);
206
- sentry.setTag('adaxon_platform', this.platform);
207
238
  this.sentryReady = true;
208
239
  });
209
240
  }
210
- safe(run) {
211
- try {
212
- run();
213
- } catch {
214
- // Logging must never interrupt SDK behavior.
215
- }
216
- }
217
241
  }
218
242
  export const AdaxonLogger = new AdaxonLoggerService();
219
243
  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.18";
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.18",
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",