@luciq/react-native 19.2.2 → 19.3.0

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 (35) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +87 -0
  3. package/android/native.gradle +1 -1
  4. package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqAPMModule.java +202 -117
  5. package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqReactnativeModule.java +20 -0
  6. package/dist/constants/Strings.d.ts +9 -0
  7. package/dist/constants/Strings.js +12 -0
  8. package/dist/index.d.ts +2 -1
  9. package/dist/index.js +2 -1
  10. package/dist/models/CustomSpan.d.ts +47 -0
  11. package/dist/models/CustomSpan.js +82 -0
  12. package/dist/modules/APM.d.ts +58 -0
  13. package/dist/modules/APM.js +62 -0
  14. package/dist/native/NativeAPM.d.ts +3 -0
  15. package/dist/native/NativeLuciq.d.ts +1 -0
  16. package/dist/utils/CustomSpansManager.d.ts +38 -0
  17. package/dist/utils/CustomSpansManager.js +173 -0
  18. package/ios/RNLuciq/LuciqAPMBridge.h +13 -0
  19. package/ios/RNLuciq/LuciqAPMBridge.m +55 -0
  20. package/ios/RNLuciq/LuciqReactBridge.m +12 -0
  21. package/ios/RNLuciq/Util/LCQAPM+PrivateAPIs.h +1 -0
  22. package/ios/native.rb +1 -1
  23. package/package.json +1 -1
  24. package/scripts/releases/changelog_to_slack_formatter.sh +9 -0
  25. package/scripts/releases/get_job_approver.sh +60 -0
  26. package/scripts/releases/get_release_notes.sh +22 -0
  27. package/scripts/releases/get_sdk_version.sh +5 -0
  28. package/scripts/releases/get_slack_id_from_username.sh +24 -0
  29. package/src/constants/Strings.ts +24 -0
  30. package/src/index.ts +2 -0
  31. package/src/models/CustomSpan.ts +102 -0
  32. package/src/modules/APM.ts +72 -0
  33. package/src/native/NativeAPM.ts +7 -0
  34. package/src/native/NativeLuciq.ts +1 -0
  35. package/src/utils/CustomSpansManager.ts +202 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Represents a custom span for performance tracking.
3
+ * A span measures the duration of an operation and reports it to the native SDK.
4
+ */
5
+ export class CustomSpan {
6
+ name;
7
+ startTime; // Date.now() in milliseconds
8
+ startMonotonic; // performance.now() in milliseconds
9
+ endTime;
10
+ duration;
11
+ hasEnded = false;
12
+ endPromise;
13
+ unregisterCallback;
14
+ syncCallback;
15
+ /**
16
+ * Creates a new custom span. The span starts immediately upon creation.
17
+ * @internal - Use APM.startCustomSpan() instead
18
+ */
19
+ constructor(name, unregisterCallback, syncCallback) {
20
+ this.name = name;
21
+ this.startTime = Date.now();
22
+ this.startMonotonic = performance.now();
23
+ this.unregisterCallback = unregisterCallback;
24
+ this.syncCallback = syncCallback;
25
+ }
26
+ /**
27
+ * Ends this custom span and reports it to the native SDK.
28
+ * This method is idempotent - calling it multiple times is safe.
29
+ * Subsequent calls will wait for the first call to complete.
30
+ */
31
+ async end() {
32
+ // Thread-safe check using Promise-based locking
33
+ if (this.hasEnded) {
34
+ if (this.endPromise) {
35
+ await this.endPromise;
36
+ }
37
+ return;
38
+ }
39
+ // Create lock and mark as ended
40
+ let resolveEnd;
41
+ this.endPromise = new Promise((resolve) => {
42
+ resolveEnd = resolve;
43
+ });
44
+ this.hasEnded = true;
45
+ try {
46
+ // Unregister from active spans
47
+ this.unregisterCallback(this);
48
+ // Calculate duration using monotonic clock
49
+ const endMonotonic = performance.now();
50
+ this.duration = endMonotonic - this.startMonotonic;
51
+ // Calculate end time using wall clock
52
+ this.endTime = this.startTime + this.duration;
53
+ // Convert to microseconds for native SDK
54
+ const startMicros = this.startTime * 1000;
55
+ const endMicros = this.endTime * 1000;
56
+ // Send to native SDK
57
+ await this.syncCallback(this.name, startMicros, endMicros);
58
+ }
59
+ finally {
60
+ // Release lock
61
+ resolveEnd();
62
+ }
63
+ }
64
+ /**
65
+ * Get the span name
66
+ */
67
+ getName() {
68
+ return this.name;
69
+ }
70
+ /**
71
+ * Check if the span has ended
72
+ */
73
+ isEnded() {
74
+ return this.hasEnded;
75
+ }
76
+ /**
77
+ * Get the span duration in milliseconds (only available after end())
78
+ */
79
+ getDuration() {
80
+ return this.duration;
81
+ }
82
+ }
@@ -1,3 +1,4 @@
1
+ import type { CustomSpan } from '../models/CustomSpan';
1
2
  /**
2
3
  * Enables or disables APM
3
4
  * @param isEnabled
@@ -80,3 +81,60 @@ export declare const _lcqSleep: () => void;
80
81
  * @param isEnabled
81
82
  */
