react-native-test-adx 0.1.16 → 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.16';
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') {
@@ -45,27 +49,57 @@ function sentryLevel(level, target) {
45
49
  }
46
50
  return 'info';
47
51
  }
48
- function shouldCaptureMessage(input, level, structuredLogSent, platform) {
49
- if (level === 'debug' || structuredLogSent) {
50
- return false;
52
+
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
+ };
60
+ }
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
+ }
51
71
  }
52
- if (input.captureAsEvent && input.error === undefined) {
53
- 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';
54
77
  }
55
- if (platform === 'web') {
56
- 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.
57
93
  }
58
- // Native dev + release: message fallback when Sentry Logs API did not accept the event.
59
- return true;
60
94
  }
61
- function emitStructuredLog(sentry, level, eventName, metadata) {
95
+ function tryStructuredLog(sentry, level, eventName, attributes) {
62
96
  try {
63
97
  const loggerFn = sentry.logger?.[level];
64
98
  if (typeof loggerFn !== 'function') {
65
99
  return false;
66
100
  }
67
101
  loggerFn.call(sentry.logger, eventName, {
68
- ...metadata,
102
+ ...attributes,
69
103
  event_name: eventName
70
104
  });
71
105
  return true;
@@ -73,43 +107,30 @@ function emitStructuredLog(sentry, level, eventName, metadata) {
73
107
  return false;
74
108
  }
75
109
  }
76
- function sentryInitOptions(dsn, environment) {
77
- return {
78
- dsn,
79
- environment,
80
- enableLogs: true,
81
- enableAutoConsoleLogs: false,
82
- tracesSampleRate: 0,
83
- enableAutoSessionTracking: false,
84
- sendDefaultPii: false
85
- };
110
+ function isUnitTestRun() {
111
+ const env = globalThis.process?.env;
112
+ return env?.NODE_ENV === 'test';
86
113
  }
87
- function emitDevActivityMirror(level, eventName, metadata) {
88
- if (typeof __DEV__ === 'undefined' || !__DEV__) {
89
- return;
114
+ function deferOnNative(platform, run) {
115
+ const sync = platform === 'web' || isUnitTestRun();
116
+ if (sync) {
117
+ run();
118
+ } else {
119
+ setTimeout(run, 0);
90
120
  }
91
- const consoleFn = level === 'error' ? console.error : level === 'warn' ? console.warn : console.info;
92
- const write = () => {
93
- consoleFn(`[Adaxon activity] ${eventName}`, metadata);
94
- };
95
- const sentry = loadSentryModule();
96
- if (typeof sentry?.consoleSandbox === 'function') {
97
- sentry.consoleSandbox(write);
121
+ }
122
+ function mirrorToDevConsole(level, eventName, metadata) {
123
+ if (!isDevBuild()) {
98
124
  return;
99
125
  }
100
- write();
101
- }
102
- function activityTags(eventName, platform) {
103
- return {
104
- adaxon_event: eventName,
105
- adaxon_platform: platform,
106
- adaxon_sdk_version: SDK_VERSION
107
- };
126
+ const log = level === 'error' ? console.error : level === 'warn' ? console.warn : console.info;
127
+ log(`[Adaxon activity] ${eventName}`, metadata);
108
128
  }
109
129
  class AdaxonLoggerService {
110
130
  enabled = false;
111
131
  platform = 'web';
112
132
  sentryReady = false;
133
+ ownsSentryClient = false;
113
134
  configure(input) {
114
135
  this.enabled = Boolean(input.enabled);
115
136
  this.platform = input.platform;
@@ -124,6 +145,7 @@ class AdaxonLoggerService {
124
145
  this.enabled = false;
125
146
  this.sentryReady = false;
126
147
  this.platform = 'web';
148
+ this.ownsSentryClient = false;
127
149
  cachedSentryModule = undefined;
128
150
  missingSentryPeerWarningShown = false;
129
151
  }
@@ -132,71 +154,90 @@ class AdaxonLoggerService {
132
154
  return;
133
155
  }
134
156
  const level = input.level ?? DEFAULT_LEVEL;
135
- const metadata = sanitizeLogMetadata({
157
+ const baseMetadata = {
136
158
  ...input.metadata,
137
159
  sdk_version: SDK_VERSION,
138
160
  platform: this.platform,
139
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
140
169
  });
141
- const tags = activityTags(input.eventName, this.platform);
142
- this.safe(() => {
143
- const sentry = loadSentryModule();
144
- if (!sentry || !this.sentryReady) {
145
- return;
146
- }
147
- const structuredLogSent = emitStructuredLog(sentry, level, input.eventName, metadata);
148
- if (level === 'warn' || level === 'error') {
149
- sentry.addBreadcrumb({
150
- category: 'adaxon.sdk',
151
- message: input.eventName,
152
- level: sentryLevel(level, 'breadcrumb'),
153
- data: metadata,
154
- timestamp: Date.now() / 1000
155
- });
156
- }
157
- if (shouldCaptureMessage(input, level, structuredLogSent, this.platform) && typeof sentry.captureMessage === 'function') {
158
- sentry.captureMessage(input.eventName, {
159
- level: sentryLevel(level, 'message'),
160
- tags,
161
- extra: metadata,
162
- fingerprint: ['adaxon.activity', input.eventName]
163
- });
164
- }
165
- if (input.captureAsEvent && input.error !== undefined) {
166
- const captureError = input.error instanceof Error ? input.error : new Error(AdaxonSdkError.messageOf(input.error, `Adaxon SDK activity: ${input.eventName}`));
167
- sentry.captureException(captureError, {
168
- tags,
169
- extra: metadata
170
- });
171
- }
172
- });
173
- 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
+ }
174
208
  }
175
209
  ensureSentry(dsn, testMode) {
176
- this.safe(() => {
210
+ neverBreakSdk(() => {
177
211
  const sentry = loadSentryModule();
178
212
  if (!sentry) {
179
213
  return;
180
214
  }
215
+ this.ownsSentryClient = false;
181
216
  if (!sentry.getClient()) {
182
217
  if (!dsn?.trim()) {
183
218
  return;
184
219
  }
185
- const environment = testMode ? 'adaxon-sdk-test' : 'adaxon-sdk';
186
- sentry.init(sentryInitOptions(dsn.trim(), environment));
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);
187
237
  }
188
- sentry.setTag('adaxon_sdk_version', SDK_VERSION);
189
- sentry.setTag('adaxon_platform', this.platform);
190
238
  this.sentryReady = true;
191
239
  });
192
240
  }
193
- safe(run) {
194
- try {
195
- run();
196
- } catch {
197
- // Logging must never interrupt SDK behavior.
198
- }
199
- }
200
241
  }
201
242
  export const AdaxonLogger = new AdaxonLoggerService();
202
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.16";
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.16",
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",