expo-modules-core 56.0.6 → 56.0.8

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 (25) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +4 -6
  4. package/android/src/main/cpp/JSIContext.h +0 -9
  5. package/android/src/main/cpp/JavaCallback.cpp +78 -47
  6. package/android/src/main/cpp/JavaCallback.h +1 -11
  7. package/android/src/main/cpp/JavaScriptValue.h +0 -1
  8. package/android/src/main/cpp/decorators/JSDecoratorsBridgingObject.cpp +10 -0
  9. package/android/src/main/cpp/decorators/JSDecoratorsBridgingObject.h +9 -9
  10. package/android/src/main/cpp/fabric/NativeStatePropsGetter.cpp +1 -0
  11. package/android/src/main/cpp/fabric/NativeStatePropsGetter.h +0 -1
  12. package/android/src/main/java/expo/modules/kotlin/devtools/cdp/CdpNetworkTypes.kt +3 -2
  13. package/android/src/main/java/expo/modules/kotlin/views/OnAttachAfterDetachmentListener.kt +41 -0
  14. package/build/polyfill/CoreModule.d.ts.map +1 -1
  15. package/build/ts-declarations/SharedRef.d.ts.map +1 -1
  16. package/ios/Core/DynamicTypes/DynamicEncodableType.swift +5 -4
  17. package/ios/Core/JSValueEncoder.swift +255 -96
  18. package/ios/DevTools/CdpNetworkTypes.swift +1 -1
  19. package/package.json +3 -3
  20. package/prebuilds/output/debug/xcframeworks/ExpoModulesCore.tar.gz +0 -0
  21. package/prebuilds/output/debug/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
  22. package/prebuilds/output/release/xcframeworks/ExpoModulesCore.tar.gz +0 -0
  23. package/prebuilds/output/release/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
  24. package/src/polyfill/CoreModule.ts +3 -3
  25. package/src/ts-declarations/SharedRef.ts +3 -3
package/CHANGELOG.md CHANGED
@@ -10,6 +10,19 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 56.0.8 — 2026-05-13
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [Android] Avoid remounting Jetpack Compose views during same-frame React Native reparenting. ([#45711](https://github.com/expo/expo/pull/45711) by [@mvincentong](https://github.com/mvincentong))
18
+ - [Android] Fixed `associatedCookies` in `Network.requestWillBeSentExtraInfo` CDP events serializing as a JSON object instead of an array. ([#45720](https://github.com/expo/expo/pull/45720) by [@huntie](https://github.com/huntie))
19
+
20
+ ## 56.0.7 — 2026-05-13
21
+
22
+ ### 🐛 Bug fixes
23
+
24
+ - [iOS] Fix encoding `Encodable` function results so arrays, dictionaries, optionals, `RawRepresentable` enums and `Convertible`s route through their dynamic types instead of producing wrong shapes. ([#45575](https://github.com/expo/expo/pull/45575) by [@tsapeta](https://github.com/tsapeta))
25
+
13
26
  ## 56.0.6 — 2026-05-11
14
27
 
15
28
  ### 💡 Others
@@ -27,7 +27,7 @@ if (shouldIncludeCompose) {
27
27
  }
28
28
 
29
29
  group = 'host.exp.exponent'
30
- version = '56.0.6'
30
+ version = '56.0.8'
31
31
 
32
32
  def isExpoModulesCoreTests = {
33
33
  Gradle gradle = getGradle()
@@ -94,7 +94,7 @@ android {
94
94
  defaultConfig {
95
95
  consumerProguardFiles 'proguard-rules.pro'
96
96
  versionCode 1
97
- versionName "56.0.6"
97
+ versionName "56.0.8"
98
98
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
99
99
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
100
100
 
@@ -193,13 +193,11 @@ abstract class ExpoComposeView<T : ComposeProps>(
193
193
  Content()
194
194
  }
195
195
  }
196
- it.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
197
- override fun onViewAttachedToWindow(v: View) {
196
+ it.addOnAttachStateChangeListener(
197
+ OnAttachAfterDetachmentListener(onAttachAfterDetachment = {
198
198
  it.disposeComposition()
199
- }
200
-
201
- override fun onViewDetachedFromWindow(v: View) = Unit
202
- })
199
+ })
200
+ )
203
201
  }
204
202
  addView(composeView)
205
203
  }
@@ -7,22 +7,13 @@
7
7
  #include "JavaScriptModuleObject.h"
8
8
  #include "JavaScriptValue.h"
9
9
  #include "JavaScriptObject.h"
10
- #include "JavaReferencesCache.h"
11
10
  #include "JSReferencesCache.h"
12
11
  #include "JNIDeallocator.h"
13
12
  #include "ThreadSafeJNIGlobalRef.h"
14
13
  #include "javaclasses/JSRunnable.h"
15
14
 
16
- #include <ReactCommon/CallInvokerHolder.h>
17
15
  #include <ReactCommon/CallInvoker.h>
18
16
 
19
- #if IS_NEW_ARCHITECTURE_ENABLED
20
-
21
- #include <ReactCommon/RuntimeExecutor.h>
22
- #include <react/jni/JRuntimeExecutor.h>
23
-
24
- #endif
25
-
26
17
  namespace jni = facebook::jni;
27
18
  namespace jsi = facebook::jsi;
28
19
  namespace react = facebook::react;
@@ -60,10 +60,8 @@ jni::local_ref<JavaCallback::javaobject> JavaCallback::newInstance(
60
60
  return object;
61
61
  }
62
62
 
63
- template<typename T>
64
- void JavaCallback::invokeJSFunction(
65
- ArgsConverter<typename std::remove_const<T>::type> argsConverter,
66
- T arg
63
+ void JavaCallback::invokeWithResolver(
64
+ std::function<void(jsi::Runtime &rt, jsi::Function &jsFunction)> resolver
67
65
  ) {
68
66
  const auto strongCallbackContext = this->callbackContext.lock();
69
67
  // The context were deallocated before the callback was invoked.
@@ -80,8 +78,7 @@ void JavaCallback::invokeJSFunction(
80
78
  jsInvoker->invokeAsync(
81
79
  [
82
80
  context = callbackContext,
83
- argsConverter = std::move(argsConverter),
84
- arg = std::move(arg)
81
+ resolver = std::move(resolver)
85
82
  ]() -> void {
86
83
  auto strongContext = context.lock();
87
84
  // The context were deallocated before the callback was invoked.
@@ -98,25 +95,11 @@ void JavaCallback::invokeJSFunction(
98
95
  jsi::Function &jsFunction = strongContext->resolveHolder.value();
99
96
  jsi::Runtime &rt = strongContext->rt;
100
97
 
101
- argsConverter(rt, jsFunction, std::move(arg));
98
+ resolver(rt, jsFunction);
102
99
  strongContext->invalidate();
103
100
  });
104
101
  }
105
102
 
106
- template<class T>
107
- void JavaCallback::invokeJSFunction(T arg) {
108
- invokeJSFunction<T>(
109
- [](
110
- jsi::Runtime &rt,
111
- jsi::Function &jsFunction,
112
- T arg
113
- ) {
114
- jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::forward<T>(arg)));
115
- },
116
- arg
117
- );
118
- }
119
-
120
103
  template<class T>
121
104
  void JavaCallback::invokeJSFunctionForArray(T &arg) {
122
105
  size_t size = arg->size();
@@ -125,38 +108,51 @@ void JavaCallback::invokeJSFunctionForArray(T &arg) {
125
108
  rawArray.size = size;
126
109
  rawArray.data = std::move(region);
127
110
 
128
- invokeJSFunction<decltype(rawArray)>(
129
- std::move(rawArray)
111
+ invokeWithResolver(
112
+ [rawArray = std::move(rawArray)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
113
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(rawArray)));
114
+ }
130
115
  );
131
116
  }
132
117
 
133
118
  void JavaCallback::invoke() {
134
- invokeJSFunction<nullptr_t>(
135
- [](
136
- jsi::Runtime &rt,
137
- jsi::Function &jsFunction,
138
- nullptr_t arg
139
- ) {
119
+ invokeWithResolver(
120
+ [](jsi::Runtime &rt, jsi::Function &jsFunction) {
140
121
  jsFunction.call(rt, {jsi::Value::null()});
141
- },
142
- nullptr
122
+ }
143
123
  );
144
124
  }
145
125
 
146
126
  void JavaCallback::invokeBool(bool result) {
147
- invokeJSFunction(result);
127
+ invokeWithResolver(
128
+ [result](jsi::Runtime &rt, jsi::Function &jsFunction) {
129
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, result));
130
+ }
131
+ );
148
132
  }
149
133
 
150
134
  void JavaCallback::invokeInt(int result) {
151
- invokeJSFunction(result);
135
+ invokeWithResolver(
136
+ [result](jsi::Runtime &rt, jsi::Function &jsFunction) {
137
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, result));
138
+ }
139
+ );
152
140
  }
153
141
 
154
142
  void JavaCallback::invokeDouble(double result) {
155
- invokeJSFunction(result);
143
+ invokeWithResolver(
144
+ [result](jsi::Runtime &rt, jsi::Function &jsFunction) {
145
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, result));
146
+ }
147
+ );
156
148
  }