82
83
  export declare const setScreenRenderingEnabled: (isEnabled: boolean) => void;
84
+ /**
85
+ * Starts a custom span for performance tracking.
86
+ *
87
+ * A custom span measures the duration of an arbitrary operation that is not
88
+ * automatically tracked by the SDK. The span must be manually ended by calling
89
+ * the `end()` method on the returned span object.
90
+ *
91
+ * @param name - The name of the span. Cannot be empty. Max 150 characters.
92
+ * Leading and trailing whitespace will be trimmed.
93
+ *
94
+ * @returns Promise<CustomSpan | null> - The span object to end later, or null if:
95
+ * - Name is empty after trimming
96
+ * - SDK is not initialized
97
+ * - APM is disabled
98
+ * - Custom spans feature is disabled
99
+ * - Maximum concurrent spans limit (100) reached
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const span = await APM.startCustomSpan('Load User Profile');
104
+ * if (span) {
105
+ * try {
106
+ * // ... perform operation ...
107
+ * } finally {
108
+ * await span.end();
109
+ * }
110
+ * }
111
+ * ```
112
+ */
113
+ export declare const startCustomSpan: (name: string) => Promise<CustomSpan | null>;
114
+ /**
115
+ * Records a completed custom span with pre-recorded timestamps.
116
+ *
117
+ * Use this method when you have already recorded the start and end times
118
+ * of an operation and want to report it retroactively.
119
+ *
120
+ * @param name - The name of the span. Cannot be empty. Max 150 characters.
121
+ * Leading and trailing whitespace will be trimmed.
122
+ * @param startDate - The start time of the operation
123
+ * @param endDate - The end time of the operation (must be after startDate)
124
+ *
125
+ * @returns Promise<void> - Resolves when the span has been recorded, or logs error if:
126
+ * - Name is empty after trimming
127
+ * - End date is not after start date
128
+ * - SDK is not initialized
129
+ * - APM is disabled
130
+ * - Custom spans feature is disabled
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * const start = new Date();
135
+ * // ... operation already completed ...
136
+ * const end = new Date();
137
+ * await APM.addCompletedCustomSpan('Cache Lookup', start, end);
138
+ * ```
139
+ */
140
+ export declare const addCompletedCustomSpan: (name: string, startDate: Date, endDate: Date) => Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import { Platform } from 'react-native';
2
2
  import { NativeAPM } from '../native/NativeAPM';
3
3
  import { NativeLuciq } from '../native/NativeLuciq';
