@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,139 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ //#region src/cli/trace.ts
4
+ const CATEGORIES = [
5
+ "entry",
6
+ "lock",
7
+ "queue",
8
+ "run",
9
+ "native",
10
+ "completion",
11
+ "compute",
12
+ "copy",
13
+ "alloc",
14
+ "build"
15
+ ];
16
+ const BUILD_PID = 1;
17
+ function buildEvents(record) {
18
+ const startedAt = record.startedAt ?? {};
19
+ return record.nodes.map((node) => {
20
+ const args = {
21
+ kind: node.kind,
22
+ status: node.status,
23
+ ...node.detail ? { detail: node.detail } : {}
24
+ };
25
+ const ms = record.timings[node.id];
26
+ if (ms === void 0 || startedAt[node.id] === void 0) return {
27
+ name: node.id,
28
+ cat: "build",
29
+ ph: "i",
30
+ s: "p",
31
+ ts: 0,
32
+ pid: BUILD_PID,
33
+ tid: 1,
34
+ args
35
+ };
36
+ return {
37
+ name: node.id,
38
+ cat: "build",
39
+ ph: "X",
40
+ ts: startedAt[node.id] * 1e3,
41
+ dur: ms * 1e3,
42
+ pid: BUILD_PID,
43
+ tid: 1,
44
+ args
45
+ };
46
+ });
47
+ }
48
+ const processName = (pid, name) => ({
49
+ name: "process_name",
50
+ ph: "M",
51
+ pid,
52
+ args: { name }
53
+ });
54
+ function mergeTraces(record, runtimes) {
55
+ const events = [];
56
+ if (record) events.push(processName(BUILD_PID, "lucent build"), ...buildEvents(record));
57
+ runtimes.forEach(({ name, trace }, i) => {
58
+ const pid = 2 + i;
59
+ events.push(processName(pid, name));
60
+ for (const e of trace.traceEvents) events.push({
61
+ ...e,
62
+ pid
63
+ });
64
+ });
65
+ return {
66
+ traceEvents: events,
67
+ displayTimeUnit: "ms"
68
+ };
69
+ }
70
+ function summarize(trace) {
71
+ const totals = /* @__PURE__ */ new Map();
72
+ for (const e of trace.traceEvents) {
73
+ if (e.ph !== "X" || !e.cat) continue;
74
+ const total = totals.get(e.cat) ?? {
75
+ category: e.cat,
76
+ ms: 0,
77
+ count: 0
78
+ };
79
+ total.ms += (e.dur ?? 0) / 1e3;
80
+ total.count++;
81
+ const bytes = e.cat === "copy" ? Number(e.args?.value ?? 0) : 0;
82
+ if (bytes > 0) total.bytes = (total.bytes ?? 0) + bytes;
83
+ totals.set(e.cat, total);
84
+ }
85
+ const order = (c) => {
86
+ const i = CATEGORIES.indexOf(c);
87
+ return i < 0 ? CATEGORIES.length : i;
88
+ };
89
+ return { categories: [...totals.values()].sort((a, b) => order(a.category) - order(b.category)).map((t) => ({
90
+ ...t,
91
+ ms: Math.round(t.ms * 1e3) / 1e3
92
+ })) };
93
+ }
94
+ function formatMs(ms) {
95
+ if (ms >= 1e3) return `${(ms / 1e3).toFixed(1)} s`;
96
+ return `${Number(ms.toPrecision(3))} ms`;
97
+ }
98
+ //#endregion
99
+ //#region src/cli/commands/trace.ts
100
+ const HOW = "run the app or its tests with LUCENT_TRACE=<file>.json and pass --runtime <file>; on a device, LUCENT_TRACE=platform (iOS, Instruments' os_signpost) or `adb shell setprop debug.lucent.trace 1` (Android, Perfetto's atrace)";
101
+ function run({ root, flags, out }) {
102
+ const t = out.theme;
103
+ const recordFile = path.join(root, ".lucent", "build-record.json");
104
+ const record = fs.existsSync(recordFile) ? JSON.parse(fs.readFileSync(recordFile, "utf8")) : void 0;
105
+ const runtimeFiles = typeof flags.runtime === "string" ? flags.runtime.split(",").map((f) => path.resolve(root, f.trim())) : [];
106
+ if (!record && !runtimeFiles.length) {
107
+ out.error(`${t.error(t.symbols.fail)} no build record in ${path.join(root, ".lucent")} and no --runtime trace: build first, or ${HOW}`);
108
+ return 1;
109
+ }
110
+ const runtimes = [];
111
+ for (const file of runtimeFiles) {
112
+ if (!fs.existsSync(file)) {
113
+ out.error(`${t.error(t.symbols.fail)} no runtime trace at ${file}: ${HOW}`);
114
+ return 1;
115
+ }
116
+ runtimes.push({
117
+ name: path.basename(file),
118
+ trace: JSON.parse(fs.readFileSync(file, "utf8"))
119
+ });
120
+ }
121
+ const merged = mergeTraces(record, runtimes);
122
+ const target = typeof flags.out === "string" ? path.resolve(root, flags.out) : path.join(root, ".lucent", "trace.json");
123
+ fs.mkdirSync(path.dirname(target), { recursive: true });
124
+ fs.writeFileSync(target, `${JSON.stringify(merged)}\n`);
125
+ const shown = path.relative(root, target) || target;
126
+ out.print(`${t.success(t.symbols.ok)} wrote ${shown} ${t.dim("open it in ui.perfetto.dev or chrome://tracing")}`);
127
+ if (record) {
128
+ const steps = Object.entries(record.timings).sort(([a], [b]) => (record.startedAt?.[a] ?? 0) - (record.startedAt?.[b] ?? 0)).map(([id, ms]) => `${id} ${formatMs(ms)}`);
129
+ out.print(` ${"build".padEnd(16)} ${steps.join(" · ") || t.dim("nothing timed")}`);
130
+ }
131
+ for (const { name, trace } of runtimes) {
132
+ const causes = summarize(trace).categories.map((c) => `${c.category} ${formatMs(c.ms)}${c.bytes ? ` (${(c.bytes / 1e6).toFixed(1)} MB)` : ""}`);
133
+ const dropped = Number(trace.otherData?.dropped ?? 0);
134
+ out.print(` ${name.padEnd(16)} ${causes.join(" · ") || t.dim("no spans")}${dropped ? t.dim(` (${dropped} events dropped)`) : ""}`);
135
+ }
136
+ return 0;
137
+ }
138
+ //#endregion
139
+ export { run };
package/lib/core.cjs CHANGED
@@ -103,4 +103,371 @@ function now() {
103
103
  return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
104
104
  }
