@sceneview-sdk/react-native 4.28.0 → 4.30.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.
@@ -64,4 +64,13 @@ dependencies {
64
64
  implementation("androidx.compose.ui:ui")
65
65
  implementation("androidx.compose.foundation:foundation")
66
66
  implementation("androidx.compose.runtime:runtime")
67
+
68
+ // Local JVM unit tests (#3062). Until this existed, the bridge's pure-Kotlin
69
+ // derivations — the tap payload's `nodeName` above all — were exercised by
70
+ // NOTHING: `tools/rn-android-compile` is a compile gate, and the RN demo has
71
+ // never been built end-to-end. A string derivation that silently reports the
72
+ // wrong value is exactly the class a compiler cannot catch, and #3071 was one.
73
+ // These run on the JVM with no device and no emulator, in the same standalone
74
+ // harness that already compiles the module.
75
+ testImplementation("junit:junit:4.13.2")
67
76
  }
@@ -404,13 +404,62 @@ data class ModelNodeData(
404
404
  * `robot.glb?sig=SIG&v=1` — a CDN signature leaking into a payload that
405
405
  * apps routinely put in a label or an analytics event.
406
406
  *
407
+ * [urlPathOf] then drops the authority, which carries the other half of the
408
+ * same exposure: the last `/`-separated segment of a path-less URL IS the
409
+ * authority, and an authority may carry userinfo, so
410
+ * `https://user:pa55w0rd@cdn.example` would report `user:pa55w0rd@cdn`
411
+ * (#3071).
412
+ *
407
413
  * `null` for a path with no usable base name, so the payload stays
408
414
  * `nodeName: null` rather than an empty string.
409
415
  */
410
- fun nodeName(): String? =
411
- src.substringBefore('?').substringBefore('#')
412
- .substringAfterLast('/').substringBeforeLast('.')
413
- .takeIf { it.isNotEmpty() }
416
+ fun nodeName(): String? = modelNodeName(src)
417
+ }
418
+
419
+ /**
420
+ * [ModelNodeData.nodeName]'s derivation, as a top-level function.
421
+ *
422
+ * Split out so it can be unit-tested without constructing a [ModelNodeData]:
423
+ * that data class defaults `position`/`rotation` to `Position`/`Rotation` from
424
+ * the **published** SceneView artifacts, which are compiled for JVM 21, while
425
+ * this module targets JVM 17 and its CI gate runs on a JDK 17. Compiling
426
+ * against a newer class file is fine; *loading* one is not, so instantiating
427
+ * the data class in a JVM unit test throws `UnsupportedClassVersionError`.
428
+ *
429
+ * That mismatch is harmless on a device — D8 re-compiles everything to DEX and
430
+ * the class-file version stops existing — so the fix belongs in the test's
431
+ * reach, not in the module's or the gate's toolchain.
432
+ *
433
+ * This also puts the derivation at the same level as the Flutter bridge's
434
+ * `tapNodeName`, which the shared case table already assumed.
435
+ */
436
+ internal fun modelNodeName(src: String): String? =
437
+ urlPathOf(src.substringBefore('?').substringBefore('#'))
438
+ .substringAfterLast('/').substringBeforeLast('.')
439
+ .takeIf { it.isNotEmpty() }
440
+
441
+ /**
442
+ * The path part of [source] when it is a URL, [source] unchanged otherwise.
443
+ *
444
+ * Returns `""` for a path-less URL: there is no file name in
445
+ * `https://cdn.example` to report, and `null` is the honest payload.
446
+ *
447
+ * Mirrored verbatim in the Flutter bridge's `SceneViewPlugin.kt`. The two
448
+ * bridges are separately published packages with no shared Kotlin, so the
449
+ * duplication is the seam, not an oversight — both sides are pinned by their
450
+ * own unit tests.
451
+ */
452
+ internal fun urlPathOf(source: String): String {
453
+ val schemeEnd = source.indexOf("://")
454
+ if (schemeEnd < 0) return source
455
+ // A "://" that appears after a slash is not a scheme delimiter — the string
456
+ // is already a path (`models/odd://name.glb`) and cutting at it would drop
457
+ // real path segments.
458
+ val firstSlash = source.indexOf('/')
459
+ if (firstSlash in 0 until schemeEnd) return source
460
+ val afterScheme = source.substring(schemeEnd + 3)
461
+ val pathStart = afterScheme.indexOf('/')
462
+ return if (pathStart < 0) "" else afterScheme.substring(pathStart)
414
463
  }
415
464
 
416
465
  data class GeometryNodeData(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sceneview-sdk/react-native",
3
- "version": "4.28.0",
3
+ "version": "4.30.0",
4
4
  "description": "React Native bindings for SceneView — 3D and AR scenes powered by Filament (Android) and RealityKit (iOS)",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -13,7 +13,8 @@
13
13
  "android",
14
14
  "ios",
15
15
  "react-native-sceneview.podspec",
16
- "!**/__tests__"
16
+ "!**/__tests__",
17
+ "!android/src/test"
17
18
  ],
18
19
  "scripts": {
19
20
  "typescript": "tsc --noEmit",
@@ -48,7 +48,7 @@ Pod::Spec.new do |s|
48
48
  #
49
49
  # Loose `~> 4.27` rather than `= s.version`: an app pinned to an older
50
50
  # SceneViewSwift still resolves, mirroring `flutter_sceneview.podspec`.
51
- s.dependency "SceneViewSwift", "~> 4.28"
51
+ s.dependency "SceneViewSwift", "~> 4.30"
52
52
 
53
53
  s.swift_version = "5.9"
54
54
  end