4
+ import { startCustomSpan as startCustomSpanInternal, addCompletedCustomSpan as addCompletedCustomSpanInternal, } from '../utils/CustomSpansManager';
4
5
  /**
5
6
  * Enables or disables APM
6
7
  * @param isEnabled
@@ -109,3 +110,64 @@ export const _lcqSleep = () => {
109
110
  export const setScreenRenderingEnabled = (isEnabled) => {
110
111
  NativeAPM.setScreenRenderingEnabled(isEnabled);
111
112
  };
113
+ /**
114
+ * Starts a custom span for performance tracking.
115
+ *
116
+ * A custom span measures the duration of an arbitrary operation that is not
117
+ * automatically tracked by the SDK. The span must be manually ended by calling
118
+ * the `end()` method on the returned span object.
119
+ *
120
+ * @param name - The name of the span. Cannot be empty. Max 150 characters.
121
+ * Leading and trailing whitespace will be trimmed.
122
+ *
123
+ * @returns Promise<CustomSpan | null> - The span object to end later, or null if:
124
+ * - Name is empty after trimming
125
+ * - SDK is not initialized
126
+ * - APM is disabled
127
+ * - Custom spans feature is disabled
128
+ * - Maximum concurrent spans limit (100) reached
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * const span = await APM.startCustomSpan('Load User Profile');
133
+ * if (span) {
134
+ * try {
135
+ * // ... perform operation ...
136
+ * } finally {
137
+ * await span.end();
138
+ * }
139
+ * }
140
+ * ```
141
+ */
142
+ export const startCustomSpan = async (name) => {
143
+ return startCustomSpanInternal(name);
144
+ };
145
+ /**
146
+ * Records a completed custom span with pre-recorded timestamps.
147
+ *
148
+ * Use this method when you have already recorded the start and end times
149
+ * of an operation and want to report it retroactively.
150
+ *
151
+ * @param name - The name of the span. Cannot be empty. Max 150 characters.
152
+ * Leading and trailing whitespace will be trimmed.
153
+ * @param startDate - The start time of the operation
154
+ * @param endDate - The end time of the operation (must be after startDate)
155
+ *
156
+ * @returns Promise<void> - Resolves when the span has been recorded, or logs error if:
157
+ * - Name is empty after trimming
158
+ * - End date is not after start date
159
+ * - SDK is not initialized
160
+ * - APM is disabled
161
+ * - Custom spans feature is disabled
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * const start = new Date();
166
+ * // ... operation already completed ...
167
+ * const end = new Date();
168
+ * await APM.addCompletedCustomSpan('Cache Lookup', start, end);
169
+ * ```
170
+ */
171
+ export const addCompletedCustomSpan = async (name, startDate, endDate) => {
172
+ return addCompletedCustomSpanInternal(name, startDate, endDate);
173
+ };
@@ -14,6 +14,9 @@ export interface ApmNativeModule extends NativeModule {
14
14
  endUITrace(): void;
15
15
  lcqSleep(): void;
16
16
  setScreenRenderingEnabled(isEnabled: boolean): void;
17
+ syncCustomSpan(name: string, startTimestamp: number, endTimestamp: number): Promise<void>;
18
+ isCustomSpanEnabled(): Promise<boolean>;
19
+ isAPMEnabled(): Promise<boolean>;
17
20
  }
18
21
  export declare const NativeAPM: ApmNativeModule;
19
22
  export declare const emitter: NativeEventEmitter;
