@depup/react-native-worklets 0.11.4-depup.0 → 0.12.2-depup.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 (82) hide show
  1. package/Common/cpp/worklets/AnimationFrameQueue/AnimationFrameBatchinator.cpp +6 -3
  2. package/Common/cpp/worklets/Compat/StableApi.cpp +4 -0
  3. package/Common/cpp/worklets/Compat/StableApi.h +3 -1
  4. package/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp +26 -43
  5. package/Common/cpp/worklets/RunLoop/EventLoop.cpp +16 -7
  6. package/Common/cpp/worklets/RunLoop/EventLoop.h +4 -2
  7. package/Common/cpp/worklets/SharedItems/MemoryManager.cpp +2 -2
  8. package/Common/cpp/worklets/SharedItems/Serializable.cpp +17 -1
  9. package/Common/cpp/worklets/SharedItems/Serializable.h +14 -16
  10. package/Common/cpp/worklets/Tools/UIScheduler.cpp +10 -0
  11. package/Common/cpp/worklets/Tools/UIScheduler.h +3 -0
  12. package/Common/cpp/worklets/Tools/WorkletsJSIUtils.h +23 -0
  13. package/Common/cpp/worklets/WorkletRuntime/RuntimeManager.cpp +7 -4
  14. package/Common/cpp/worklets/WorkletRuntime/RuntimeManager.h +2 -1
  15. package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp +60 -88
  16. package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h +335 -122
  17. package/README.md +7 -7
  18. package/android/build.gradle.kts +20 -6
  19. package/android/src/main/cpp/worklets/android/AndroidUIScheduler.cpp +10 -8
  20. package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.cpp +2 -11
  21. package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.h +1 -4
  22. package/android/src/main/java/com/swmansion/worklets/AndroidUIScheduler.kt +3 -0
  23. package/android/src/main/java/com/swmansion/worklets/ScriptBufferWrapper.kt +87 -43
  24. package/android/src/main/java/com/swmansion/worklets/runloop/AnimationFrameQueue.kt +37 -5
  25. package/android/src/networking/com/swmansion/worklets/WorkletsModule.kt +2 -3
  26. package/android/src/no-networking/com/swmansion/worklets/WorkletsModule.kt +2 -3
  27. package/apple/worklets/apple/IOSUIScheduler.h +3 -0
  28. package/apple/worklets/apple/IOSUIScheduler.mm +6 -1
  29. package/apple/worklets/apple/ScriptLoader.h +11 -0
  30. package/apple/worklets/apple/ScriptLoader.mm +124 -0
  31. package/apple/worklets/apple/WorkletsModule.h +1 -16
  32. package/apple/worklets/apple/WorkletsModule.mm +4 -16
  33. package/changes.json +6 -6
  34. package/compatibility.json +4 -1
  35. package/lib/module/WorkletsModule/NativeWorklets.native.js +2 -2
  36. package/lib/module/WorkletsModule/NativeWorklets.native.js.map +1 -1
  37. package/lib/module/debug/jsVersion.js +1 -1
  38. package/lib/module/memory/serializable.native.js +61 -23
  39. package/lib/module/memory/serializable.native.js.map +1 -1
  40. package/lib/module/memory/synchronizableUnpacker.native.js +16 -8
  41. package/lib/module/memory/synchronizableUnpacker.native.js.map +1 -1
  42. package/lib/module/mock.js +58 -17
  43. package/lib/module/mock.js.map +1 -1
  44. package/lib/module/runLoop/uiRuntime/requestAnimationFrame.js +1 -2
  45. package/lib/module/runLoop/uiRuntime/requestAnimationFrame.js.map +1 -1
  46. package/lib/module/runLoop/workletRuntime/taskQueue.js +0 -1
  47. package/lib/module/runLoop/workletRuntime/taskQueue.js.map +1 -1
  48. package/lib/module/runtimes.native.js +7 -8
  49. package/lib/module/runtimes.native.js.map +1 -1
  50. package/lib/module/threads.js +35 -24
  51. package/lib/module/threads.js.map +1 -1
  52. package/lib/typescript/WorkletsModule/NativeWorklets.native.d.ts.map +1 -1
  53. package/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts +1 -1
  54. package/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts.map +1 -1
  55. package/lib/typescript/debug/jsVersion.d.ts +1 -1
  56. package/lib/typescript/memory/serializable.native.d.ts.map +1 -1
  57. package/lib/typescript/memory/synchronizableUnpacker.native.d.ts.map +1 -1
  58. package/lib/typescript/runLoop/uiRuntime/requestAnimationFrame.d.ts.map +1 -1
  59. package/lib/typescript/runLoop/workletRuntime/taskQueue.d.ts.map +1 -1
  60. package/lib/typescript/runtimes.native.d.ts.map +1 -1
  61. package/lib/typescript/specs/NativeWorkletsModule.d.ts +1 -1
  62. package/lib/typescript/threads.d.ts.map +1 -1
  63. package/lib/typescript/types.d.ts +38 -8
  64. package/lib/typescript/types.d.ts.map +1 -1
  65. package/package.json +24 -25
  66. package/plugin/index.js +1625 -1572
  67. package/src/WorkletsModule/NativeWorklets.native.ts +4 -2
  68. package/src/WorkletsModule/workletsModuleProxy.ts +2 -1
  69. package/src/debug/jsVersion.ts +1 -1
  70. package/src/memory/serializable.native.ts +108 -38
  71. package/src/memory/synchronizableUnpacker.native.ts +16 -10
  72. package/src/mock.ts +85 -23
  73. package/src/runLoop/uiRuntime/requestAnimationFrame.ts +1 -2
  74. package/src/runLoop/workletRuntime/taskQueue.ts +0 -1
  75. package/src/runtimes.native.ts +12 -8
  76. package/src/threads.ts +38 -26
  77. package/src/types.ts +42 -8
  78. package/Common/cpp/worklets/Registries/EventHandlerRegistry.cpp +0 -95
  79. package/Common/cpp/worklets/Registries/EventHandlerRegistry.h +0 -44
  80. package/Common/cpp/worklets/Tools/WorkletEventHandler.cpp +0 -34
  81. package/Common/cpp/worklets/Tools/WorkletEventHandler.h +0 -44
  82. package/jest/pluginTestUtils.ts +0 -16