157
149
 
158
150
  void JavaCallback::invokeFloat(float result) {
159
- invokeJSFunction(result);
151
+ invokeWithResolver(
152
+ [result](jsi::Runtime &rt, jsi::Function &jsFunction) {
153
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, result));
154
+ }
155
+ );
160
156
  }
161
157
 
162
158
  void JavaCallback::invokeString(jni::alias_ref<jstring> result) {
@@ -164,41 +160,76 @@ void JavaCallback::invokeString(jni::alias_ref<jstring> result) {
164
160
  const char *rawValue = env->GetStringUTFChars(result.get(), nullptr);
165
161
  std::string parsedResult = rawValue;
166
162
  env->ReleaseStringUTFChars(result.get(), rawValue);
167
- invokeJSFunction(parsedResult);
163
+ invokeWithResolver(
164
+ [parsedResult = std::move(parsedResult)](jsi::Runtime &rt, jsi::Function &jsFunction) {
165
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, parsedResult));
166
+ }
167
+ );
168
168
  }
169
169
 
170
170
  void JavaCallback::invokeCollection(jni::alias_ref<jni::JCollection<jobject>> result) {
171
- invokeJSFunction<
172
- jni::global_ref<jni::JCollection<jobject>>
173
- >(jni::make_global(result));
171
+ jni::global_ref<jni::JCollection<jobject>> globalResult = jni::make_global(result);
172
+ invokeWithResolver(
173
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
174
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
175
+ }
176
+ );
174
177
  }
175
178
 
176
179
  void JavaCallback::invokeMap(jni::alias_ref<jni::JMap<jstring, jobject>> result) {
177
- invokeJSFunction<
178
- jni::global_ref<jni::JMap<jstring, jobject>>
179
- >(jni::make_global(result));
180
+ jni::global_ref<jni::JMap<jstring, jobject>> globalResult = jni::make_global(result);
181
+ invokeWithResolver(
182
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
183
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
184
+ }
185
+ );
180
186
  }
181
187
 
182
188
  void
183
189
  JavaCallback::invokeWritableArray(jni::alias_ref<react::WritableNativeArray::javaobject> result) {
184
- invokeJSFunction(result->cthis()->consume());
190
+ auto consumed = result->cthis()->consume();
191
+ invokeWithResolver(
192
+ [consumed = std::move(consumed)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
193
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(consumed)));
194
+ }
195
+ );
185
196
  }
186
197
 
187
198
  void JavaCallback::invokeWritableMap(jni::alias_ref<react::WritableNativeMap::javaobject> result) {
188
- invokeJSFunction(result->cthis()->consume());
199
+ auto consumed = result->cthis()->consume();
200
+ invokeWithResolver(
201
+ [consumed = std::move(consumed)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
202
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(consumed)));
203
+ }
204
+ );
189
205
  }
