@lucent-lang/lucent 0.0.4 → 0.1.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 (152) hide show
  1. package/README.md +97 -0
  2. package/app.plugin.js +46 -10
  3. package/dist/{bench-CR3E7e2e.js → bench-B555m0wU.js} +10 -6
  4. package/dist/{build-CAIrRSYA.js → build-UZ5Zm5m6.js} +1 -1
  5. package/dist/{build-check-QozuNJm5.js → build-check-Dmf8_yiw.js} +28 -13
  6. package/dist/{check-1SCmYlqA.js → check-D-H9WLG4.js} +1 -1
  7. package/dist/cli.js +62 -13
  8. package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
  9. package/dist/compiler.js +4 -4
  10. package/dist/{dashboard-B-NzjjaN.js → dashboard-C8iF5-6T.js} +2 -2
  11. package/dist/{dev-BYWMP7oC.js → dev-B3lSx5CF.js} +62 -23
  12. package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
  13. package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
  14. package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
  15. package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
  16. package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
  17. package/dist/pipeline-BBY0df_2.js +700 -0
  18. package/dist/project-BLhWwnns.js +342 -0
  19. package/dist/sdk-coverage-DFGiDOzR.js +113 -0
  20. package/dist/sdk-diff-DpPYWzrl.js +109 -0
  21. package/dist/sdk-lock-QOxEFD1o.js +38 -0
  22. package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-DX6ztlPN.js} +2 -2
  23. package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-BShBL1LL.js} +2 -2
  24. package/dist/{sdk-show-CkrdlqlP.js → sdk-show-B3bI-0M7.js} +5 -4
  25. package/dist/sdk-usage-ljX5bTuZ.js +95 -0
  26. package/dist/src-DlICbK91.js +39682 -0
  27. package/dist/trace-Bi4hkmPx.js +139 -0
  28. package/lib/core.cjs +368 -1
  29. package/lib/sdk/android.d.ts +54 -1
  30. package/lib/sdk/compose.d.ts +94 -0
  31. package/lib/sdk/compose.schemas.json.gz +0 -0
  32. package/lib/sdk/core.d.ts +201 -0
  33. package/lib/sdk/ios.d.ts +75 -2
  34. package/lib/sdk/ui.d.ts +129 -0
  35. package/metro/transformer.cjs +11 -2
  36. package/package.json +2 -1
  37. package/runtime/cpp/lucent/abort.cpp +59 -11
  38. package/runtime/cpp/lucent/abort.h +22 -6
  39. package/runtime/cpp/lucent/array.h +12 -2
  40. package/runtime/cpp/lucent/async.h +101 -21
  41. package/runtime/cpp/lucent/bigint.cpp +898 -0
  42. package/runtime/cpp/lucent/bigint.h +302 -0
  43. package/runtime/cpp/lucent/buffer.cpp +228 -0
  44. package/runtime/cpp/lucent/buffer.h +328 -0
  45. package/runtime/cpp/lucent/builtins.cpp +4 -1
  46. package/runtime/cpp/lucent/bytes.h +21 -2
  47. package/runtime/cpp/lucent/callback.cpp +29 -0
  48. package/runtime/cpp/lucent/callback.h +230 -0
  49. package/runtime/cpp/lucent/compute.cpp +370 -0
  50. package/runtime/cpp/lucent/compute.h +455 -0
  51. package/runtime/cpp/lucent/conversions.h +111 -0
  52. package/runtime/cpp/lucent/core.h +16 -5
  53. package/runtime/cpp/lucent/equality.h +107 -28
  54. package/runtime/cpp/lucent/execution.cpp +416 -0
  55. package/runtime/cpp/lucent/execution.h +270 -0
  56. package/runtime/cpp/lucent/extension.cpp +97 -0
  57. package/runtime/cpp/lucent/extension.h +210 -0
  58. package/runtime/cpp/lucent/helpers.h +12 -5
  59. package/runtime/cpp/lucent/items.h +52 -0
  60. package/runtime/cpp/lucent/jserror.cpp +9 -0
  61. package/runtime/cpp/lucent/jserror.h +11 -0
  62. package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
  63. package/runtime/cpp/lucent/jsi/convert.h +128 -59
  64. package/runtime/cpp/lucent/jsi/host.cpp +213 -35
  65. package/runtime/cpp/lucent/jsi/host.h +116 -16
  66. package/runtime/cpp/lucent/json.h +19 -1
  67. package/runtime/cpp/lucent/lifecycle.cpp +260 -0
  68. package/runtime/cpp/lucent/lifecycle.h +124 -0
  69. package/runtime/cpp/lucent/lucent.h +7 -0
  70. package/runtime/cpp/lucent/map.h +41 -7
  71. package/runtime/cpp/lucent/native.cpp +35 -47
  72. package/runtime/cpp/lucent/native.h +116 -26
  73. package/runtime/cpp/lucent/number.cpp +38 -38
  74. package/runtime/cpp/lucent/number.h +33 -0
  75. package/runtime/cpp/lucent/operation.h +65 -0
  76. package/runtime/cpp/lucent/ops.h +14 -6
  77. package/runtime/cpp/lucent/platform/android.cpp +223 -10
  78. package/runtime/cpp/lucent/platform/android.h +227 -4
  79. package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
  80. package/runtime/cpp/lucent/platform/android_requests.h +140 -0
  81. package/runtime/cpp/lucent/platform/compose.h +256 -0
  82. package/runtime/cpp/lucent/platform/ios.h +325 -25
  83. package/runtime/cpp/lucent/platform/ios.mm +1 -14
  84. package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
  85. package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
  86. package/runtime/cpp/lucent/platform/swiftui.h +190 -0
  87. package/runtime/cpp/lucent/presentation.cpp +11 -0
  88. package/runtime/cpp/lucent/presentation.h +109 -0
  89. package/runtime/cpp/lucent/reactive.cpp +406 -0
  90. package/runtime/cpp/lucent/reactive.h +566 -0
  91. package/runtime/cpp/lucent/report.cpp +40 -0
  92. package/runtime/cpp/lucent/report.h +16 -0
  93. package/runtime/cpp/lucent/resource.cpp +83 -0
  94. package/runtime/cpp/lucent/resource.h +75 -0
  95. package/runtime/cpp/lucent/scheduler.cpp +38 -124
  96. package/runtime/cpp/lucent/scheduler.h +102 -83
  97. package/runtime/cpp/lucent/scope.cpp +308 -0
  98. package/runtime/cpp/lucent/scope.h +452 -0
  99. package/runtime/cpp/lucent/sizing.h +263 -0
  100. package/runtime/cpp/lucent/slots.h +231 -0
  101. package/runtime/cpp/lucent/trace.cpp +539 -0
  102. package/runtime/cpp/lucent/trace.h +251 -0
  103. package/runtime/cpp/lucent/transport.cpp +9 -0
  104. package/runtime/cpp/lucent/transport.h +341 -0
  105. package/runtime/cpp/lucent/view.cpp +123 -0
  106. package/runtime/cpp/lucent/view.h +157 -0
  107. package/runtime/cpp/rn/LucentComponentView.h +121 -0
  108. package/runtime/cpp/rn/LucentComponentView.mm +479 -0
  109. package/runtime/cpp/rn/LucentModule.cpp +37 -7
  110. package/runtime/cpp/rn/LucentModule.h +3 -0
  111. package/runtime/cpp/rn/LucentViewChildren.h +80 -0
  112. package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
  113. package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
  114. package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
  115. package/runtime/cpp/rn/LucentViewRequests.h +104 -0
  116. package/runtime/cpp/rn/LucentViewSizing.h +345 -0
  117. package/runtime/cpp/rn/LucentViewSlots.h +242 -0
  118. package/runtime/cpp/rn/LucentViewValues.h +125 -0
  119. package/runtime/cpp/rn/LucentViews.h +440 -0
  120. package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
  121. package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
  122. package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
  123. package/runtime/cpp/third_party/dragonbox/README.md +16 -0
  124. package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
  125. package/runtime/js/index.d.ts +11 -1
  126. package/runtime/js/index.js +84 -8
  127. package/runtime/js/views.d.ts +38 -0
  128. package/runtime/js/views.js +226 -0
  129. package/runtime/native/android/CMakeLists.txt +4 -1
  130. package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
  131. package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
  132. package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
  133. package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
  134. package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
  135. package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
  136. package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
  137. package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
  138. package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
  139. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
  140. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
  141. package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
  142. package/runtime/test/jsi/harness.cpp +9 -0
  143. package/schemas/build.schema.json +96 -1
  144. package/schemas/check.schema.json +56 -1
  145. package/schemas/lucent.schema.json +495 -9
  146. package/schemas/sdk-diff.schema.json +70 -0
  147. package/schemas/sdk-lock.schema.json +67 -0
  148. package/ts-plugin/index.js +77 -4
  149. package/dist/pipeline-CH7-2cxu.js +0 -232
  150. package/dist/project-D8tEet0k.js +0 -150
  151. package/dist/sdk-coverage-BoW_3Efd.js +0 -64
  152. package/dist/src-BD7f1_aZ.js +0 -11493