@@ -25,21 +25,13 @@ jni::local_ref<JScriptBufferWrapper::jhybriddata> JScriptBufferWrapper::initHybr
25
25
 
26
26
  jni::local_ref<JScriptBufferWrapper::jhybriddata> JScriptBufferWrapper::initHybridFromFile(
27
27
  jni::alias_ref<jhybridobject> jThis, // NOLINT //(performance-unnecessary-value-param)
28
- const std::string &fileName) {
28
+ const std::string &fileName,
29
+ const std::string &sourceURL) {
29
30
  std::shared_ptr<const ScriptBuffer> script;
30
31
  RecoverableError::runRethrowingAsRecoverable<std::system_error>([&fileName, &script]() {
31
32
  auto bigString = JSBigFileString::fromPath(fileName);
32
33
  script = std::make_shared<ScriptBuffer>(std::move(bigString));
33
34
  });
34
- return makeCxxInstance(std::move(script), fileName);
35
- }
36
-
37
- jni::local_ref<JScriptBufferWrapper::jhybriddata> JScriptBufferWrapper::initHybridFromString(
38
- jni::alias_ref<jhybridobject> jThis, // NOLINT //(performance-unnecessary-value-param)
39
- const std::string &scriptStr,
40
- const std::string &sourceURL) {
41
- auto bigString = std::make_shared<JSBigStdString>(scriptStr);
42
- auto script = std::make_shared<ScriptBuffer>(std::move(bigString));
43
35
  return makeCxxInstance(std::move(script), sourceURL);
44
36
  }
45
37
 
@@ -60,7 +52,6 @@ void JScriptBufferWrapper::registerNatives() {
60
52
  registerHybrid({
61
53
  makeNativeMethod("initHybridFromAssets", JScriptBufferWrapper::initHybridFromAssets),
62
54
  makeNativeMethod("initHybridFromFile", JScriptBufferWrapper::initHybridFromFile),
63
- makeNativeMethod("initHybridFromString", JScriptBufferWrapper::initHybridFromString),
64
55
  });
65
56
  }
66
57
  } // namespace worklets
@@ -31,11 +31,8 @@ class JScriptBufferWrapper : public jni::HybridClass<JScriptBufferWrapper> {
31
31
  jni::alias_ref<jhybridobject> jThis,
32
32
  jni::alias_ref<JAssetManager::javaobject> assetManager,
33
33
  const std::string &assetURL);
34
- static jni::local_ref<JScriptBufferWrapper::jhybriddata> initHybridFromFile(
35
- jni::alias_ref<jhybridobject> jThis,
36
- const std::string &fileName);
37
34
  static jni::local_ref<JScriptBufferWrapper::jhybriddata>