190
206
 
191
207
  void JavaCallback::invokeSharedObject(jni::alias_ref<JSharedObject::javaobject> result) {
192
- invokeJSFunction(jni::make_global(result));
208
+ auto globalResult = jni::make_global(result);
209
+ invokeWithResolver(
210
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
211
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
212
+ }
213
+ );
193
214
  }
194
215
 
195
216
  void JavaCallback::invokeJavaScriptArrayBuffer(
196
217
  jni::alias_ref<JavaScriptArrayBuffer::javaobject> result) {
197
- invokeJSFunction(jni::make_global(result));
218
+ auto globalResult = jni::make_global(result);
219
+ invokeWithResolver(
220
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
221
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
222
+ }
223
+ );
198
224
  }
199
225
 
200
226
  void JavaCallback::invokeNativeArrayBuffer(jni::alias_ref<NativeArrayBuffer::javaobject> result) {
201
- invokeJSFunction(jni::make_global(result));
227
+ auto globalResult = jni::make_global(result);
228
+ invokeWithResolver(
229
+ [globalResult = std::move(globalResult)](jsi::Runtime &rt, jsi::Function &jsFunction) mutable {
230
+ jsFunction.call(rt, convertToJS(jni::Environment::current(), rt, std::move(globalResult)));
231
+ }
232
+ );
202
233
  }
203
234
 
204
235
  void JavaCallback::invokeIntArray(jni::alias_ref<jni::JArrayInt> result) {
@@ -90,19 +90,9 @@ private:
90
90
  void invokeDoubleArray(jni::alias_ref<jni::JArrayDouble> result);
91
91
  void invokeFloatArray(jni::alias_ref<jni::JArrayFloat> result);
92
92
 
93
- template<class T>
94
- using ArgsConverter = std::function<void(jsi::Runtime &rt, jsi::Function &jsFunction, T arg)>;
95
-
96
- template<class T>
97
- inline void invokeJSFunction(
98
- ArgsConverter<typename std::remove_const<T>::type> argsConverter,
99
- T arg
100
- );
93
+ void invokeWithResolver(std::function<void(jsi::Runtime &rt, jsi::Function &jsFunction)> resolver);
101
94
 
102
95
  template<class T>
103
96
  void invokeJSFunctionForArray(T &arg);
104
-
105
- template<class T>
106
- inline void invokeJSFunction(T arg);
107
97
  };
108
98
  } // namespace expo
@@ -5,7 +5,6 @@
5
5
  #include "ExpoHeader.pch"
6
6
  #include "JSIObjectWrapper.h"
7
7
  #include "JavaScriptTypedArray.h"
8
- #include "JavaScriptArrayBuffer.h"
9
8
  #include "JNIDeallocator.h"
10
9
 
11
10
  namespace jni = facebook::jni;
@@ -2,11 +2,21 @@
2
2
 
3
3
  #include "JSDecoratorsBridgingObject.h"
4
4
 
5
+ #include "JSFunctionsDecorator.h"
6
+ #include "JSPropertiesDecorator.h"
7
+ #include "JSConstantsDecorator.h"
8
+ #include "JSObjectDecorator.h"
5
9
  #include "JSClassesDecorator.h"
6
10
  #include "../types/ReturnType.h"
7
11
 
