@luciq/react-native 19.4.0 → 19.6.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 (58) hide show
  1. package/.claude/agents/codebase-analyzer.md +33 -0
  2. package/.claude/agents/codebase-locator.md +42 -0
  3. package/.claude/agents/codebase-pattern-finder.md +40 -0
  4. package/.claude/commands/apply-pr-reviews.md +253 -0
  5. package/.claude/commands/create-jira-workitem.md +27 -0
  6. package/.claude/commands/create-pr.md +138 -0
  7. package/.claude/commands/create-public-release-notes.md +145 -0
  8. package/.claude/commands/create-rca.md +286 -0
  9. package/.claude/commands/debug-sdk.md +66 -0
  10. package/.claude/commands/describe-pr.md +40 -0
  11. package/.claude/commands/new-api.md +60 -0
  12. package/.claude/commands/new-feature.md +75 -0
  13. package/.claude/commands/pr-review.md +85 -0
  14. package/.claude/commands/research-codebase.md +41 -0
  15. package/.claude/commands/review.md +73 -0
  16. package/.claude/memory/MEMORY.md +1 -0
  17. package/.claude/memory/feedback_pr_title_format.md +10 -0
  18. package/.claude/rules/react-native-typescript.md +46 -0
  19. package/CHANGELOG.md +12 -0
  20. package/CLAUDE.md +125 -0
  21. package/android/native.gradle +1 -1
  22. package/android/src/main/java/ai/luciq/reactlibrary/LuciqScreenLoadingFrameTracker.java +88 -0
  23. package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqAPMModule.java +184 -10
  24. package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqReactnativeModule.java +5 -3
  25. package/dist/components/LuciqCaptureScreenLoading.d.ts +8 -0
  26. package/dist/components/LuciqCaptureScreenLoading.js +154 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +2 -0
  29. package/dist/modules/APM.d.ts +19 -0
  30. package/dist/modules/APM.js +38 -0
  31. package/dist/modules/Luciq.d.ts +1 -1
  32. package/dist/modules/Luciq.js +169 -11
  33. package/dist/modules/apm/ScreenLoadingManager.d.ts +99 -0
  34. package/dist/modules/apm/ScreenLoadingManager.js +296 -0
  35. package/dist/native/NativeAPM.d.ts +9 -0
  36. package/dist/native/NativeLuciq.d.ts +1 -1
  37. package/dist/utils/LuciqUtils.d.ts +25 -0
  38. package/dist/utils/LuciqUtils.js +44 -0
  39. package/dist/utils/RouteMatcher.d.ts +30 -0
  40. package/dist/utils/RouteMatcher.js +67 -0
  41. package/ios/RNLuciq/LuciqAPMBridge.m +82 -0
  42. package/ios/RNLuciq/LuciqReactBridge.m +1 -1
  43. package/ios/RNLuciq/LuciqScreenLoadingFrameTracker.h +11 -0
  44. package/ios/RNLuciq/LuciqScreenLoadingFrameTracker.m +121 -0
  45. package/ios/RNLuciq/Util/LCQAPM+PrivateAPIs.h +14 -0
  46. package/ios/native.rb +1 -1
  47. package/package.json +4 -1
  48. package/scripts/get-github-app-token.sh +70 -0
  49. package/scripts/notify-github.sh +17 -8
  50. package/src/components/LuciqCaptureScreenLoading.tsx +210 -0
  51. package/src/index.ts +4 -0
  52. package/src/modules/APM.ts +42 -0
  53. package/src/modules/Luciq.ts +197 -11
  54. package/src/modules/apm/ScreenLoadingManager.ts +364 -0
  55. package/src/native/NativeAPM.ts +22 -0
  56. package/src/native/NativeLuciq.ts +1 -1
  57. package/src/utils/LuciqUtils.ts +49 -0
  58. package/src/utils/RouteMatcher.ts +83 -0
@@ -420,6 +420,54 @@ export function updateNetworkLogSnapshot(networkSnapshot: NetworkData) {
420
420
  );
421
421
  }
422
422
 