38
- initHybridFromString(jni::alias_ref<jhybridobject> jThis, const std::string &script, const std::string &sourceURL);
35
+ initHybridFromFile(jni::alias_ref<jhybridobject> jThis, const std::string &fileName, const std::string &sourceURL);
39
36
 
40
37
  friend HybridBase;
41
38
 
@@ -31,6 +31,9 @@ class AndroidUIScheduler(
31
31
 
32
32
  private external fun initHybrid(): HybridData
33
33
 
34
+ @DoNotStrip
35
+ private fun isOnUIThread(): Boolean = UiThreadUtil.isOnUiThread()
36
+
34
37
  external fun triggerUI()
35
38
 
36
39
  external fun invalidate()
@@ -1,13 +1,16 @@
1
1
  package com.swmansion.worklets
2
2
 
3
3
  import android.annotation.SuppressLint
4
+ import android.content.Context
4
5
  import android.content.res.AssetManager
5
- import android.os.Build
6
6
  import com.facebook.jni.HybridData
7
7
  import com.facebook.proguard.annotations.DoNotStrip
8
8
  import com.facebook.proguard.annotations.DoNotStripAny
9
+ import com.facebook.react.ReactApplication
10
+ import org.json.JSONException
11
+ import org.json.JSONObject
12
+ import java.io.File
9
13
  import java.io.IOException
10
- import java.io.InputStream
11
14
  import java.net.HttpURLConnection
12
15
  import java.net.URL
13
16
  import java.nio.charset.StandardCharsets
@@ -17,13 +20,18 @@ import java.nio.charset.StandardCharsets
17
20
  @SuppressLint("MissingNativeLoadLibrary")
18
21
  @DoNotStripAny
19
22
  class ScriptBufferWrapper(
20
- uri: String,
21
- assetManager: AssetManager,
23
+ uri: String?,
24
+ context: Context,
22
25
  ) {
23
26
  @field:DoNotStrip
24
27
  private val mHybridData: HybridData
25
28
 
26
29
  init {
30
+ checkNotNull(uri) {
31
+ "[Worklets] No script URL provided. Make sure the packager is running or you have " +
32
+ "embedded a JS bundle in your application bundle."
33
+ }
34
+
27
35
  val filePrefix = "file://"
28
36
  val absPathPrefix = "/"
29
37
  val assetsPrefix = "assets://"
@@ -32,23 +40,20 @@ class ScriptBufferWrapper(
32
40
  when {
33
41
  uri.startsWith(filePrefix) -> {
34
42
  val fileName = uri.substring(filePrefix.length)
35
- initHybridFromFile(fileName)
43
+ initHybridFromFile(fileName, fileName)
36
44
  }
37
45
  uri.startsWith(assetsPrefix) -> {
38
46
  val assetURL = uri.substring(assetsPrefix.length)
39
- initHybridFromAssets(assetManager, assetURL)
47
+ initHybridFromAssets(context.assets, assetURL)
40
48
  }
41
49
  uri.startsWith(absPathPrefix) -> {
42
- initHybridFromFile(uri)
50
+ initHybridFromFile(uri, uri)
43
51
  }
44
52
  else -> {
45
- val scriptContent =
46
- try {
47
- downloadScript(uri)
48
- } catch (e: IOException) {
49
- throw RuntimeException(e)
50
- }
51
- initHybridFromString(scriptContent, uri)
53
+ val bundleFile =
54
+ reactNativeDownloadedBundleFile(context)
55
+ ?: File(context.cacheDir, DEV_BUNDLE_FILE_NAME).also { downloadScriptToFile(uri, it) }
56
+ initHybridFromFile(bundleFile.absolutePath, uri)
52
57
  }
53
58
  }
54
59
  }
@@ -58,45 +63,84 @@ class ScriptBufferWrapper(
58
63
  assetURL: String,
59
64
  ): HybridData
60
65
 
61
- private external fun initHybridFromFile(fileName: String): HybridData
62
-
63
- private external fun initHybridFromString(
64
- script: String,
65
- url: String,
66
+ private external fun initHybridFromFile(
67
+ fileName: String,
68
+ sourceURL: String,
66
69
  ): HybridData
67
70
 
68
71
  companion object {
69
- private fun downloadScript(url: String): String {
70
- val scriptUrl = URL(url)
71
- val connection = scriptUrl.openConnection() as HttpURLConnection
72
- try {
73
- val content: ByteArray
72
+ private const val DEV_BUNDLE_FILE_NAME = "WorkletsDevBundle.js"
73
+ private const val CONNECT_TIMEOUT_MS = 5_000
74
74
 
75
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
76
- content = connection.inputStream.readAllBytes()
77
- } else {
78
- content = readBytes(connection.inputStream)
79
- }
75
+ private fun reactNativeDownloadedBundleFile(context: Context): File? {
76
+ val reactApplication = context.applicationContext as? ReactApplication ?: return null
77
+ val bundlePath =
78
+ reactApplication.reactHost
79
+ ?.devSupportManager
80
+ ?.downloadedJSBundleFile ?: return null
81
+ return File(bundlePath).takeIf { it.exists() }
82
+ }
80
83
 
81
- return String(content, StandardCharsets.UTF_8)
84
+ private fun downloadScriptToFile(
85
+ url: String,
86
+ outputFile: File,
87
+ ) {
88
+ val connection = URL(url).openConnection() as HttpURLConnection
89
+ connection.connectTimeout = CONNECT_TIMEOUT_MS
90
+ try {
91
+ val statusCode =
92
+ try {
93
+ connection.responseCode
94
+ } catch (e: IOException) {
95
+ throw RuntimeException("[Worklets] Could not connect to development server.\n", e)
96
+ }
97
+ if (statusCode != 200) {
98
+ throw RuntimeException(serverErrorMessage(url, statusCode, connection))
99
+ }
100
+ try {
101
+ val tmpFile = File(outputFile.path + ".tmp")
102
+ connection.inputStream.use { input ->
103
+ tmpFile.outputStream().use { output -> input.copyTo(output) }
104
+ }
105
+ if (!tmpFile.renameTo(outputFile)) {
106
+ throw IOException("Couldn't rename $tmpFile to $outputFile")
107
+ }
108
+ } catch (e: IOException) {
109
+ throw RuntimeException(
110
+ "[Worklets] Failed to store worklets bundle from URL $url: ${e.message}",
111
+ e,
112
+ )
113
+ }
82
114
  } finally {
83
115
  connection.disconnect()
84
116
  }
85
117
  }
86
118
 
87
- /** Reads all bytes from an InputStream into a byte array for SDKs below 33. */
88
- private fun readBytes(inputStream: InputStream): ByteArray {
89
- val bufferSize = 4096
90
- try {
91
- val byteBuffer = java.io.ByteArrayOutputStream()
92
- val buffer = ByteArray(bufferSize)
93
- var len: Int
94
- while (inputStream.read(buffer).also { len = it } != -1) {
95
- byteBuffer.write(buffer, 0, len)
96
- }
97
- return byteBuffer.toByteArray()
98
- } finally {
99
- inputStream.close()
119
+ private fun serverErrorMessage(
120
+ url: String,
121
+ statusCode: Int,
122
+ connection: HttpURLConnection,
123
+ ): String {
124
+ val body = connection.errorStream?.use { String(it.readBytes(), StandardCharsets.UTF_8) }
125
+ val metroMessage = parseMetroError(body)
126
+ if (metroMessage != null) {
127
+ return "[Worklets] $metroMessage"
128
+ }
129
+ return "[Worklets] The development server returned response error code: $statusCode\n\n" +
130
+ "URL: $url\n\n" +
131
+ "Body:\n$body"
132
+ }
133
+
134
+ private fun parseMetroError(body: String?): String? {
135
+ if (body.isNullOrEmpty()) {
136
+ return null
137
+ }
138
+ return try {
139
+ val json = JSONObject(body)
140
+ val fileName = json.getString("filename").substringAfterLast('/')
141
+ "${json.getString("message")}\n at $fileName:${json.getInt("lineNumber")}:${json.getInt("column")}"
142
+ } catch (e: JSONException) {
143
+ null
100
144
  }
101
145
  }
102
146
  }
@@ -25,7 +25,9 @@ class AnimationFrameQueue(
25
25
  }
26
26
  private val mCallbackPosted = AtomicBoolean()
27
27
  private val mPaused = AtomicBoolean()
28
+ private val mInvalidated = AtomicBoolean()
28
29
  private val mFrameCallbacks = mutableListOf<AnimationFrameCallback>()
30
+ private val mDispatchLock = Any()
29
31
 
30
32
  fun resume() {
31
33
  if (mPaused.getAndSet(false)) {
@@ -44,8 +46,21 @@ class AnimationFrameQueue(
44
46
  }
45
47
  }
46
48
 
49
+ fun invalidate() {
50
+ mInvalidated.set(true)
51
+ removePostedFrameCallback()
52
+ synchronized(mDispatchLock) {
53
+ synchronized(mFrameCallbacks) {
54
+ mFrameCallbacks.clear()
55
+ }
56
+ }
57
+ }
58
+
47
59
  fun requestAnimationFrame(animationFrameCallback: AnimationFrameCallback) {
48
60
  synchronized(mFrameCallbacks) {
61
+ if (mInvalidated.get()) {
62
+ return
63
+ }
49
64
  mFrameCallbacks.add(animationFrameCallback)
50
65
  }
51
66
  scheduleQueueExecution()
@@ -64,6 +79,9 @@ class AnimationFrameQueue(
64
79
 
65
80
  private fun scheduleQueueExecution() {
66
81
  synchronized(mPaused) {
82
+ if (mInvalidated.get()) {
83
+ return
84
+ }
67
85
  if (!mPaused.get() && !mCallbackPosted.getAndSet(true)) {
68
86
  mReactChoreographer.postFrameCallback(
69
87
  ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE,
@@ -73,6 +91,15 @@ class AnimationFrameQueue(
73
91
  }
74
92
  }
75
93
 
94
+ private fun removePostedFrameCallback() {
95
+ if (mCallbackPosted.getAndSet(false)) {
96
+ mReactChoreographer.removeFrameCallback(
97
+ ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE,
98
+ mChoreographerCallback,
99
+ )
100
+ }
101
+ }
102
+
76
103
  private fun executeQueue(frameTimeNanos: Long) {
77
104
  val currentFrameTimeMs = calculateTimestamp(frameTimeNanos)
78
105
  if (currentFrameTimeMs <= lastFrameTimeMs) {
@@ -83,12 +110,17 @@ class AnimationFrameQueue(
83
110
  return
84
111
  }
85
112
 
86
- val frameCallbacks = pullCallbacks()
87
- mCallbackPosted.set(false)
113
+ synchronized(mDispatchLock) {
114
+ val frameCallbacks = pullCallbacks()
115
+ mCallbackPosted.set(false)
116
+ if (mInvalidated.get()) {
117
+ return
118
+ }
88
119
 
89
- lastFrameTimeMs = currentFrameTimeMs
90
- for (callback in frameCallbacks) {
91
- callback.onAnimationFrame(currentFrameTimeMs)
120
+ lastFrameTimeMs = currentFrameTimeMs
121
+ for (callback in frameCallbacks) {
122
+ callback.onAnimationFrame(currentFrameTimeMs)
123
+ }
92
124
  }
93
125
  }
94
126
 
@@ -71,11 +71,9 @@ class WorkletsModule(
71
71
  val jsContext = checkNotNull(context.javaScriptContextHolder).get()
72
72
  val jsCallInvokerHolder = context.jsCallInvokerHolder as CallInvokerHolderImpl
73
73
 
74
- val sourceURL = context.sourceURL
75
-
76
74
  val scriptBufferWrapper: ScriptBufferWrapper? =
77
75
  if (bundleModeEnabled) {
78
- ScriptBufferWrapper(sourceURL!!, context.assets)
76
+ ScriptBufferWrapper(context.sourceURL, context)
79
77
  } else {
80
78
  null
81
79
  }
@@ -173,6 +171,7 @@ class WorkletsModule(
173
171
  mInvalidated = true
174
172
  reactApplicationContext.removeLifecycleEventListener(this)
175
173
  }
174
+ mAnimationFrameQueue.invalidate()
176
175
  if (mHybridData != null && mHybridData!!.isValid) {
177
176
  invalidateCpp()
178
177
  }
@@ -67,11 +67,9 @@ class WorkletsModule(
67
67
  val jsContext = checkNotNull(context.javaScriptContextHolder).get()
68
68
  val jsCallInvokerHolder = context.jsCallInvokerHolder as CallInvokerHolderImpl
69
69
 
70
- val sourceURL = context.sourceURL
71
-
72
70
  val scriptBufferWrapper: ScriptBufferWrapper? =
73
71
  if (bundleModeEnabled) {
74
- ScriptBufferWrapper(sourceURL!!, context.assets)
72
+ ScriptBufferWrapper(context.sourceURL, context)
75
73
  } else {
76
74
  null
77
75
  }
@@ -129,6 +127,7 @@ class WorkletsModule(
129
127
  mInvalidated = true
130
128
  reactApplicationContext.removeLifecycleEventListener(this)
131
129
  }
130
+ mAnimationFrameQueue.invalidate()
132
131
  if (mHybridData != null && mHybridData!!.isValid) {
133
132
  invalidateCpp()
134
133
  }
@@ -9,6 +9,9 @@ using namespace worklets;
9
9
  class IOSUIScheduler : public UIScheduler, public std::enable_shared_from_this<IOSUIScheduler> {
10
10
  public:
11
11
  void scheduleOnUI(std::function<void()> job) override;
12
+
13
+ protected:
14
+ bool queryIsOnUIThread() const override;
12
15
  };
13
16
 
14
17
  } // namespace worklets
@@ -5,9 +5,14 @@ namespace worklets {
5
5
  using namespace facebook;
6
6
  using namespace react;
7
7
 
8
+ bool IOSUIScheduler::queryIsOnUIThread() const
9
+ {
10
+ return [NSThread isMainThread];
11
+ }
12
+
8
13
  void IOSUIScheduler::scheduleOnUI(std::function<void()> job)
9
14
  {
10
- if ([NSThread isMainThread]) {
15
+ if (isOnUIThread()) {
11
16
  job();
12
17
  return;
13
18
  }
@@ -0,0 +1,11 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ #import <worklets/Tools/ScriptBuffer.h>
4
+
5
+ #import <memory>
6
+
7
+ namespace worklets {
8
+
9
+ std::shared_ptr<const ScriptBuffer> getScript(NSURL *url);
10
+
11
+ } // namespace worklets
@@ -0,0 +1,124 @@
1
+ #import <worklets/apple/ScriptLoader.h>
2
+
3
+ namespace worklets {
4
+ namespace {
5
+
6
+ class NSDataScript : public JSBigString {
7
+ public:
8
+ explicit NSDataScript(NSData *data) : data_(data), size_([data length]) {}
9
+
10
+ bool isAscii() const override
11
+ {
12
+ return false;
13
+ }
14
+
15
+ const char *c_str() const override
16
+ {
17
+ return static_cast<const char *>(data_.bytes);
18
+ }
19
+
20
+ size_t size() const override
21
+ {
22
+ return size_;
23
+ }
24
+
25
+ private:
26
+ NSData *data_;
27
+ size_t size_;
28
+ };
29
+
30
+ NSData *loadScriptFromFile(NSURL *url)
31
+ {
32
+ NSError *error;
33
+ NSData *data = [NSData dataWithContentsOfFile:url.path options:NSDataReadingMappedIfSafe error:&error];
34
+
35
+ if (data == nil) [[unlikely]] {
36
+ NSString *errorMsg = [NSString stringWithFormat:@"[Worklets] Failed to load worklets bundle from file %@: %@",
37
+ url.path,
38
+ error.localizedDescription];
39
+ NSLog(@"%@", errorMsg);
40
+ throw std::runtime_error([errorMsg UTF8String]);
41
+ }
42
+
43
+ return data;
44
+ }
45
+
46
+ NSData *downloadScript(NSURL *url)
47
+ {
48
+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
49
+ __block NSData *data = nil;
50
+ __block NSURLResponse *response = nil;
51
+ __block NSError *error = nil;
52
+
53
+ NSURLSessionDataTask *task = [[NSURLSession sharedSession]
54
+ dataTaskWithURL:url
55
+ completionHandler:^(NSData *taskData, NSURLResponse *taskResponse, NSError *taskError) {
56
+ data = taskData;
57
+ response = taskResponse;
58
+ error = taskError;
59
+ dispatch_semaphore_signal(semaphore);
60
+ }];
61
+ [task resume];
62
+ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
63
+
64
+ if (error != nil) [[unlikely]] {
65
+ NSString *errorMsg;
66
+ if ([error.domain isEqualToString:NSURLErrorDomain]) {
67
+ errorMsg = @"[Worklets] Could not connect to development server.\n";
68
+ } else {
69
+ errorMsg = [NSString stringWithFormat:@"[Worklets] Failed to load worklets bundle from URL %@: %@",
70
+ url,
71
+ error.localizedDescription];
72
+ }
73
+ NSLog(@"%@", errorMsg);
74
+ throw std::runtime_error([errorMsg UTF8String]);
75
+ }
76
+
77
+ NSInteger statusCode =
78
+ [response isKindOfClass:[NSHTTPURLResponse class]] ? [(NSHTTPURLResponse *)response statusCode] : 200;
79
+ if (statusCode != 200) [[unlikely]] {
80
+ NSString *description = nil;
81
+ NSDictionary *jsonError = data != nil ? [NSJSONSerialization JSONObjectWithData:data options:0 error:nil] : nil;
82
+ if ([jsonError isKindOfClass:[NSDictionary class]] && [jsonError[@"message"] isKindOfClass:[NSString class]] &&
83
+ [jsonError[@"message"] length] > 0) {
84
+ description = jsonError[@"message"];
85
+ } else {
86
+ description = [NSString stringWithFormat:@"Received status code %ld while fetching the bundle", (long)statusCode];
87
+ }
88
+ NSString *errorMsg =
89
+ [NSString stringWithFormat:@"[Worklets] Failed to load worklets bundle from URL %@: %@", url, description];
90
+ NSLog(@"%@", errorMsg);
91
+ throw std::runtime_error([errorMsg UTF8String]);
92
+ }
93
+
94
+ NSString *mimeType = response.MIMEType;
95
+ if (![mimeType isEqualToString:@"application/javascript"] && ![mimeType isEqualToString:@"text/javascript"])
96
+ [[unlikely]] {
97
+ NSString *errorMsg = [NSString
98
+ stringWithFormat:
99
+ @"[Worklets] Expected MIME-Type of the worklets bundle to be 'application/javascript' or 'text/javascript', but got '%@'",
100
+ mimeType];
101
+ NSLog(@"%@", errorMsg);
102
+ throw std::runtime_error([errorMsg UTF8String]);
103
+ }
104
+
105
+ return data;
106
+ }
107
+
108
+ } // namespace
109
+
110
+ std::shared_ptr<const ScriptBuffer> getScript(NSURL *url)
111
+ {
112
+ if (url == nil) [[unlikely]] {
113
+ NSString *errorMsg =
114
+ @"[Worklets] No script URL provided. Make sure the packager is running or you have embedded a "
115
+ @"JS bundle in your application bundle.";
116
+ NSLog(@"%@", errorMsg);
117
+ throw std::runtime_error([errorMsg UTF8String]);
118
+ }
119
+
120
+ NSData *data = url.isFileURL ? loadScriptFromFile(url) : downloadScript(url);
121
+ return std::make_shared<const ScriptBuffer>(std::make_shared<const NSDataScript>(data));
122
+ }
123
+
124
+ } // namespace worklets
@@ -6,22 +6,7 @@
6
6
 
7
7
  #import <worklets/NativeModules/WorkletsModuleProxy.h>
8
8
 
9
- #if __has_include(<React/RCTBundleConsumer.h>)
10
- // Bundle mode
11
- #import <React/NSBigStringBuffer.h>
12
- #import <React/RCTBundleConsumer.h>
13
- #endif // __has_include(<React/RCTBundleConsumer.h>)
14
-
15
- @interface WorkletsModule : RCTEventEmitter <
16
- NativeWorkletsModuleSpec,
17
- RCTCallInvokerModule,
18
- RCTInvalidating
19
- #if __has_include(<React/RCTBundleConsumer.h>)
20
- // Bundle mode
21
- ,
22
- RCTBundleConsumer
23
- #endif // __has_include(<React/RCTBundleConsumer.h>)
24
- >
9
+ @interface WorkletsModule : RCTEventEmitter <NativeWorkletsModuleSpec, RCTCallInvokerModule, RCTInvalidating>
25
10
 
26
11
  - (std::shared_ptr<worklets::WorkletsModuleProxy>)getWorkletsModuleProxy;
27
12
 
@@ -7,8 +7,11 @@
7
7
  #import <worklets/apple/AssertJavaScriptQueue.h>
8
8
  #import <worklets/apple/AssertTurboModuleManagerQueue.h>
9
9
  #import <worklets/apple/IOSUIScheduler.h>
10
+ #import <worklets/apple/ScriptLoader.h>
10
11
  #import <worklets/apple/WorkletsModule.h>
11
12
 
13
+ #import <Foundation/Foundation.h>
14
+
12
15
  #import <React/RCTBridge+Private.h>
13
16
  #import <React/RCTCallInvoker.h>
14
17
 
@@ -64,7 +67,7 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(installTurboModule : (BOOL)bundleModeEnab
64
67
 
65
68
  if (bundleModeEnabled) {
66
69
  NSURL *url = bundleManager_.bundleURL;
67
- script = [self getScript:url];
70
+ script = getScript(url);
68
71
  sourceURL = [[url absoluteString] UTF8String];
69
72
  }
70
73
 
@@ -127,21 +130,6 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(toggleSlowAnimationsOnUIRuntime)
127
130
  return std::make_shared<facebook::react::NativeWorkletsModuleSpecJSI>(params);
128
131
  }
129
132
 
130
- - (std::shared_ptr<const ScriptBuffer>)getScript:(NSURL *)url
131
- {
132
- NSData *data = [NSData dataWithContentsOfURL:url];
133
-
134
- if (!data) [[unlikely]] {
135
- NSString *errorMsg = [NSString stringWithFormat:@"[Worklets] Failed to load worklets bundle from URL: %@", url];
136
- NSLog(@"%@", errorMsg);
137
- throw std::runtime_error([errorMsg UTF8String]);
138
- }
139
-
140
- auto str = std::string(reinterpret_cast<const char *>([data bytes]), [data length]);
141
- auto bigString = std::make_shared<const JSBigStdString>(str);
142
- return std::make_shared<const ScriptBuffer>(bigString);
143
- }
144
-
145
133
  - (std::shared_ptr<RuntimeBindings>)getRuntimeBindings:(jsi::Runtime &)rnRuntime
146
134
  bundleModeEnabled:(BOOL)bundleModeEnabled
147
135
  {
package/changes.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "bumped": {
3
3
  "@babel/generator": {
4
4
  "from": "^7.27.1",
5
- "to": "^8.0.0"
5
+ "to": "^8.0.5"
6
6
  },
7
7
  "@babel/plugin-transform-arrow-functions": {
8
8
  "from": "^7.27.1",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "@babel/plugin-transform-classes": {
16
16
  "from": "^7.28.6",
17
- "to": "^8.0.1"
17
+ "to": "^8.0.5"
18
18
  },
19
19
  "@babel/plugin-transform-nullish-coalescing-operator": {
20
20
  "from": "^7.28.6",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "@babel/plugin-transform-optional-chaining": {
24
24
  "from": "^7.28.6",
25
- "to": "^8.0.1"
25
+ "to": "^8.0.5"
26
26
  },
27
27
  "@babel/plugin-transform-shorthand-properties": {
28
28
  "from": "^7.27.1",
@@ -42,17 +42,17 @@
42
42
  },
43
43
  "@babel/traverse": {
44
44
  "from": "^7.27.1",
45
- "to": "^8.0.4"
45
+ "to": "^8.0.5"
46
46
  },
47
47
  "@babel/types": {
48
48
  "from": "^7.27.1",
49
- "to": "^8.0.4"
49
+ "to": "^8.0.5"
50
50
  },
51
51
  "semver": {
52
52
  "from": "^7.7.4",
53
53
  "to": "^7.8.5"
54
54
  }
55
55
  },
56
- "timestamp": "2026-08-16T00:26:58.915Z",
56
+ "timestamp": "2026-09-13T00:59:44.373Z",
57
57
  "totalUpdated": 13
58
58
  }
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "nightly": {
3
- "react-native": ["0.83", "0.84", "0.85", "0.86"]
3
+ "react-native": ["0.83", "0.84", "0.85", "0.86", "0.87"]
4
+ },
5
+ "0.12.x": {
6
+ "react-native": ["0.83", "0.84", "0.85", "0.86", "0.87"]
4
7
  },
5
8
  "0.11.x": {
6
9
  "react-native": ["0.83", "0.84", "0.85", "0.86"]
@@ -124,8 +124,8 @@ See https://docs.swmansion.com/react-native-worklets/docs/guides/troubleshooting
124
124
  runOnUISync(worklet, scheduleStack) {
125
125
  return this.#workletsModuleProxy.runOnUISync(worklet, scheduleStack);
126
126
  }
127
- createWorkletRuntime(name, initializer, useDefaultQueue, customQueue, enableEventLoop) {
128
- return this.#workletsModuleProxy.createWorkletRuntime(name, initializer, useDefaultQueue, customQueue, enableEventLoop);
127
+ createWorkletRuntime(name, initializer, useDefaultQueue, customQueue, enableEventLoop, enableLocking) {
128
+ return this.#workletsModuleProxy.createWorkletRuntime(name, initializer, useDefaultQueue, customQueue, enableEventLoop, enableLocking);
129
129
  }
130
130
  scheduleOnRuntime(workletRuntime, serializableWorklet, scheduleStack) {
131
131
  return this.#workletsModuleProxy.scheduleOnRuntime(workletRuntime, serializableWorklet, scheduleStack);