8
12
  namespace expo {
9
13
 
14
+ JSDecoratorsBridgingObject::~JSDecoratorsBridgingObject() = default;
15
+
16
+ JSClassesDecorator* JSDecoratorsBridgingObject::getClassDecorator() const {
17
+ return classDecorator.get();
18
+ }
19
+
10
20
  jni::local_ref<jni::HybridClass<JSDecoratorsBridgingObject>::jhybriddata>
11
21
  JSDecoratorsBridgingObject::initHybrid(jni::alias_ref<jhybridobject> jThis) {
12
22
  return makeCxxInstance();
@@ -8,18 +8,20 @@
8
8
  #include "../JNIFunctionBody.h"
9
9
  #include "../types/ExpectedType.h"
10
10
 
11
- #include "JSFunctionsDecorator.h"
12
- #include "JSPropertiesDecorator.h"
13
- #include "JSConstantsDecorator.h"
14
- #include "JSObjectDecorator.h"
15
- #include "JSClassesDecorator.h"
16
-
17
11
  namespace jni = facebook::jni;
18
12
 
19
13
  namespace expo {
20
14
 
15
+ class JSFunctionsDecorator;
16
+ class JSPropertiesDecorator;
17
+ class JSConstantsDecorator;
18
+ class JSObjectDecorator;
19
+ class JSClassesDecorator;
20
+
21
21
  class JSDecoratorsBridgingObject : public jni::HybridClass<JSDecoratorsBridgingObject> {
22
22
  public:
23
+ ~JSDecoratorsBridgingObject();
24
+
23
25
  static auto constexpr
24
26
  kJavaDescriptor = "Lexpo/modules/kotlin/jni/decorators/JSDecoratorsBridgingObject;";
25
27
  static auto constexpr TAG = "JSDecoratorsBridgingObject";
@@ -87,9 +89,7 @@ public:
87
89
  /**
88
90
  * Returns the class decorator, or nullptr if none was registered.
89
91
  */
90
- JSClassesDecorator* getClassDecorator() const {
91
- return classDecorator.get();
92
- }
92
+ JSClassesDecorator* getClassDecorator() const;
93
93
 
94
94
  private:
95
95
  friend HybridBase;
@@ -1,5 +1,6 @@
1
1
  #include "NativeStatePropsGetter.h"
2
2
  #include "AndroidExpoViewState.h"
3
+ #include <react/fabric/StateWrapperImpl.h>
3
4
 
4
5
  #include <react/renderer/core/ConcreteState.h>
5
6
 
@@ -1,7 +1,6 @@
1
1
  #pragma once
2
2
 
3
3
  #include "../ExpoHeader.pch"
4
- #include <react/fabric/StateWrapperImpl.h>
5
4
 
6
5
  namespace jni = facebook::jni;
7
6
  namespace react = facebook::react;
@@ -5,6 +5,7 @@ package expo.modules.kotlin.devtools.cdp
5
5
  import expo.modules.kotlin.devtools.ExpoNetworkInspectOkHttpNetworkInterceptor
6
6
  import expo.modules.kotlin.devtools.toSingleMap
7
7
  import okio.Buffer
8
+ import org.json.JSONArray
8
9
  import org.json.JSONObject
9
10
  import java.math.BigDecimal
10
11
 
@@ -155,7 +156,7 @@ data class RequestWillBeSentParams(
155
156
 
156
157
  data class RequestWillBeSentExtraInfoParams(
157
158
  val requestId: RequestId,
158
- val associatedCookies: Map<String, String> = emptyMap(),
159
+ val associatedCookies: List<Any> = emptyList(),
159
160
  val headers: Headers,
160
161
  val connectTiming: ConnectTiming
161
162
  ) : JsonSerializable {
@@ -168,7 +169,7 @@ data class RequestWillBeSentExtraInfoParams(
168
169
  override fun toJSONObject(): JSONObject {
169
170
  return JSONObject().apply {
170
171
  put("requestId", requestId)
171
- put("associatedCookies", JSONObject(associatedCookies))
172
+ put("associatedCookies", JSONArray(associatedCookies))
172
173
  put("headers", JSONObject(headers))
173
174
  put("connectTiming", connectTiming.toJSONObject())
174
175
  }
@@ -0,0 +1,41 @@
1
+ package expo.modules.kotlin.views
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import android.view.View
6
+
7
+ /**
8
+ * Runs a callback only when a view is attached after staying detached through one main-loop turn.
9
+ * React Native can synchronously detach and reattach native children during view reparenting, and
10
+ * those moves should not be treated like a full detach.
11
+ */
12
+ internal class OnAttachAfterDetachmentListener(
13
+ private val onAttachAfterDetachment: () -> Unit,
14
+ private val post: (Runnable) -> Unit = { runnable ->
15
+ Handler(Looper.getMainLooper()).post(runnable)
16
+ }
17
+ ) : View.OnAttachStateChangeListener {
18
+ private var shouldRunOnAttach = false
19
+ private var detachGeneration = 0
20
+
21
+ override fun onViewAttachedToWindow(v: View) {
22
+ detachGeneration += 1
23
+
24
+ if (shouldRunOnAttach) {
25
+ shouldRunOnAttach = false
26
+ onAttachAfterDetachment()
27
+ }
28
+ }
29
+
30
+ override fun onViewDetachedFromWindow(v: View) {
31
+ val generation = ++detachGeneration
32
+
33
+ post(
34
+ Runnable {
35
+ if (detachGeneration == generation && !v.isAttachedToWindow) {
36
+ shouldRunOnAttach = true
37
+ }
38
+ }
39
+ )
40
+ }
41
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"CoreModule.d.ts","sourceRoot":"","sources":["../../src/polyfill/CoreModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,IAAI,gBAAgB,EAChC,iBAAiB,EAClB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE/E,qBAAa,YAAY,CAAC,UAAU,SAAS,SAAS,CAAE,YAAW,gBAAgB;IACjF,OAAO,CAAC,SAAS,CAAC,CAAuC;IAEzD,WAAW,CAAC,SAAS,SAAS,MAAM,UAAU,EAC5C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,GAC9B,iBAAiB;IAuBpB,cAAc,CAAC,SAAS,SAAS,MAAM,UAAU,EAC/C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,GAC9B,IAAI;IAOP,kBAAkB,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAQlF,IAAI,CAAC,SAAS,SAAS,MAAM,UAAU,EACrC,SAAS,EAAE,SAAS,EACpB,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GACzC,IAAI;IAeP,aAAa,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM;IAI/E,cAAc,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAE9E,aAAa,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;CAC9E;AAED,qBAAa,YAAY,CAAC,UAAU,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAC/D,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,gBAAgB;IAE3B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,cAAc,CAAC,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,YAAY,CAAC,UAAU,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAC/D,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,gBAAgB;IAE3B,OAAO,IAAI,IAAI;CAGhB;AAED,qBAAa,SAAS,CAClB,cAAc,SAAS,MAAM,GAAG,SAAS,EACzC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAErD,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,aAAa,CAAC,cAAc,CAAC;IAExC,aAAa,EAAE,MAAM,CAAa;CACnC"}
1
+ {"version":3,"file":"CoreModule.d.ts","sourceRoot":"","sources":["../../src/polyfill/CoreModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,IAAI,gBAAgB,EAChC,iBAAiB,EAClB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE/E,qBAAa,YAAY,CAAC,UAAU,SAAS,SAAS,CAAE,YAAW,gBAAgB;IACjF,OAAO,CAAC,SAAS,CAAC,CAAuC;IAEzD,WAAW,CAAC,SAAS,SAAS,MAAM,UAAU,EAC5C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,GAC9B,iBAAiB;IAuBpB,cAAc,CAAC,SAAS,SAAS,MAAM,UAAU,EAC/C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,GAC9B,IAAI;IAOP,kBAAkB,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAQlF,IAAI,CAAC,SAAS,SAAS,MAAM,UAAU,EACrC,SAAS,EAAE,SAAS,EACpB,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GACzC,IAAI;IAeP,aAAa,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM;IAI/E,cAAc,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAE9E,aAAa,CAAC,SAAS,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;CAC9E;AAED,qBAAa,YAAY,CAAC,UAAU,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAC/D,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,gBAAgB;IAE3B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,cAAc,CAAC,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,YAAY,CAAC,UAAU,SAAS,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAC/D,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,gBAAgB;IAE3B,OAAO,IAAI,IAAI;CAGhB;AAED,qBAAa,SAAS,CACpB,cAAc,SAAS,MAAM,GAAG,SAAS,EACzC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAEnD,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,aAAa,CAAC,cAAc,CAAC;IAExC,aAAa,EAAE,MAAM,CAAa;CACnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"SharedRef.d.ts","sourceRoot":"","sources":["../../src/ts-declarations/SharedRef.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,SAAS,CAC1B,cAAc,SAAS,MAAM,GAAG,SAAS,EACzC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAErD,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,YAAY,CAAC,UAAU,CAAC;IAEnC;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,cAAc,CAAC;IAE7C;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB"}
1
+ {"version":3,"file":"SharedRef.d.ts","sourceRoot":"","sources":["../../src/ts-declarations/SharedRef.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,SAAS,CAC5B,cAAc,SAAS,MAAM,GAAG,SAAS,EACzC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAEnD,SAAQ,YAAY,CAAC,UAAU,CAC/B,YAAW,YAAY,CAAC,UAAU,CAAC;IAEnC;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,cAAc,CAAC;IAE7C;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB"}
@@ -29,15 +29,16 @@ internal struct DynamicEncodableType: AnyDynamicType {
29
29
  }
30
30
 
31
31
  func castToJS<ValueType>(_ value: ValueType, appContext: AppContext) throws -> JavaScriptValue {
32
+ return try castToJS(value, appContext: appContext, in: try appContext.runtime)
33
+ }
34
+
35
+ func castToJS<ValueType>(_ value: ValueType, appContext: AppContext, in runtime: JavaScriptRuntime) throws -> JavaScriptValue {
32
36
  if let value = value as? JavaScriptValue {
33
37
  return value
34
38
  }
35
39
  if let value = value as? Encodable {
36
- let runtime = try appContext.runtime
37
- let encoder = JSValueEncoder(runtime: runtime)
38
-
40
+ let encoder = JSValueEncoder(appContext: appContext, runtime: runtime)
39
41
  try value.encode(to: encoder)
40
-
41
42
  return encoder.value
42
43
  }
43
44
  throw Conversions.ConversionToJSFailedException((kind: .object, nativeType: ValueType.self))
@@ -3,12 +3,21 @@
3
3
  import ExpoModulesJSI
4
4
 
5
5
  /**
6
- Encodes `Encodable` objects or values to `JavaScriptValue`. This implementation is incomplete,
7
- but it supports basic use cases with structs defined by the user and when the default `Encodable` implementation is used.
6
+ Encodes `Encodable` values to `JavaScriptValue`.
7
+
8
+ For any property whose static type is known to the dynamic-type registry
9
+ (anything routed via the `~` operator: arrays, dictionaries, optionals,
10
+ `RawRepresentable` enums, `Convertible`s, `JavaScriptValue` and so on),
11
+ the encoder takes the fast path through `castToJS`, preserving full
12
+ element-type metadata.
13
+
14
+ Only types the registry doesn't recognize (i.e. plain `Encodable` structs and
15
+ classes) fall back to Swift's `Encodable` machinery via `value.encode(to:)`.
8
16
  */
9
17
  internal final class JSValueEncoder: Encoder {
18
+ private let appContext: AppContext
10
19
  private let runtime: JavaScriptRuntime
11
- private let valueHolder = JSValueHolder()
20
+ private let valueHolder: JSValueHolder
12
21
 
13
22
  /**
14
23
  The result of encoding to `JavaScriptValue`. Use this property after running `encode(to:)` on the encodable.
@@ -18,43 +27,106 @@ internal final class JSValueEncoder: Encoder {
18
27
  }
19
28
 
20
29
  /**
21
- Initializes the encoder with the given runtime in which the value will be created.
30
+ Initializes the encoder with the given app context. Throws if the runtime has been lost.
31
+ */
32
+ convenience init(appContext: AppContext) throws {
33
+ try self.init(appContext: appContext, runtime: appContext.runtime)
34
+ }
35
+
36
+ /**
37
+ Initializes the encoder with the given app context and an explicit runtime.
38
+ Use this when produced JS values must live in a runtime other than the
39
+ one currently held by the app context.
22
40
  */
23
- init(runtime: JavaScriptRuntime) {
41
+ convenience init(appContext: AppContext, runtime: JavaScriptRuntime) {
42
+ self.init(
43
+ appContext: appContext,
44
+ runtime: runtime,
45
+ codingPath: [],
46
+ valueHolder: JSValueHolder()
47
+ )
48
+ }
49
+
50
+ fileprivate init(
51
+ appContext: AppContext,
52
+ runtime: JavaScriptRuntime,
53
+ codingPath: [any CodingKey],
54
+ valueHolder: JSValueHolder
55
+ ) {
56
+ self.appContext = appContext
24
57
  self.runtime = runtime
58
+ self.codingPath = codingPath
59
+ self.valueHolder = valueHolder
25
60
  }
26
61
 
27
62
  // MARK: - Encoder
28
63
 
29
- // We don't use `codingPath` and `userInfo`, but they are required by the protocol.
30
- let codingPath: [any CodingKey] = []
64
+ let codingPath: [any CodingKey]
31
65
  let userInfo: [CodingUserInfoKey: Any] = [:]
32
66
 
33
- /**
34
- Returns an encoding container appropriate for holding multiple values keyed by the given key type.
35
- */
36
67
  func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
37
- let container = JSObjectEncodingContainer<Key>(to: valueHolder, runtime: runtime)
68
+ let container = JSObjectEncodingContainer<Key>(
69
+ to: valueHolder,
70
+ appContext: appContext,
71
+ runtime: runtime,
72
+ codingPath: codingPath
73
+ )
38
74
  return KeyedEncodingContainer(container)
39
75
  }
40
76
 
41
- /**
42
- Returns an encoding container appropriate for holding multiple unkeyed values.
43
- */
44
77
  func unkeyedContainer() -> any UnkeyedEncodingContainer {
45
- return JSArrayEncodingContainer(to: valueHolder, runtime: runtime)
78
+ return JSArrayEncodingContainer(
79
+ to: valueHolder,
80
+ appContext: appContext,
81
+ runtime: runtime,
82
+ codingPath: codingPath
83
+ )
46
84
  }
47
85
 
48
- /**
49
- Returns an encoding container appropriate for holding a single primitive value, including optionals.
50
- */
51
86
  func singleValueContainer() -> any SingleValueEncodingContainer {
52
- return JSValueEncodingContainer(to: valueHolder, runtime: runtime)
87
+ return JSValueEncodingContainer(
88
+ to: valueHolder,
89
+ appContext: appContext,
90
+ runtime: runtime,
91
+ codingPath: codingPath
92
+ )
53
93
  }
54
94
  }
55
95
 
96
+ // MARK: - Shared encoding logic
97
+
56
98
  /**
57
- An object that holds a JS value that could be overriden by the encoding container.
99
+ Encodes a single value into a `JavaScriptValue`, routing through the dynamic-type
100
+ registry whenever possible and falling back to `Encodable` only for plain types.
101
+ */
102
+ private func encodeUsingDynamicType<ValueType: Encodable>(
103
+ _ value: ValueType,
104
+ appContext: AppContext,
105
+ runtime: JavaScriptRuntime,
106
+ codingPath: [any CodingKey]
107
+ ) throws -> JavaScriptValue {
108
+ let dynamicType = ~ValueType.self
109
+
110
+ if !(dynamicType is DynamicEncodableType) {
111
+ return try dynamicType.castToJS(value, appContext: appContext, in: runtime)
112
+ }
113
+
114
+ // Plain Encodable type the registry doesn't recognize — recurse via Encodable.
115
+ let holder = JSValueHolder()
116
+ let encoder = JSValueEncoder(
117
+ appContext: appContext,
118
+ runtime: runtime,
119
+ codingPath: codingPath,
120
+ valueHolder: holder
121
+ )
122
+ try value.encode(to: encoder)
123
+ return holder.value
124
+ }
125
+
126
+ // MARK: - Containers
127
+
128
+ /**
129
+ An object that holds a JS value, mutated by an encoding container as it makes progress.
58
130
  */
59
131
  private final class JSValueHolder {
60
132
  var value: JavaScriptValue = .undefined
@@ -64,146 +136,233 @@ private final class JSValueHolder {
64
136
  Single value container used to encode primitive values, including optionals.
65
137
  */
66
138
  private struct JSValueEncodingContainer: SingleValueEncodingContainer {
67
- private weak var runtime: JavaScriptRuntime?
139
+ private let appContext: AppContext
140
+ private let runtime: JavaScriptRuntime
68
141
  private let valueHolder: JSValueHolder
142
+ let codingPath: [any CodingKey]
69
143
 
70
- init(to valueHolder: JSValueHolder, runtime: JavaScriptRuntime?) {
71
- self.runtime = runtime
144
+ init(to valueHolder: JSValueHolder, appContext: AppContext, runtime: JavaScriptRuntime, codingPath: [any CodingKey]) {
72
145
  self.valueHolder = valueHolder
146
+ self.appContext = appContext
147
+ self.runtime = runtime
148
+ self.codingPath = codingPath
73
149
  }
74
150
 
75
- // MARK: - SingleValueEncodingContainer
76
-
77
- // Unused, but required by the protocol.
78
- let codingPath: [any CodingKey] = []
79
-
80
151
  mutating func encodeNil() throws {
81
- self.valueHolder.value = .null
152
+ valueHolder.value = .null
82
153
  }
83
154
 
84
155
  mutating func encode<ValueType: Encodable>(_ value: ValueType) throws {
85
- guard let runtime else {
86
- // Do nothing when the runtime is already deallocated
87
- return
88
- }
89
- let jsValue = JavaScriptValue.from(value, runtime: runtime)
90
-
91
- // If the given value couldn't be converted to JavaScriptValue, try to encode it farther.
92
- // It might be the case when the default implementation of `Encodable` has chosen the single value container
93
- // for an optional type that should rather use keyed or unkeyed container when unwrapped.
94
- if jsValue.isUndefined() {
95
- let encoder = JSValueEncoder(runtime: runtime)
96
- try value.encode(to: encoder)
97
- self.valueHolder.value = encoder.value
98
- return
99
- }
100
- self.valueHolder.value = jsValue
156
+ valueHolder.value = try encodeUsingDynamicType(
157
+ value,
158
+ appContext: appContext,
159
+ runtime: runtime,
160
+ codingPath: codingPath
161
+ )
101
162
  }
102
163
  }
103
164
 
104
165
  /**
105
166
  Keyed container that encodes to a JavaScript object.
106
167
 
107
- - Note: This is a `class` (rather than `struct` like `JSArrayEncodingContainer` below)
108
- because it holds a non-copyable `JavaScriptObject`. A struct property of a non-copyable
109
- type makes the containing struct non-copyable too, which conflicts with
110
- `KeyedEncodingContainerProtocol`'s expectation of a copyable conformer. Using a class
111
- sidesteps the constraint via reference semantics.
168
+ - Note: This is a `class` (not a `struct`) because it holds a non-copyable
169
+ `JavaScriptObject`. A struct property of a non-copyable type makes the
170
+ containing struct non-copyable too, which conflicts with
171
+ `KeyedEncodingContainerProtocol`'s expectation of a copyable conformer.
172
+ Using a class sidesteps the constraint via reference semantics.
112
173
  */
113
- private class JSObjectEncodingContainer<Key: CodingKey>: KeyedEncodingContainerProtocol {
114
- private weak var runtime: JavaScriptRuntime?
174
+ private final class JSObjectEncodingContainer<Key: CodingKey>: KeyedEncodingContainerProtocol {
175
+ private let appContext: AppContext
176
+ private let runtime: JavaScriptRuntime
115
177
  private let valueHolder: JSValueHolder
116
- private var object: JavaScriptObject
178
+ private let object: JavaScriptObject
179
+ let codingPath: [any CodingKey]
117
180
 
118
- init(to valueHolder: JSValueHolder, runtime: JavaScriptRuntime) {
181
+ init(to valueHolder: JSValueHolder, appContext: AppContext, runtime: JavaScriptRuntime, codingPath: [any CodingKey]) {
119
182
  let object = runtime.createObject()
120
183
  valueHolder.value = object.asValue()
121
184
 
185
+ self.appContext = appContext
122
186
  self.runtime = runtime
123
- self.object = object
124
187
  self.valueHolder = valueHolder
188
+ self.object = object
189
+ self.codingPath = codingPath
125
190
  }
126
191
 
127
- // MARK: - KeyedEncodingContainerProtocol
128
-
129
- // Unused, but required by the protocol.
130
- var codingPath: [any CodingKey] = []
131
-
132
192
  func encodeNil(forKey key: Key) throws {
133
193
  object.setProperty(key.stringValue, value: JavaScriptValue.null)
134
194
  }
135
195
 
136
196
  func encode<ValueType: Encodable>(_ value: ValueType, forKey key: Key) throws {
137
- guard let runtime else {
138
- // Do nothing when the runtime is already deallocated
139
- return
197
+ let encoded = try encodeUsingDynamicType(
198
+ value,
199
+ appContext: appContext,
200
+ runtime: runtime,
201
+ codingPath: codingPath + [key]
202
+ )
203
+ object.setProperty(key.stringValue, value: encoded)
204
+ }
205
+
206
+ // The default `KeyedEncodingContainerProtocol.encodeIfPresent` does nothing for
207
+ // nil values, which leaves the JS object without the key entirely — so `'label' in obj`
208
+ // returns false and consumers can't distinguish "absent" from "explicitly null".
209
+ // Override every overload (one per primitive plus a generic `Encodable` one) so nil
210
+ // optional fields produce an explicit `null` instead.
211
+ func encodeIfPresent<ValueType: Encodable>(_ value: ValueType?, forKey key: Key) throws {
212
+ if let value {
213
+ try encode(value, forKey: key)
214
+ } else {
215
+ try encodeNil(forKey: key)
216
+ }
217
+ }
218
+
219
+ func encodeIfPresent(_ value: Bool?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
220
+ func encodeIfPresent(_ value: String?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
221
+ func encodeIfPresent(_ value: Double?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
222
+ func encodeIfPresent(_ value: Float?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
223
+ func encodeIfPresent(_ value: Int?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
224
+ func encodeIfPresent(_ value: Int8?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
225
+ func encodeIfPresent(_ value: Int16?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
226
+ func encodeIfPresent(_ value: Int32?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
227
+ func encodeIfPresent(_ value: Int64?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
228
+ func encodeIfPresent(_ value: UInt?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
229
+ func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
230
+ func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
231
+ func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
232
+ func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws { try encodeOptional(value, forKey: key) }
233
+
234
+ private func encodeOptional<ValueType: Encodable>(_ value: ValueType?, forKey key: Key) throws {
235
+ if let value {
236
+ try encode(value, forKey: key)
237
+ } else {
238
+ try encodeNil(forKey: key)
140
239
  }
141
- let encoder = JSValueEncoder(runtime: runtime)
142
- try value.encode(to: encoder)
143
- object.setProperty(key.stringValue, value: encoder.value)
144
240
  }
145
241
 
146
242
  func nestedContainer<NestedKey: CodingKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
147
- fatalError("JSValueEncoder does not support nested containers")
243
+ let holder = JSValueHolder()
244
+ let container = JSObjectEncodingContainer<NestedKey>(
245
+ to: holder,
246
+ appContext: appContext,
247
+ runtime: runtime,
248
+ codingPath: codingPath + [key]
249
+ )
250
+ object.setProperty(key.stringValue, value: holder.value)
251
+ return KeyedEncodingContainer(container)
148
252
  }
149
253
 
150
254
  func nestedUnkeyedContainer(forKey key: Key) -> any UnkeyedEncodingContainer {
151
- fatalError("JSValueEncoder does not support nested containers")
255
+ let holder = JSValueHolder()
256
+ let container = JSArrayEncodingContainer(
257
+ to: holder,
258
+ appContext: appContext,
259
+ runtime: runtime,
260
+ codingPath: codingPath + [key]
261
+ )
262
+ object.setProperty(key.stringValue, value: holder.value)
263
+ return container
152
264
  }
153
265
 
154
266
  func superEncoder() -> any Encoder {
155
- fatalError("superEncoder() is not implemented in JSValueEncoder")
267
+ fatalError("JSValueEncoder does not support superEncoder()")
156
268
  }
157
269
 
158
270
  func superEncoder(forKey key: Key) -> any Encoder {
159
- return self.superEncoder()
271
+ fatalError("JSValueEncoder does not support superEncoder(forKey:)")
160
272
  }
161
273
  }
162
274
 
163
275
  /**
164
276
  Unkeyed container that encodes values to a JavaScript array.
277
+
278
+ Like `JSObjectEncodingContainer`, this is a `class` so it can hold the
279
+ non-copyable `JavaScriptArray` directly without forcing the container itself
280
+ to be non-copyable.
165
281
  */
166
- private struct JSArrayEncodingContainer: UnkeyedEncodingContainer {
167
- private weak var runtime: JavaScriptRuntime?
282
+ private final class JSArrayEncodingContainer: UnkeyedEncodingContainer {
283
+ private let appContext: AppContext
284
+ private let runtime: JavaScriptRuntime
168
285
  private let valueHolder: JSValueHolder
169
- private var items: [JavaScriptValue] = []
286
+ private let array: JavaScriptArray
287
+ let codingPath: [any CodingKey]
288
+ var count: Int = 0
170
289
 
171
- init(to valueHolder: JSValueHolder, runtime: JavaScriptRuntime) {
290
+ init(to valueHolder: JSValueHolder, appContext: AppContext, runtime: JavaScriptRuntime, codingPath: [any CodingKey]) {
291
+ let array = runtime.createArray()
292
+ valueHolder.value = array.asValue()
293
+
294
+ self.appContext = appContext
172
295
  self.runtime = runtime
173
296
  self.valueHolder = valueHolder
297
+ self.array = array
298
+ self.codingPath = codingPath
174
299
  }
175
300
 
176
- // MARK: - UnkeyedEncodingContainer
177
-
178
- // Unused, but required by the protocol.
179
- var codingPath: [any CodingKey] = []
180
- var count: Int = 0
301
+ func encodeNil() throws {
302
+ array[count] = JavaScriptValue.null
303
+ count += 1
304
+ }
181
305
 
182
- mutating func encodeNil() throws {
183
- items.append(JavaScriptValue.null)
306
+ func encode<ValueType: Encodable>(_ value: ValueType) throws {
307
+ let encoded = try encodeUsingDynamicType(
308
+ value,
309
+ appContext: appContext,
310
+ runtime: runtime,
311
+ codingPath: codingPath + [AnyCodingKey(intValue: count)]
312
+ )
313
+ array[count] = encoded
314
+ count += 1
184
315
  }
185
316
 
186
- mutating func encode<ValueType: Encodable>(_ value: ValueType) throws {
187
- guard let runtime else {
188
- // Do nothing when the runtime is already deallocated
189
- return
190
- }
191
- let encoder = JSValueEncoder(runtime: runtime)
192
- try value.encode(to: encoder)
317
+ func nestedContainer<NestedKey: CodingKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
318
+ let holder = JSValueHolder()
319
+ let container = JSObjectEncodingContainer<NestedKey>(
320
+ to: holder,
321
+ appContext: appContext,
322
+ runtime: runtime,
323
+ codingPath: codingPath + [AnyCodingKey(intValue: count)]
324
+ )
325
+ array[count] = holder.value
326
+ count += 1
327
+ return KeyedEncodingContainer(container)
328
+ }
193
329
 
194
- items.append(encoder.value)
195
- valueHolder.value = .representing(value: items, in: runtime)
330
+ func nestedUnkeyedContainer() -> any UnkeyedEncodingContainer {
331
+ let holder = JSValueHolder()
332
+ let container = JSArrayEncodingContainer(
333
+ to: holder,
334
+ appContext: appContext,
335
+ runtime: runtime,
336
+ codingPath: codingPath + [AnyCodingKey(intValue: count)]
337
+ )
338
+ array[count] = holder.value
339
+ count += 1
340
+ return container
196
341
  }
197
342
 
198
- mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
199
- fatalError("JSValueEncoder does not support nested containers")
343
+ func superEncoder() -> any Encoder {
344
+ fatalError("JSValueEncoder does not support superEncoder()")
200
345
  }
346
+ }
347
+
348
+ // MARK: - Helpers
349
+
350
+ /**
351
+ A coding key carrying just a string and an integer index. Used to extend
352
+ `codingPath` with array indices in the unkeyed container, since unkeyed
353
+ containers have no associated `Key` type to draw from.
354
+ */
355
+ private struct AnyCodingKey: CodingKey {
356
+ let stringValue: String
357
+ let intValue: Int?
201
358
 
202
- mutating func nestedUnkeyedContainer() -> any UnkeyedEncodingContainer {
203
- fatalError("JSValueEncoder does not support nested containers")
359
+ init(stringValue: String) {
360
+ self.stringValue = stringValue
361
+ self.intValue = Int(stringValue)
204
362
  }
205
363
 
206
- mutating func superEncoder() -> any Encoder {
207
- fatalError("superEncoder() is not implemented in JSValueEncoder")
364
+ init(intValue: Int) {
365
+ self.stringValue = String(intValue)
366
+ self.intValue = intValue
208
367
  }
209
368
  }
@@ -110,7 +110,7 @@ struct CdpNetwork {
110
110
 
111
111
  struct RequestWillBeSentExtraInfoParams: EventParams {
112
112
  let requestId: RequestId
113
- var associatedCookies = [String: String]()
113
+ var associatedCookies = [String]()
114
114
  let headers: Headers
115
115
  let connectTiming: ConnectTiming
116
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "56.0.6",
3
+ "version": "56.0.8",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@expo/expo-modules-macros-plugin": "~0.0.8",
50
- "expo-modules-jsi": "~56.0.3",
50
+ "expo-modules-jsi": "~56.0.4",
51
51
  "invariant": "^2.2.4"
52
52
  },
53
53
  "peerDependencies": {
@@ -66,7 +66,7 @@
66
66
  "@types/invariant": "^2.2.33",
67
67
  "expo-module-scripts": "56.0.2"
68
68
  },
69
- "gitHead": "42013232893cb2aa71ab218e9b422d4a8476b3f0",
69
+ "gitHead": "51c27fce31a5b3a877a4b05d832dabf4a99db5e1",
70
70
  "scripts": {
71
71
  "build": "expo-module build",
72
72
  "clean": "expo-module clean",
@@ -100,9 +100,9 @@ export class SharedObject<TEventsMap extends Record<never, never>>
100
100
  }
101
101
 
102
102
  export class SharedRef<
103
- TNativeRefType extends string = 'unknown',
104
- TEventsMap extends EventsMap = Record<never, never>,
105
- >
103
+ TNativeRefType extends string = 'unknown',
104
+ TEventsMap extends EventsMap = Record<never, never>,
105
+ >
106
106
  extends SharedObject<TEventsMap>
107
107
  implements SharedRefType<TNativeRefType>
108
108
  {
@@ -11,9 +11,9 @@ import type { SharedObject } from './SharedObject';
11
11
  * directly to the image view from `expo-image` without any additional writes and reads from the file system.
12
12
  */
13
13
  export declare class SharedRef<
14
- TNativeRefType extends string = 'unknown',
15
- TEventsMap extends EventsMap = Record<never, never>,
16
- >
14
+ TNativeRefType extends string = 'unknown',
15
+ TEventsMap extends EventsMap = Record<never, never>,
16
+ >
17
17
  extends SharedObject<TEventsMap>
18
18
  implements SharedObject<TEventsMap>
19
19
  {