@@ -0,0 +1,211 @@
1
+ // Lucent runtime — a component's React children and slot on iOS (see
2
+ // LucentViewChildren.h). Built only when the app's Lucent code has
3
+ // components, as their host is.
4
+ #if __has_include(<views/lucent_views.h>)
5
+
6
+ #import "LucentViewChildren.h"
7
+
8
+ #include <lucent/report.h>
9
+
10
+ #include <string>
11
+
12
+ namespace {
13
+
14
+ /** Reports what went wrong with a component's children: nothing throws into UIKit. */
15
+ void report(NSString *message)
16
+ {
17
+ lucent::logError((std::string("[lucent] ") + message.UTF8String).c_str());
18
+ }
19
+
20
+ } // namespace
21
+
22
+ @implementation LucentSlotView {
23
+ __weak UIView *_host;
24
+ void (^_placed)(void);
25
+ }
26
+
27
+ - (instancetype)initWithHost:(UIView *)host placed:(void (^)(void))placed
28
+ {
29
+ if ((self = [super initWithFrame:CGRectZero])) {
30
+ _host = host;
31
+ _placed = [placed copy];
32
+ self.clipsToBounds = YES;
33
+ // Follows the view it fills as that view resizes.
34
+ self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
35
+ }
36
+
37
+ return self;
38
+ }
39
+
40
+ // Unsized when setup adds it: it fills the view it is added to.
41
+ - (void)didMoveToSuperview
42
+ {
43
+ [super didMoveToSuperview];
44
+
45
+ UIView *parent = self.superview;
46
+
47
+ if (parent && CGRectIsEmpty(self.frame)) self.frame = parent.bounds;
48
+
49
+ [self setNeedsLayout];
50
+ }
51
+
52
+ - (void)setFrame:(CGRect)frame
53
+ {
54
+ [super setFrame:frame];
55
+ [self setNeedsLayout];
56
+ }
57
+
58
+ - (void)setCenter:(CGPoint)center
59
+ {
60
+ [super setCenter:center];
61
+ [self setNeedsLayout];
62
+ }
63
+
64
+ - (void)layoutSubviews
65
+ {
66
+ [super layoutSubviews];
67
+ [self lucentAlign];
68
+
69
+ if (_placed) _placed();
70
+ }
71
+
72
+ - (void)lucentAlign
73
+ {
74
+ UIView *host = _host;
75
+ UIView *parent = self.superview;
76
+
77
+ if (!host || !parent || ![self isDescendantOfView:host]) return;
78
+
79
+ CGPoint origin = [parent convertPoint:self.frame.origin toView:host];
80
+ CGRect bounds = self.bounds;
81
+
82
+ if (CGPointEqualToPoint(bounds.origin, origin)) return;
83
+
84
+ bounds.origin = origin;
85
+ self.bounds = bounds;
86
+ }
87
+
88
+ @end
89
+
90
+ @implementation LucentChildren {
91
+ __weak UIView *_host;
92
+ NSString *_name;
93
+ BOOL _takesChildren;
94
+ void (^_placed)(void);
95
+ /// React Native's children of the component, in its order.
96
+ NSMutableArray<UIView *> *_children;
97
+ /// Whether children given to a component taking none were reported.
98
+ BOOL _refused;
99
+ }
100
+
101
+ - (instancetype)initWithHost:(UIView *)host
102
+ name:(NSString *)name
103
+ takesChildren:(BOOL)takesChildren
104
+ placed:(void (^)(void))placed
105
+ {
106
+ if ((self = [super init])) {
107
+ _host = host;
108
+ _name = [name copy];
109
+ _takesChildren = takesChildren;
110
+ _placed = [placed copy];
111
+ _children = [NSMutableArray new];
112
+ }
113
+
114
+ return self;
115
+ }
116
+
117
+ - (void)mount:(UIView *)child index:(NSInteger)index
118
+ {
119
+ NSInteger count = static_cast<NSInteger>(_children.count);
120
+
121
+ if (index < 0 || index > count) {
122
+ report([NSString stringWithFormat:@"%@: React Native mounted a child at %ld of %ld", _name, (long)index, (long)count]);
123
+ index = index < 0 ? 0 : count;
124
+ }
125
+
126
+ [_children insertObject:child atIndex:static_cast<NSUInteger>(index)];
127
+
128
+ if (!_takesChildren && !_refused) {
129
+ _refused = YES;
130
+ report([NSString stringWithFormat:@"%@ takes no React children: React Native gave it %lu, which it does not show",
131
+ _name,
132
+ (unsigned long)_children.count]);
133
+ }
134
+
135
+ [self place:child];
136
+ }
137
+
138
+ - (void)unmount:(UIView *)child index:(NSInteger)index
139
+ {
140
+ NSUInteger at = index >= 0 && static_cast<NSUInteger>(index) < _children.count && _children[index] == child
141
+ ? static_cast<NSUInteger>(index)
142
+ : [_children indexOfObjectIdenticalTo:child];
143
+
144
+ if (at == NSNotFound) {
145
+ report([NSString stringWithFormat:@"%@: React Native unmounted a child it never mounted here", _name]);
146
+ return;
147
+ }
148
+
149
+ [_children removeObjectAtIndex:at];
150
+
151
+ // Out of the slot it was put in (an earlier mount's, if React Native kept it past a recycle).
152
+ if ([child.superview isKindOfClass:[LucentSlotView class]]) [child removeFromSuperview];
153
+ }
154
+
155
+ - (void)startMount
156
+ {
157
+ _slot = _takesChildren ? [[LucentSlotView alloc] initWithHost:_host placed:_placed] : nil;
158
+
159
+ for (UIView *child in _children) [self place:child];
160
+ }
161
+
162
+ - (void)mountedContent:(UIView *)content
163
+ {
164
+ if (_slot && content && ![_slot isDescendantOfView:content])
165
+ report([NSString stringWithFormat:@"%@'s setup did not put its slot in the view it returned: its React children do not show",
166
+ _name]);
167
+ }
168
+
169
+ - (void)contentChanged
170
+ {
171
+ [_slot setNeedsLayout];
172
+ }
173
+
174
+ - (void)endMount
175
+ {
176
+ // React Native unmounts a view's children before it recycles the view: any left stay where they are.
177
+ if (_children.count)
178
+ report([NSString stringWithFormat:@"%@ was recycled holding %lu React children: they stay where React Native left them",
179
+ _name,
180
+ (unsigned long)_children.count]);
181
+
182
+ [_children removeAllObjects];
183
+ _slot = nil;
184
+ _refused = NO;
185
+ }
186
+
187
+ /** Puts `child` in the slot, below the next child there: React Native's order. */
188
+ - (void)place:(UIView *)child
189
+ {
190
+ if (!_slot || child.superview == _slot) return;
191
+
192
+ if (child.superview) {
193
+ report([NSString stringWithFormat:@"%@: a React child is in another view: React Native keeps it there", _name]);
194
+ return;
195
+ }
196
+
197
+ NSUInteger at = [_children indexOfObjectIdenticalTo:child];
198
+
199
+ for (NSUInteger i = at + 1; i < _children.count; i++) {
200
+ if (_children[i].superview == _slot) {
201
+ [_slot insertSubview:child belowSubview:_children[i]];
202
+ return;
203
+ }
204
+ }
205
+
206
+ [_slot addSubview:child];
207
+ }
208
+
209
+ @end
210
+
211
+ #endif
@@ -0,0 +1,62 @@
1
+ // Lucent runtime — what the renderer made for each view, found again by
2
+ // the view's surface and tag.
3
+ //
4
+ // The Android host never gets a view's event emitter from Java: its
5
+ // descriptor records each one as the renderer makes it (HostDescriptor),
6
+ // and the host finds it when the view mounts. A tag alone does not name a
7
+ // view: each JavaScript runtime numbers its views again, so across a reload
8
+ // an old surface's view and a new one's share tags. Surfaces are numbered
9
+ // for the process, so the two together do.
10
+ #pragma once
11
+
12
+ #include <algorithm>
13
+ #include <cstddef>
14
+ #include <cstdint>
15
+ #include <map>
16
+ #include <memory>
17
+ #include <mutex>
18
+ #include <utility>
19
+
20
+ namespace lucent::views {
21
+
22
+ /** Per view, by surface and tag, what the renderer made: held weakly. Any thread. */
23
+ template <class T>
24
+ class ViewRegistry {
25
+ public:
26
+ /// `value` is what the renderer made for view `tag` of `surface`, from now on.
27
+ void record(int32_t surface, int32_t tag, const std::shared_ptr<T>& value) {
28
+ std::lock_guard<std::mutex> g(mutex_);
29
+
30
+ // Views gone since leave expired entries: dropped whenever the registry has doubled.
31
+ if (entries_.size() > limit_) {
32
+ std::erase_if(entries_, [](const auto& entry) { return entry.second.expired(); });
33
+ limit_ = std::max(kFloor, 2 * entries_.size());
34
+ }
35
+
36
+ entries_[{surface, tag}] = value;
37
+ }
38
+
39
+ /// What the renderer made for view `tag` of `surface`, or null (none, or gone).
40
+ std::shared_ptr<T> find(int32_t surface, int32_t tag) const {
41
+ std::lock_guard<std::mutex> g(mutex_);
42
+ auto found = entries_.find({surface, tag});
43
+
44
+ return found == entries_.end() ? nullptr : found->second.lock();
45
+ }
46
+
47
+ /// How many entries it holds, gone ones included until pruned.
48
+ std::size_t size() const {
49
+ std::lock_guard<std::mutex> g(mutex_);
50
+
51
+ return entries_.size();
52
+ }
53
+
54
+ private:
55
+ static constexpr std::size_t kFloor = 256;
56
+
57
+ mutable std::mutex mutex_;
58
+ std::map<std::pair<int32_t, int32_t>, std::weak_ptr<T>> entries_;
59
+ std::size_t limit_ = kFloor;
60
+ };
61
+
62
+ } // namespace lucent::views
@@ -0,0 +1,91 @@
1
+ #include "LucentViewRequests.h"
2
+
3
+ #include <lucent/jsi/host.h>
4
+ #include <lucent/report.h>
5
+
6
+ #include <mutex>
7
+ #include <utility>
8
+
9
+ namespace lucent::views {
10
+
11
+ namespace jsi = facebook::jsi;
12
+
13
+ namespace {
14
+
15
+ struct Connection {
16
+ std::weak_ptr<js::Host> host;
17
+ uint64_t settle = 0;
18
+ };
19
+
20
+ std::mutex& connectionMutex() {
21
+ static std::mutex mutex;
22
+ return mutex;
23
+ }
24
+
25
+ Connection& connection() {
26
+ static Connection current;
27
+ return current;
28
+ }
29
+
30
+ } // namespace
31
+
32
+ Requester Requester::current() {
33
+ std::lock_guard<std::mutex> lock(connectionMutex());
34
+
35
+ return Requester(connection().host, connection().settle);
36
+ }
37
+
38
+ Requester::operator bool() const {
39
+ auto host = host_.lock();
40
+
41
+ return host && host->alive();
42
+ }
43
+
44
+ void Requester::settle(double id, std::function<void(jsi::Runtime&, jsi::Function&)> call) const {
45
+ auto host = host_.lock();
46
+
47
+ if (!host) return;
48
+
49
+ host->postToJs([weak = host_, settle = settle_, id, call = std::move(call)](jsi::Runtime& runtime) {
50
+ auto host = weak.lock();
51
+ jsi::Function* fn = host ? host->retained(settle) : nullptr;
52
+
53
+ if (!fn) return;
54
+
55
+ try {
56
+ call(runtime, *fn);
57
+ } catch (const std::exception& e) {
58
+ logError((std::string("[lucent] answering view request ") + std::to_string(id) + ": " + e.what()).c_str());
59
+ }
60
+ });
61
+ }
62
+
63
+ void Requester::resolve(double id, AnswerValue value) const {
64
+ settle(id, [id, value = std::move(value)](jsi::Runtime& runtime, jsi::Function& fn) {
65
+ fn.call(runtime, jsi::Value(id), jsi::Value::null(), value(runtime));
66
+ });
67
+ }
68
+
69
+ void Requester::reject(double id, std::string message) const {
70
+ settle(id, [id, message = std::move(message)](jsi::Runtime& runtime, jsi::Function& fn) {
71
+ fn.call(runtime, jsi::Value(id), jsi::String::createFromUtf8(runtime, message));
72
+ });
73
+ }
74
+
75
+ void connectRequests(const std::shared_ptr<js::Host>& host, jsi::Runtime& runtime, jsi::Function settle) {
76
+ if (!host->alive()) throw jsi::JSError(runtime, "Lucent: this JavaScript runtime's Lucent host is torn down");
77
+
78
+ uint64_t id = host->retain(runtime, std::move(settle));
79
+ Connection previous;
80
+
81
+ {
82
+ std::lock_guard<std::mutex> lock(connectionMutex());
83
+
84
+ previous = std::exchange(connection(), Connection{host, id});
85
+ }
86
+
87
+ // The same runtime connecting again (a second views runtime): its old function goes.
88
+ if (auto old = previous.host.lock(); old == host && previous.settle != id) host->release(previous.settle);
89
+ }
90
+
91
+ } // namespace lucent::views
@@ -0,0 +1,104 @@
1
+ // Lucent runtime — answers to the commands of Lucent components that return
2
+ // a value: requests, settled in the JavaScript runtime that sent them.
3
+ //
4
+ // A component's proxy numbers each request and hands the views runtime's
5
+ // settle function to the Lucent module once (its __lucentViewRequests
6
+ // property, connectRequests below). A host view captures the Requester
7
+ // current when a command arrives, and answers through it from any thread.
8
+ // The answer runs on that runtime's JavaScript thread, or not at all once
9
+ // that runtime is torn down (a reload): request ids start again in each
10
+ // runtime, so an answer never reaches another one.
11
+ #pragma once
12
+
13
+ #include <jsi/jsi.h>
14
+
15
+ #include <cstdint>
16
+ #include <functional>
17
+ #include <memory>
18
+ #include <optional>
19
+ #include <string>
20
+ #include <utility>
21
+
22
+ namespace lucent::js {
23
+ class Host;
24
+ }
25
+
26
+ namespace lucent::views {
27
+
28
+ /// A request's answer, built on the JavaScript thread.
29
+ using AnswerValue = std::function<facebook::jsi::Value(facebook::jsi::Runtime&)>;
30
+
31
+ /** The runtime a request came from. Copyable; any thread. */
32
+ class Requester {
33
+ public:
34
+ Requester() = default;
35
+
36
+ /// The runtime connected now: a command's, when it arrives. Empty when no
37
+ /// runtime has connected (its answers are dropped).
38
+ static Requester current();
39
+
40
+ /// Settles request `id` with `value`. Dropped when the runtime is gone.
41
+ void resolve(double id, AnswerValue value) const;
42
+
43
+ /// Rejects request `id` with an Error whose message is `message`.
44
+ void reject(double id, std::string message) const;
45
+
46
+ /// Whether its runtime is still there (not torn down).
47
+ explicit operator bool() const;
48
+
49
+ private:
50
+ Requester(std::weak_ptr<js::Host> host, uint64_t settle) : host_(std::move(host)), settle_(settle) {}
51
+
52
+ void settle(double id, std::function<void(facebook::jsi::Runtime&, facebook::jsi::Function&)> call) const;
53
+
54
+ std::weak_ptr<js::Host> host_;
55
+ /// The settle function, retained by the host.
56
+ uint64_t settle_ = 0;
57
+ };
58
+
59
+ /// Makes `host`'s runtime the one requests come from, settled through
60
+ /// `settle(id, error, value)`. On the JavaScript thread.
61
+ void connectRequests(const std::shared_ptr<js::Host>& host, facebook::jsi::Runtime& runtime, facebook::jsi::Function settle);
62
+
63
+ /// The name the Lucent module gives the function JavaScript connects with.
64
+ inline constexpr const char* kRequestsName = "__lucentViewRequests";
65
+
66
+ /**
67
+ * A request command's answer. The host settles JavaScript's promise with it
68
+ * on the JS thread (`settle(request, error, value)`): rejected with an
69
+ * Error of `error`'s message when set, else resolved with `value()`.
70
+ */
71
+ struct Answer {
72
+ double request{0};
73
+ std::optional<std::string> error{};
74
+ AnswerValue value{};
75
+ };
76
+
77
+ /**
78
+ * Where a mount sends its answers. A mount answers each request once:
79
+ * with its result or error, or, still unanswered when the mount ends,
80
+ * with a rejection then.
81
+ */
82
+ using Respond = std::function<void(Answer)>;
83
+
84
+ /**
85
+ * A mount's answers, to `requester`'s runtime while `host` (the platform's
86
+ * HostView: `current()`, whether its view still holds that mount) says so.
87
+ * Once the view holds another mount, or none (recycled, remounted: a view
88
+ * or a tag reused), an answer is dropped with what it carries: JavaScript
89
+ * rejected the request when React unmounted its view. On the thread the
90
+ * host reads its view on (the main thread).
91
+ */
92
+ template <class Host>
93
+ Respond answerTo(Host host, Requester requester) {
94
+ return [host = std::move(host), requester = std::move(requester)](Answer answer) {
95
+ if (!host.current()) return;
96
+
97
+ if (answer.error)
98
+ requester.reject(answer.request, std::move(*answer.error));
99
+ else
100
+ requester.resolve(answer.request, std::move(answer.value));
101
+ };
102
+ }
103
+
104
+ } // namespace lucent::views