105
105
 
106
- module.exports = { delay, error, errorCode, utf8Encode, utf8Decode, now };
106
+ // An error nobody can catch (a cleanup's): the host's uncaught error
107
+ // handler gets it, as native code's goes to the platform log.
108
+ function reportUncaught(e) {
109
+ setTimeout(() => {
110
+ throw e;
111
+ }, 0);
112
+ }
113
+
114
+ // One promise that settles once, at the first of what `start`'s callbacks
115
+ // report and the signal aborting; the cleanup `start` returns runs once, as
116
+ // soon as it settles.
117
+ function compose(signal, start) {
118
+ return new Promise((resolve, reject) => {
119
+ if (signal && signal.aborted) return void reject(signal.reason);
120
+
121
+ let settled = false;
122
+ let registered = false;
123
+ let cleanup;
124
+
125
+ const runCleanup = () => {
126
+ const f = cleanup;
127
+ cleanup = undefined;
128
+ if (typeof f !== "function") return;
129
+ try {
130
+ f();
131
+ } catch (e) {
132
+ reportUncaught(e);
133
+ }
134
+ };
135
+
136
+ const settle = (how, value) => {
137
+ if (settled) return;
138
+ settled = true;
139
+ if (signal) signal.removeEventListener("abort", onAbort);
140
+ how(value);
141
+ if (registered) runCleanup();
142
+ };
143
+
144
+ const onAbort = () => settle(reject, signal.reason);
145
+ if (signal) signal.addEventListener("abort", onAbort);
146
+
147
+ try {
148
+ cleanup = start({
149
+ open: () => !settled,
150
+ resolve: (value) => settle(resolve, value),
151
+ reject: (reason) => settle(reject, reason),
152
+ });
153
+ } catch (e) {
154
+ settle(reject, e);
155
+ }
156
+
157
+ registered = true;
158
+ if (settled) runCleanup();
159
+ });
160
+ }
161
+
162
+ function fromCallback(register, signal) {
163
+ return compose(signal, (c) =>
164
+ register(
165
+ (value) => void c.resolve(value),
166
+ (reason) => void c.reject(reason),
167
+ ),
168
+ );
169
+ }
170
+
171
+ function subscribe(register, onValue, signal) {
172
+ return compose(signal, (c) => {
173
+ const next = (value) => {
174
+ if (!c.open()) return;
175
+ try {
176
+ onValue(value);
177
+ } catch (e) {
178
+ c.reject(e);
179
+ }
180
+ };
181
+ return register(
182
+ next,
183
+ () => void c.resolve(undefined),
184
+ (error) => void c.reject(error),
185
+ );
186
+ });
187
+ }
188
+
189
+ // --- native buffers ---------------------------------------------------------
190
+
191
+ const MAX_SIZE = Number.MAX_SAFE_INTEGER;
192
+
193
+ const bufferCounts = { allocated: 0, adopted: 0, transfers: 0, copies: 0, bytesCopied: 0 };
194
+
195
+ function countCopy(bytes) {
196
+ bufferCounts.copies++;
197
+ bufferCounts.bytesCopied += bytes;
198
+ }
199
+
200
+ // Each buffer's state, out of reach of the code using it: its bytes, open,
201
+ // transferred or closed, and its borrows.
202
+ const buffers = new WeakMap();
203
+ const creating = Symbol("NativeBuffer");
204
+
205
+ function invalidState(why) {
206
+ const e = new Error(`NativeBuffer ${why}`);
207
+ e.name = "InvalidStateError";
208
+ return e;
209
+ }
210
+
211
+ function stateOf(buffer) {
212
+ const s = buffers.get(buffer);
213
+ if (!s) throw new TypeError("not a NativeBuffer");
214
+ return s;
215
+ }
216
+
217
+ /** Refuses a use unless the buffer is open and, for `write`, unborrowed (reads need no writer). */
218
+ function borrowable(s, write) {
219
+ if (s.state !== "open")
220
+ throw invalidState(s.state === "closed" ? "is closed" : "was transferred");
221
+ if (s.writing || (write && s.readers > 0)) throw invalidState("is borrowed");
222
+ }
223
+
224
+ /**
225
+ * Bytes native code owns, as the native runtime keeps them: borrowed for
226
+ * one call, moved by transfer() and compute, copied only by from() and
227
+ * toUint8Array(). Here the bytes a borrow lends are the buffer's own.
228
+ */
229
+ class NativeBuffer {
230
+ constructor(token, bytes) {
231
+ if (token !== creating) throw new TypeError("Illegal constructor");
232
+ buffers.set(this, { bytes, state: "open", readers: 0, writing: false });
233
+ }
234
+
235
+ static allocate(size) {
236
+ if (!(size >= 0) || Math.trunc(size) !== size || size > MAX_SIZE)
237
+ throw new RangeError("Invalid buffer size");
238
+
239
+ bufferCounts.allocated++;
240
+ return new NativeBuffer(creating, new Uint8Array(size));
241
+ }
242
+
243
+ static from(bytes) {
244
+ const buffer = NativeBuffer.allocate(bytes.length);
245
+
246
+ stateOf(buffer).bytes.set(bytes);
247
+ countCopy(bytes.length);
248
+ return buffer;
249
+ }
250
+
251
+ static stats() {
252
+ return { ...bufferCounts };
253
+ }
254
+
255
+ get byteLength() {
256
+ const s = stateOf(this);
257
+ return s.state === "open" ? s.bytes.length : 0;
258
+ }
259
+
260
+ withRead(read) {
261
+ const s = stateOf(this);
262
+
263
+ borrowable(s, false);
264
+ s.readers++;
265
+ try {
266
+ return read(s.bytes);
267
+ } finally {
268
+ s.readers--;
269
+ }
270
+ }
271
+
272
+ withWrite(write) {
273
+ const s = stateOf(this);
274
+
275
+ borrowable(s, true);
276
+ s.writing = true;
277
+ try {
278
+ return write(s.bytes);
279
+ } finally {
280
+ s.writing = false;
281
+ }
282
+ }
283
+
284
+ toUint8Array() {
285
+ return this.withRead((bytes) => {
286
+ countCopy(bytes.length);
287
+ return bytes.slice();
288
+ });
289
+ }
290
+
291
+ transfer() {
292
+ const moved = beginTransfer(this);
293
+
294
+ commitTransfer(this);
295
+ return moved;
296
+ }
297
+
298
+ close() {
299
+ const s = stateOf(this);
300
+
301
+ if (s.state !== "open") return;
302
+ borrowable(s, true);
303
+
304
+ s.state = "closed";
305
+ s.bytes = new Uint8Array(0);
306
+ }
307
+
308
+ [Symbol.dispose]() {
309
+ this.close();
310
+ }
311
+ }
312
+
313
+ // A move in two steps, as a task's input is copied: the new buffer takes
314
+ // the bytes at once, and the old one gives them up for good only once the
315
+ // whole input has been copied.
316
+ function beginTransfer(buffer) {
317
+ const s = stateOf(buffer);
318
+
319
+ borrowable(s, true);
320
+ s.state = "transferring";
321
+ return new NativeBuffer(creating, s.bytes);
322
+ }
323
+
324
+ function commitTransfer(buffer) {
325
+ const s = stateOf(buffer);
326
+
327
+ s.state = "transferred";
328
+ s.bytes = new Uint8Array(0);
329
+ bufferCounts.transfers++;
330
+ }
331
+
332
+ function abortTransfer(buffer) {
333
+ stateOf(buffer).state = "open";
334
+ }
335
+
336
+ /** What a copy refuses to copy: a DataCloneError, as structuredClone throws. */
337
+ function dataCloneError(message) {
338
+ const e = new Error(message);
339
+ e.name = "DataCloneError";
340
+ return e;
341
+ }
342
+
343
+ /**
344
+ * A compute task's input, copied as the native runtime copies it: every
345
+ * object, array, map, set, date and byte buffer reached is copied once
346
+ * (aliases stay aliases, cycles survive), objects keep their class, and
347
+ * views of one buffer stay views of one copied buffer. A NativeBuffer
348
+ * moves instead, once: `moved` collects the moves to commit or undo.
349
+ */
350
+ function transportCopy(value, copies = new Map(), moved = []) {
351
+ if (value === null || (typeof value !== "object" && typeof value !== "function")) return value;
352
+ if (typeof value === "function")
353
+ throw dataCloneError("A function cannot be copied to a compute task");
354
+
355
+ const known = copies.get(value);
356
+ if (known) return known;
357
+
358
+ if (value instanceof NativeBuffer) {
359
+ const successor = beginTransfer(value);
360
+ moved.push(value);
361
+ copies.set(value, successor);
362
+ return successor;
363
+ }
364
+
365
+ if (value instanceof Uint8Array) {
366
+ let buffer = copies.get(value.buffer);
367
+ if (!buffer) {
368
+ buffer = value.buffer.slice(0);
369
+ copies.set(value.buffer, buffer);
370
+ }
371
+ const view = new Uint8Array(buffer, value.byteOffset, value.length);
372
+ copies.set(value, view);
373
+ return view;
374
+ }
375
+
376
+ if (value instanceof Date) {
377
+ const date = new Date(value.getTime());
378
+ copies.set(value, date);
379
+ return date;
380
+ }
381
+
382
+ if (value instanceof Map) {
383
+ const map = new Map();
384
+ copies.set(value, map);
385
+ for (const [k, v] of value)
386
+ map.set(transportCopy(k, copies, moved), transportCopy(v, copies, moved));
387
+ return map;
388
+ }
389
+
390
+ if (value instanceof Set) {
391
+ const set = new Set();
392
+ copies.set(value, set);
393
+ for (const v of value) set.add(transportCopy(v, copies, moved));
394
+ return set;
395
+ }
396
+
397
+ if (Array.isArray(value)) {
398
+ const array = [];
399
+ copies.set(value, array);
400
+ for (const v of value) array.push(transportCopy(v, copies, moved));
401
+ return array;
402
+ }
403
+
404
+ if (value instanceof Promise || value instanceof AbortSignal || value instanceof AbortController)
405
+ throw dataCloneError(`A ${value.constructor.name} cannot be copied to a compute task`);
406
+
407
+ const object = Object.create(Object.getPrototypeOf(value));
408
+ copies.set(value, object);
409
+ // Defined, not assigned: an own "__proto__" key stays a key.
410
+ for (const key of Object.keys(value))
411
+ Object.defineProperty(object, key, {
412
+ value: transportCopy(value[key], copies, moved),
413
+ writable: true,
414
+ enumerable: true,
415
+ configurable: true,
416
+ });
417
+ return object;
418
+ }
419
+
420
+ /**
421
+ * Runs `task(input)` as a compute task would, on this thread: the input is
422
+ * copied now, the task runs in a later turn, and aborting the signal first
423
+ * rejects with its reason (the task then never runs).
424
+ */
425
+ function compute(task, input, options) {
426
+ const signal = options && options.signal;
427
+
428
+ return new Promise((resolve, reject) => {
429
+ let copy;
430
+ const moved = [];
431
+ try {
432
+ copy = transportCopy(input, new Map(), moved);
433
+ } catch (e) {
434
+ moved.forEach(abortTransfer);
435
+ return void reject(e);
436
+ }
437
+ moved.forEach(commitTransfer);
438
+
439
+ if (signal && signal.aborted) return void reject(signal.reason);
440
+
441
+ let settled = false;
442
+ const onAbort = () => {
443
+ if (settled) return;
444
+ settled = true;
445
+ clearTimeout(timer);
446
+ reject(signal.reason);
447
+ };
448
+ const timer = setTimeout(() => {
449
+ if (settled) return;
450
+ settled = true;
451
+ if (signal) signal.removeEventListener("abort", onAbort);
452
+ try {
453
+ resolve(task(copy));
454
+ } catch (e) {
455
+ reject(e);
456
+ }
457
+ }, 0);
458
+ if (signal) signal.addEventListener("abort", onAbort);
459
+ });
460
+ }
461
+
462
+ module.exports = {
463
+ delay,
464
+ error,
465
+ errorCode,
466
+ utf8Encode,
467
+ utf8Decode,
468
+ now,
469
+ fromCallback,
470
+ subscribe,
471
+ compute,
472
+ NativeBuffer,
473
+ };
@@ -1,8 +1,61 @@
1
1
  // lucent:android — Android helpers for platform code.