@@ -8,6 +8,7 @@ import type { ThemeConfig } from '../models/ThemeConfig';
8
8
  export interface LuciqNativeModule extends NativeModule {
9
9
  getConstants(): NativeConstants;
10
10
  setEnabled(isEnabled: boolean): void;
11
+ isBuilt(): Promise<boolean>;
11
12
  init(token: string, invocationEvents: InvocationEvent[], debugLogsLevel: LogLevel, useNativeNetworkInterception: boolean, codePushVersion?: string, appVariant?: string, options?: {
12
13
  ignoreAndroidSecureFlag?: boolean;
13
14
  }, overAirVersion?: OverAirUpdate): void;
@@ -0,0 +1,38 @@
1
+ import { CustomSpan } from '../models/CustomSpan';
2
+ /**
3
+ * Starts a custom span for performance tracking.
4
+ *
5
+ * A custom span measures the duration of an arbitrary operation that is not
6
+ * automatically tracked by the SDK. The span must be manually ended by calling
7
+ * the `end()` method on the returned span object.
8
+ *
9
+ * @param name - The name of the span. Cannot be empty. Max 150 characters.
10
+ * Leading and trailing whitespace will be trimmed.
11
+ *
12
+ * @returns Promise<CustomSpan | null> - The span object to end later, or null if:
13
+ * - Name is empty after trimming
14
+ * - SDK is not initialized
15
+ * - APM is disabled
16
+ * - Custom spans feature is disabled
17
+ * - Maximum concurrent spans limit (100) reached
18
+ */
19
+ export declare const startCustomSpan: (name: string) => Promise<CustomSpan | null>;
20
+ /**
21
+ * Records a completed custom span with pre-recorded timestamps.
22
+ *
23
+ * Use this method when you have already recorded the start and end times
24
+ * of an operation and want to report it retroactively.
25
+ *
26
+ * @param name - The name of the span. Cannot be empty. Max 150 characters.
27
+ * Leading and trailing whitespace will be trimmed.
28
+ * @param startDate - The start time of the operation
29
+ * @param endDate - The end time of the operation (must be after startDate)
30
+ *
31
+ * @returns Promise<void>
32
+ */
33
+ export declare const addCompletedCustomSpan: (name: string, startDate: Date, endDate: Date) => Promise<void>;
34
+ /**
35
+ * Test-only helper to clear active spans between tests.
36
+ * @internal
37
+ */
38
+ export declare const __resetCustomSpansForTests: () => void;
@@ -0,0 +1,173 @@
1
+ import { NativeAPM } from '../native/NativeAPM';
2
+ import { NativeLuciq } from '../native/NativeLuciq';
3
+ import { CustomSpan } from '../models/CustomSpan';
4
+ import { LuciqStrings } from '../constants/Strings';
5
+ /**
6
+ * Tracks currently active custom spans
7
+ * @internal
8
+ */
9
+ const activeSpans = new Set();
10
+ /**
11
+ * Maximum concurrent custom spans allowed at any time
12
+ * @internal
13
+ */
14
+ const MAX_CONCURRENT_SPANS = 100;
15
+ /**
16
+ * Internal: unregister a span from active tracking
17
+ * @internal
18
+ */
19
+ const unregisterSpan = (span) => {
20
+ activeSpans.delete(span);
21
+ };
22
+ /**
23
+ * Internal: sync custom span data to native SDK
24
+ * @internal
25
+ */
26
+ const syncCustomSpan = async (name, startTimestamp, endTimestamp) => {
27
+ // Validate inputs (safety net)
28
+ if (!name || name.trim().length === 0) {
29
+ console.error(LuciqStrings.customSpanNameEmpty);
30
+ return;
31
+ }
32
+ if (endTimestamp <= startTimestamp) {
33
+ console.error(LuciqStrings.customSpanEndTimeBeforeStartTime);
34
+ return;
35
+ }
36
+ // Truncate name if needed (safety net)
37
+ let spanName = name.trim();
38
+ if (spanName.length > 150) {
39
+ spanName = spanName.substring(0, 150);
40
+ }
41
+ await NativeAPM.syncCustomSpan(spanName, startTimestamp, endTimestamp);
42
+ };
43
+ /**
44
+ * Starts a custom span for performance tracking.
45
+ *
46
+ * A custom span measures the duration of an arbitrary operation that is not
47
+ * automatically tracked by the SDK. The span must be manually ended by calling
48
+ * the `end()` method on the returned span object.
49
+ *
50
+ * @param name - The name of the span. Cannot be empty. Max 150 characters.
51
+ * Leading and trailing whitespace will be trimmed.
52
+ *
53
+ * @returns Promise<CustomSpan | null> - The span object to end later, or null if:
54
+ * - Name is empty after trimming
55
+ * - SDK is not initialized
56
+ * - APM is disabled
57
+ * - Custom spans feature is disabled
58
+ * - Maximum concurrent spans limit (100) reached
59
+ */
60
+ export const startCustomSpan = async (name) => {
61
+ try {
62
+ // Validate name
63
+ const trimmedName = name.trim();
64
+ if (trimmedName.length === 0) {
65
+ console.error(LuciqStrings.customSpanNameEmpty);
66
+ return null;
67
+ }
68
+ // Check SDK initialization
69
+ const isInitialized = await NativeLuciq.isBuilt();
70
+ if (!isInitialized) {
71
+ console.error(LuciqStrings.customSpanSDKNotInitializedMessage);
72
+ return null;
73
+ }
74
+ // Check APM enabled
75
+ const isAPMEnabled = await NativeAPM.isAPMEnabled();
76
+ if (!isAPMEnabled) {
77
+ console.log(LuciqStrings.customSpanAPMDisabledMessage);
78
+ return null;
79
+ }
80
+ // Check custom spans enabled
81
+ const isCustomSpanEnabled = await NativeAPM.isCustomSpanEnabled();
82
+ if (!isCustomSpanEnabled) {
83
+ console.log(LuciqStrings.customSpanDisabled);
84
+ return null;
85
+ }
86
+ // Check concurrent span limit
87
+ if (activeSpans.size >= MAX_CONCURRENT_SPANS) {
88
+ console.error(LuciqStrings.customSpanLimitReached);
89
+ return null;
90
+ }
91
+ // Truncate name if needed
92
+ let spanName = trimmedName;
93
+ if (spanName.length > 150) {
94
+ spanName = spanName.substring(0, 150);
95
+ console.log(LuciqStrings.customSpanNameTruncated);
96
+ }
97
+ // Create and register span with callbacks
98
+ const span = new CustomSpan(spanName, unregisterSpan, syncCustomSpan);
99
+ activeSpans.add(span);
100
+ return span;
101
+ }
102
+ catch (error) {
103
+ console.error('[CustomSpan] Error starting span:', error);
104
+ return null;
105
+ }
106
+ };
107
+ /**
108
+ * Records a completed custom span with pre-recorded timestamps.
109
+ *
110
+ * Use this method when you have already recorded the start and end times
111
+ * of an operation and want to report it retroactively.
112
+ *
113
+ * @param name - The name of the span. Cannot be empty. Max 150 characters.
114
+ * Leading and trailing whitespace will be trimmed.
115
+ * @param startDate - The start time of the operation
116
+ * @param endDate - The end time of the operation (must be after startDate)
117
+ *
118
+ * @returns Promise<void>
119
+ */
120
+ export const addCompletedCustomSpan = async (name, startDate, endDate) => {
121
+ try {
122
+ // Validate name
123
+ const trimmedName = name.trim();
124
+ if (trimmedName.length === 0) {
125
+ console.error(LuciqStrings.customSpanNameEmpty);
126
+ return;
127
+ }
128
+ // Validate timestamps
129
+ if (endDate <= startDate) {
130
+ console.error(LuciqStrings.customSpanEndTimeBeforeStartTime);
131
+ return;
132
+ }
133
+ // Check SDK initialization
134
+ const isInitialized = await NativeLuciq.isBuilt();
135
+ if (!isInitialized) {
136
+ console.error(LuciqStrings.customSpanSDKNotInitializedMessage);
137
+ return;
138
+ }
139
+ // Check APM enabled
140
+ const isAPMEnabled = await NativeAPM.isAPMEnabled();
141
+ if (!isAPMEnabled) {
142
+ console.log(LuciqStrings.customSpanAPMDisabledMessage);
143
+ return;
144
+ }
145
+ // Check custom spans enabled
146
+ const isCustomSpanEnabled = await NativeAPM.isCustomSpanEnabled();
147
+ if (!isCustomSpanEnabled) {
148
+ console.log(LuciqStrings.customSpanDisabled);
149
+ return;
150
+ }
151
+ // Truncate name if needed
152
+ let spanName = trimmedName;
153
+ if (spanName.length > 150) {
154
+ spanName = spanName.substring(0, 150);
155
+ console.log(LuciqStrings.customSpanNameTruncated);
156
+ }
157
+ // Convert to microseconds
158
+ const startMicros = startDate.getTime() * 1000;
159
+ const endMicros = endDate.getTime() * 1000;
160
+ // Send to native SDK
161
+ await syncCustomSpan(spanName, startMicros, endMicros);
162
+ }
163
+ catch (error) {
164
+ console.error('[CustomSpan] Error adding completed span:', error);
165
+ }
166
+ };
167
+ /**
168
+ * Test-only helper to clear active spans between tests.
169
+ * @internal
170
+ */
171
+ export const __resetCustomSpansForTests = () => {
172
+ activeSpans.clear();
173
+ };
@@ -23,4 +23,17 @@
23
23
 
24
24
  - (void)setScreenRenderingEnabled:(BOOL)isEnabled;
25
25
 
26
+ // Custom Span methods
27
+ - (void)syncCustomSpan:(NSString *)name
28
+ startTimestamp:(double)startTimestamp
29
+ endTimestamp:(double)endTimestamp
30
+ resolver:(RCTPromiseResolveBlock)resolve
31
+ rejecter:(RCTPromiseRejectBlock)reject;
32
+
33
+ - (void)isCustomSpanEnabled:(RCTPromiseResolveBlock)resolve
34
+ rejecter:(RCTPromiseRejectBlock)reject;
35
+
36
+ - (void)isAPMEnabled:(RCTPromiseResolveBlock)resolve
37
+ rejecter:(RCTPromiseRejectBlock)reject;
38
+
26
39
  @end
@@ -90,6 +90,61 @@ RCT_EXPORT_METHOD(setScreenRenderingEnabled:(BOOL)isEnabled) {
90
90
  LCQAPM.screenRenderingEnabled = isEnabled;
91
91
  }
92
92
 
93
+ // Syncs a custom span to the native SDK (currently logs only)
94
+ RCT_EXPORT_METHOD(syncCustomSpan:(NSString *)name
95
+ startTimestamp:(double)startTimestamp
96
+ endTimestamp:(double)endTimestamp
97
+ resolver:(RCTPromiseResolveBlock)resolve
98
+ rejecter:(RCTPromiseRejectBlock)reject)
99
+ {
100
+ @try {
101
+ // Convert microseconds → seconds (NSDate uses seconds)
102
+ NSTimeInterval startSeconds = startTimestamp / 1e6;
103
+ NSTimeInterval endSeconds = endTimestamp / 1e6;
104
+
105
+ NSDate *startDate = [NSDate dateWithTimeIntervalSince1970:startSeconds];
106
+ NSDate *endDate = [NSDate dateWithTimeIntervalSince1970:endSeconds];
107
+
108
+ // Add completed span to APM
109
+ [LCQAPM addCompletedCustomSpanWithName:name
110
+ startDate:startDate
111
+ endDate:endDate];
112
+
113
+ resolve(@YES);
114
+ }
115
+ @catch (NSException *exception) {
116
+ reject(
117
+ @"SYNC_CUSTOM_SPAN_ERROR",
118
+ exception.reason ?: @"Failed to sync custom span",
119
+ nil
120
+ );
121
+ }
122
+ }
123
+
124
+ // Checks if custom spans feature is enabled
125
+ RCT_EXPORT_METHOD(isCustomSpanEnabled:(RCTPromiseResolveBlock)resolve
126
+ rejecter:(RCTPromiseRejectBlock)reject) {
127
+ @try {
128
+ BOOL enabled = LCQAPM.customSpansEnabled;
129
+ resolve(@(enabled));
130
+ } @catch (NSException *exception) {
131
+ NSLog(@"[CustomSpan] Error checking feature flag: %@", exception);
132
+ resolve(@NO);
133
+ }
134
+ }
135
+
136
+ // Checks if APM is enabled
137
+ RCT_EXPORT_METHOD(isAPMEnabled:(RCTPromiseResolveBlock)resolve
138
+ rejecter:(RCTPromiseRejectBlock)reject) {
139
+ @try {
140
+ BOOL enabled = LCQAPM.enabled;
141
+ resolve(@(enabled));
142
+ } @catch (NSException *exception) {
143
+ NSLog(@"[CustomSpan] Error checking APM enabled: %@", exception);
144
+ resolve(@NO);
145
+ }
146
+ }
147
+
93
148
 
94
149
 
95
150
  @synthesize description;
@@ -545,4 +545,16 @@ RCT_EXPORT_METHOD(setNetworkLogBodyEnabled:(BOOL)isEnabled) {
545
545
  LCQNetworkLogger.logBodyEnabled = isEnabled;
546
546
  }
547
547
 
548
+ // Checks if Luciq SDK is initialized
549
+ RCT_EXPORT_METHOD(isBuilt:(RCTPromiseResolveBlock)resolve
550
+ rejecter:(RCTPromiseRejectBlock)reject) {
551
+ @try {
552
+ BOOL isBuilt = YES;
553
+ resolve(@(isBuilt));
554
+ } @catch (NSException *exception) {
555
+ NSLog(@"[Luciq] Error checking if SDK is built: %@", exception);
556
+ resolve(@NO);
557
+ }
558
+ }
559
+
548
560
  @end
@@ -11,5 +11,6 @@
11
11
 
12
12
  @property (class, atomic, assign) BOOL networkEnabled;
13
13
 
14
+ + (BOOL)customSpansEnabled;
14
15
 
15
16
  @end
package/ios/native.rb CHANGED
@@ -1,4 +1,4 @@
1
- $luciq= { :version => '19.4.1' }
1
+ $luciq= { :version => '19.5.0' }
2
2
 
3
3
  def use_luciq! (spec = nil)
4
4
  version = $luciq[:version]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@luciq/react-native",
3
3
  "description": "Luciq is the Agentic Observability Platform built for Mobile.",
4
- "version": "19.2.2",
4
+ "version": "19.3.0",
5
5
  "author": "Luciq (https://luciq.ai)",
6
6
  "repository": "github:luciqai/luciq-reactnative-sdk",
7
7
  "homepage": "https://www.luciq.ai/platforms/react-native",
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ input=$(cat)
3
+
4
+ input=$(sed -E \
5
+ -e 's/\[([^]]+)\]\(([^)]+)\)/<\2|\1>/g' \
6
+ -e 's/^#{1,6}[[:space:]]*([^[:space:]].*)$/\*\1\*/' \
7
+ -e 's/^- /• /' <<< "$input")
8
+
9
+ echo "$input"
@@ -0,0 +1,60 @@
1
+ if [ -z "$CIRCLE_TOKEN" ]; then
2
+ echo "Error: CIRCLE_TOKEN is not set" >&2
3
+ exit 1
4
+ fi
5
+
6
+ if [ -z "$CIRCLE_WORKFLOW_ID" ]; then
7
+ if [ -z "$CIRCLE_PIPELINE_ID" ]; then
8
+ echo "Error: Neither CIRCLE_WORKFLOW_ID nor CIRCLE_PIPELINE_ID is set" >&2
9
+ exit 1
10
+ fi
11
+ pipelineJson=$(curl -s -X GET "https://circleci.com/api/v2/pipeline/$CIRCLE_PIPELINE_ID/workflow" --header "Circle-Token: $CIRCLE_TOKEN")
12
+ CIRCLE_WORKFLOW_ID=$(echo "$pipelineJson" | jq -r '.items[0].id // empty')
13
+ if [ -z "$CIRCLE_WORKFLOW_ID" ]; then
14
+ echo "Error: Failed to get workflow ID from pipeline" >&2
15
+ exit 1
16
+ fi
17
+ fi
18
+
19
+ response=$(curl -s -w "\n%{http_code}" -X GET "https://circleci.com/api/v2/workflow/$CIRCLE_WORKFLOW_ID/job" --header "Circle-Token: $CIRCLE_TOKEN")
20
+ http_code=$(echo "$response" | tail -n1)
21
+ jobsJson=$(echo "$response" | sed '$d')
22
+
23
+ if [ "$http_code" != "200" ]; then
24
+ echo "Error: CircleCI API returned HTTP $http_code" >&2
25
+ echo "Workflow ID: $CIRCLE_WORKFLOW_ID" >&2
26
+ echo "Response: $jobsJson" >&2
27
+ exit 1
28
+ fi
29
+
30
+ if [ -z "$jobsJson" ]; then
31
+ echo "Error: Failed to fetch jobs from CircleCI API (empty response)" >&2
32
+ exit 1
33
+ fi
34
+
35
+ if ! echo "$jobsJson" | jq -e '.items' > /dev/null 2>&1; then
36
+ echo "Error: Invalid JSON response from CircleCI API" >&2
37
+ echo "Response: $jobsJson" >&2
38
+ exit 1
39
+ fi
40
+
41
+ job=$(jq '.items[] | select(.name == "hold_publish" or .name == "hold_slack_notification") | select(.approved_by != null)' <<< "$jobsJson")
42
+
43
+ if [ -z "$job" ] || [ "$job" == "null" ]; then
44
+ echo "Error: Could not find approved job in workflow" >&2
45
+ exit 1
46
+ fi
47
+
48
+ approver_id=$(jq '.approved_by' <<< "$job")
49
+
50
+ approver_id=$(tr -d '"' <<< "$approver_id")
51
+
52
+ user=$(curl -s -X GET "https://circleci.com/api/v2/user/$approver_id" --header "Circle-Token: $CIRCLE_TOKEN")
53
+
54
+ username=$(jq '.login' <<< "$user")
55
+
56
+ username=$(tr -d '"' <<< "$username")
57
+
58
+ slack_id=$(./scripts/releases/get_slack_id_from_username.sh "$username")
59
+
60
+ echo "$slack_id"
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ latest_release=""
3
+ capturing=false
4
+
5
+ while IFS= read -r line; do
6
+ if [[ "$line" == "## ["* ]]; then
7
+ if $capturing; then
8
+ break
9
+ fi
10
+ fi
11
+
12
+ if [[ "$line" == "### "* ]]; then
13
+ capturing=true
14
+ fi
15
+
16
+ if $capturing; then
17
+ line=$(./scripts/releases/changelog_to_slack_formatter.sh <<< "$line")
18
+ latest_release+="$line\n"
19
+ fi
20
+ done < CHANGELOG.md
21
+
22
+ echo "$latest_release"
@@ -0,0 +1,5 @@
1
+ sdk_version=$(grep -i 'version' package.json) #"version": "xx.x.x+x",
2
+ sdk_version=$(cut -f2 -d' ' <<< $sdk_version | tr -d '" ,') #xx.x.x+x,
3
+ sdk_version=$(cut -f1 -d'+' <<< $sdk_version) #xx.x.x
4
+
5
+ echo "$sdk_version"