423
+ /**
424
+ * @internal
425
+ * This method is for internal use only.
426
+ *
427
+ * Parses a string value to an integer, returning null if the value is null or cannot be parsed.
428
+ * @param value The string value to parse
429
+ * @returns The parsed integer or null
430
+ */
431
+ export function getIntValue(value: string | null): number | null {
432
+ if (value === null) {
433
+ return null;
434
+ }
435
+ const parsed = parseInt(value, 10);
436
+ return isNaN(parsed) ? null : parsed;
437
+ }
438
+
439
+ // One-time anchor captured at module load to convert performance.now() to epoch time.
440
+ // performance.now() gives high-resolution monotonic time (sub-ms precision) but relative
441
+ // to app start. We pair it with Date.now() once, then derive epoch from the offset.
442
+ const perfAnchorMs: number = performance.now();
443
+ const epochAnchorUs: number = Date.now() * 1000;
444
+
445
+ /**
446
+ * Returns a high-resolution monotonic timestamp in microseconds.
447
+ * Use this for all internal duration measurements.
448
+ */
449
+ export function nowMicros(): number {
450
+ return performance.now() * 1000;
451
+ }
452
+
453
+ /**
454
+ * Converts an internal monotonic microsecond timestamp to epoch microseconds.
455
+ * Use this only when reporting to the native layer or external systems.
456
+ */
457
+ export function toEpochMicros(monotonicUs: number): number {
458
+ const offsetUs = monotonicUs - perfAnchorMs * 1000;
459
+ return epochAnchorUs + offsetUs;
460
+ }
461
+
462
+ /**
463
+ * Converts an epoch microsecond timestamp to internal monotonic microseconds.
464
+ * Use this when receiving timestamps from the native layer that are epoch-based.
465
+ */
466
+ export function fromEpochMicros(epochUs: number): number {
467
+ const offsetUs = epochUs - epochAnchorUs;
468
+ return perfAnchorMs * 1000 + offsetUs;
469
+ }
470
+
423
471
  export default {
424
472
  parseErrorStack,
425
473
  captureJsErrors,
@@ -427,6 +475,7 @@ export default {
427
475
  getFullRoute,
428
476
  getStackTrace,
429
477
  stringifyIfNotString,
478
+ getIntValue,
430
479
  sendCrashReport,
431
480
  reportNetworkLog,
432
481
  generateTracePartialId,
@@ -0,0 +1,83 @@
1
+ // TODO: This class is currently unused but will be used later for route matching.
2
+ /**
3
+ * Matches route path definitions (potentially containing parameters and wildcards)
4
+ * against actual navigation paths.
5
+ *
6
+ * Supports `:param` segments for named parameters and `**` for wildcard matching.
7
+ */
8
+ class RouteMatcher {
9
+ private static _instance: RouteMatcher = new RouteMatcher();
10
+
11
+ static get instance(): RouteMatcher {
12
+ return RouteMatcher._instance;
13
+ }
14
+
15
+ /** @internal visible for testing */
16
+ static setInstance(instance: RouteMatcher): void {
17
+ RouteMatcher._instance = instance;
18
+ }
19
+
20
+ /**
21
+ * Checks whether the given `routePath` definition matches the given `actualPath`.
22
+ *
23
+ * The `routePath` definition can contain parameters in the form of `:param`,
24
+ * or `**` for a wildcard parameter.
25
+ *
26
+ * Returns `true` if the `actualPath` matches the `routePath`, otherwise `false`.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * RouteMatcher.instance.match('/users', '/users'); // true
31
+ * RouteMatcher.instance.match('/user/:id', '/user/123'); // true
32
+ * RouteMatcher.instance.match('/user/**', '/user/123/profile'); // true
33
+ * ```
34
+ */
35
+ match(routePath: string | null, actualPath: string | null): boolean {
36
+ if (routePath == null || actualPath == null) {
37
+ return routePath === actualPath;
38
+ }
39
+
40
+ const routePathSegments = this.segmentPath(routePath);
41
+ const actualPathSegments = this.segmentPath(actualPath);
42
+
43
+ const hasWildcard = routePathSegments.includes('**');
44
+
45
+ if (routePathSegments.length !== actualPathSegments.length && !hasWildcard) {
46
+ return false;
47
+ }
48
+
49
+ for (let i = 0; i < routePathSegments.length; i++) {
50
+ const routeSegment = routePathSegments[i];
51
+
52
+ const isWildcard = routeSegment === '**';
53
+ const isParameter = routeSegment.startsWith(':');
54
+
55
+ const noMoreActualSegments = i >= actualPathSegments.length;
56
+
57
+ if (noMoreActualSegments) {
58
+ return isWildcard;
59
+ }
60
+
61
+ if (isParameter) {
62
+ continue;
63
+ }
64
+
65
+ if (isWildcard) {
66
+ return true;
67
+ }
68
+
69
+ if (routeSegment !== actualPathSegments[i]) {
70
+ return false;
71
+ }
72
+ }
73
+
74
+ return true;
75
+ }
76
+
77
+ private segmentPath(path: string): string[] {
78
+ const pathWithoutQuery = path.split('?')[0];
79
+ return pathWithoutQuery.split('/').filter((segment) => segment.length > 0);
80
+ }
81
+ }
82
+
83
+ export default RouteMatcher;