2
- import type { Context } from "lucent:android/android.content";
2
+ import type { Activity, Instrumentation_ActivityResult } from "lucent:android/android.app";
3
+ import type { Context, Intent } from "lucent:android/android.content";
3
4
 
4
5
  /** The application Context. */
5
6
  export declare function appContext(): Context;
6
7
 
7
8
  /** Whether the device runs at least API level `api` (`Build.VERSION.SDK_INT >= api`). */
8
9
  export declare function available(platform: "android", api: number): boolean;
10
+
11
+ /**
12
+ * The Activity in front: the one last created, started or resumed and not
13
+ * destroyed, or null (before the first, between two, after the last).
14
+ * Lucent holds Activities weakly: use this one now, on the main thread
15
+ * (inside `main()`), and ask again later rather than keeping it.
16
+ */
17
+ export declare function currentActivity(): Activity | null;
18
+
19
+ /**
20
+ * Starts `intent` from the Activity in front and resolves with its result
21
+ * (`getResultCode()`, `getResultData()`). The request survives the app's
22
+ * Activity being recreated meanwhile. Rejects with `ERR_NO_ACTIVITY` without
23
+ * an Activity, `ERR_ACTIVITY_NOT_FOUND` when no app handles the intent, and
24
+ * the signal's reason if it aborts first: the answer, if it still comes, is
25
+ * dropped (Lucent closes what it started when Android lets it).
26
+ */
27
+ export declare function startActivityForResult(
28
+ intent: Intent,
29
+ signal?: AbortSignal,
30
+ ): Promise<Instrumentation_ActivityResult>;
31
+
32
+ /**
33
+ * Asks for runtime permissions (`"android.permission.CAMERA"`), and
34
+ * resolves with whether each was granted, in order. Requests wait for the
35
+ * one before them, as Android asks one at a time. Rejects like
36
+ * `startActivityForResult`.
37
+ */
38
+ export declare function requestPermissions(
39
+ permissions: string[],
40
+ signal?: AbortSignal,
41
+ ): Promise<boolean[]>;
42
+
43
+ /** The lifecycle events of the app's Activities, and a new intent sent to one. */
44
+ export type ActivityEvent =
45
+ | "created"
46
+ | "started"
47
+ | "resumed"
48
+ | "paused"
49
+ | "stopped"
50
+ | "destroyed"
51
+ | "newIntent";
52
+
53
+ /**
54
+ * Runs `handler` after each `event` of the app's Activities, with the
55
+ * Activity (and the intent, for "newIntent"), in a turn of the calling
56
+ * context. Returns the function that stops it.
57
+ */
58
+ export declare function onActivityEvent(
59
+ event: ActivityEvent,
60
+ handler: (activity: Activity, intent: Intent | null) => void,
61
+ ): () => void;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * lucent:compose — Jetpack Compose, for a component's content on Android.
3
+ *
4
+ * Experimental and internal: this module resolves only when the
5
+ * LUCENT_VIEWS environment variable is `fabric`, and changes without notice
6
+ * until Lucent's views are public.
7
+ *
8
+ * A component returns its content: JSX, compiled to Kotlin. A composable
9
+ * showing UI is an element (`<Box modifier={…}>`), whose props are
10
+ * Kotlin's named arguments and whose children are its trailing content
11
+ * lambda, or a function of the lambda's scope (`{(row) => …}`); a lazy
12
+ * list's items are elements of its scope (`{(list) => <list.items
13
+ * items={…} key={…}>…</list.items>}`). The component's statements that
14
+ * compose (`animateDpAsState(…)`, `remember(…)`, `LaunchedEffect(…)`)
15
+ * run in the content, in order, before what the JSX shows; the rest of
16
+ * its code is its setup, which runs once and reads none of their values.
17
+ * The content reads the setup's props, signals and functions: a number,
18
+ * boolean or string the setup computes is Compose state, and a setup
19
+ * function the content calls from a callback runs as Lucent code. Other
20
+ * functions take Kotlin's parameters in order, an options object holding
21
+ * the defaulted ones where Kotlin's order cannot (`spring({ stiffness })`);
22
+ * modifiers, animations and effects are Compose's.
23
+ *
24
+ * A value Compose pairs with the callback of its changes (`value` and
25
+ * `onValueChange`) takes a setup signal bound to both instead,
26
+ * `bind(signal)` from lucent:ui.
27
+ *
28
+ * Below lucent:compose's own declarations come Compose's, under their own
29
+ * names, made from the bindings of the Compose release Lucent builds with.
30
+ * Float, Int, Long, Short and Byte are Kotlin's number types: a number the
31
+ * body passes to one is converted.
32
+ *
33
+ * @experimental
34
+ */
35
+ import type { ViewGroup } from "lucent:android/android.view";
36
+ import type { Bound } from "lucent:ui";
37
+
38
+ /** Kotlin's Float: a number, converted where the body passes it. */
39
+ export type Float = number & { readonly "lucent:compose.Float"?: never };
40
+ /** Kotlin's Int. */
41
+ export type Int = number & { readonly "lucent:compose.Int"?: never };
42
+ /** Kotlin's Long. */
43
+ export type Long = number & { readonly "lucent:compose.Long"?: never };
44
+ /** Kotlin's Short. */
45
+ export type Short = number & { readonly "lucent:compose.Short"?: never };
46
+ /** Kotlin's Byte. */
47
+ export type Byte = number & { readonly "lucent:compose.Byte"?: never };
48
+
49
+ /**
50
+ * What an element shows (`<Box …/>`): an item of content, the value of
51
+ * JSX, never a value setup holds.
52
+ */
53
+ export interface Composed {
54
+ readonly "lucent:compose.Composed": true;
55
+ }
56
+
57
+ /** Content: what composables show, in order, or nothing where a condition is false. */
58
+ export type Shown = Composed | false | null | undefined | readonly Shown[];
59
+
60
+ /** A @Composable content lambda given as a prop: its statements compose, and it returns what it shows. */
61
+ export type Content = () => Shown;
62
+
63
+ /**
64
+ * A content lambda composed in a receiver scope (a Row's RowScope), given
65
+ * as a prop: its first parameter is the scope, whose methods and Modifier
66
+ * run in it.
67
+ */
68
+ export type ScopedContent<S> = (scope: S) => Shown;
69
+
70
+ /**
71
+ * An element's children composed in a receiver scope: what they show, or
72
+ * a function of the scope showing it, for children that use the scope
73
+ * (`{(row) => <Text modifier={row.Modifier.weight(1)} … />}`).
74
+ */
75
+ export type ScopedChildren<S> = Shown | ((scope: S) => Shown);
76
+
77
+ /**
78
+ * How TypeScript types an Android file's JSX: an element is a composable,
79
+ * a function of its props whose children are its `children` prop.
80
+ */
81
+ export declare namespace JSX {
82
+ type Element = Composed;
83
+
84
+ interface ElementChildrenAttribute {
85
+ children: {};
86
+ }
87
+
88
+ interface IntrinsicElements {}
89
+ }
90
+
91
+ /** The Android view hosting a composition: the view a component returning JSX makes. */
92
+ export declare class ComposeView extends ViewGroup {
93
+ private constructor();
94
+ }
Binary file