@delali/narsil-react-native 0.0.0-stage → 0.3.1-next.151

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 (186) hide show
  1. package/LICENSE +201 -0
  2. package/NarsilReactNative.podspec +57 -0
  3. package/README.md +46 -2
  4. package/android/CMakeLists.txt +48 -0
  5. package/android/build.gradle +151 -0
  6. package/android/gradle.properties +5 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/cpp/AndroidEnvironment.cpp +44 -0
  9. package/android/src/main/cpp/AndroidEnvironment.hpp +11 -0
  10. package/android/src/main/cpp/cpp-adapter.cpp +11 -0
  11. package/android/src/main/java/com/margelo/nitro/narsil/NarsilAndroidEnvironment.kt +29 -0
  12. package/android/src/main/java/com/margelo/nitro/narsil/NarsilReactNativePackage.kt +22 -0
  13. package/app.plugin.js +25 -0
  14. package/cpp/AttachedField.cpp +234 -0
  15. package/cpp/AttachedField.hpp +61 -0
  16. package/cpp/Compression.cpp +113 -0
  17. package/cpp/Compression.hpp +16 -0
  18. package/cpp/GraphHeldAlone.cpp +29 -0
  19. package/cpp/GraphHeldAlone.hpp +25 -0
  20. package/cpp/HostMemory.cpp +123 -0
  21. package/cpp/HostMemory.hpp +53 -0
  22. package/cpp/HybridNarsilCoreImpl.cpp +390 -0
  23. package/cpp/HybridNarsilCoreImpl.hpp +68 -0
  24. package/cpp/HybridNarsilFilesImpl.cpp +245 -0
  25. package/cpp/HybridNarsilFilesImpl.hpp +34 -0
  26. package/cpp/HybridNarsilHostImpl.cpp +126 -0
  27. package/cpp/HybridNarsilHostImpl.hpp +42 -0
  28. package/cpp/NativeAtomics.cpp +110 -0
  29. package/cpp/NativeAtomics.hpp +11 -0
  30. package/cpp/ProcessInfo.cpp +83 -0
  31. package/cpp/ProcessInfo.hpp +11 -0
  32. package/cpp/ThreadPriority.cpp +77 -0
  33. package/cpp/ThreadPriority.hpp +8 -0
  34. package/cpp/WorkletRuntimeDispatcher.cpp +61 -0
  35. package/cpp/WorkletRuntimeDispatcher.hpp +9 -0
  36. package/cpp/core/include/narsil_core.h +208 -0
  37. package/cpp/core/src/connect.c +213 -0
  38. package/cpp/core/src/distance.c +103 -0
  39. package/cpp/core/src/files.c +99 -0
  40. package/cpp/core/src/heap.c +84 -0
  41. package/cpp/core/src/internal.h +284 -0
  42. package/cpp/core/src/kernels_code.c +209 -0
  43. package/cpp/core/src/kernels_float.c +197 -0
  44. package/cpp/core/src/kernels_pair.c +133 -0
  45. package/cpp/core/src/last_links.c +145 -0
  46. package/cpp/core/src/links.c +195 -0
  47. package/cpp/core/src/locks.c +168 -0
  48. package/cpp/core/src/nearest_links.c +147 -0
  49. package/cpp/core/src/place.c +215 -0
  50. package/cpp/core/src/quantize.c +226 -0
  51. package/cpp/core/src/records.c +97 -0
  52. package/cpp/core/src/remove.c +205 -0
  53. package/cpp/core/src/vectors.c +139 -0
  54. package/cpp/core/src/walk.c +231 -0
  55. package/cpp/core/src/workspace.c +167 -0
  56. package/ios/PrivacyInfo.xcprivacy +23 -0
  57. package/lib/client.d.ts +19 -0
  58. package/lib/client.js +41 -0
  59. package/lib/constants.d.ts +9 -0
  60. package/lib/constants.js +9 -0
  61. package/lib/engine/byte-files.d.ts +5 -0
  62. package/lib/engine/byte-files.js +97 -0
  63. package/lib/engine/core.d.ts +3 -0
  64. package/lib/engine/core.js +113 -0
  65. package/lib/engine/files.d.ts +3 -0
  66. package/lib/engine/files.js +188 -0
  67. package/lib/engine/host.d.ts +13 -0
  68. package/lib/engine/host.js +102 -0
  69. package/lib/engine/install.d.ts +4 -0
  70. package/lib/engine/install.js +29 -0
  71. package/lib/engine/native-error.d.ts +1 -0
  72. package/lib/engine/native-error.js +12 -0
  73. package/lib/engine/stand-ins/atomics.d.ts +5 -0
  74. package/lib/engine/stand-ins/atomics.js +236 -0
  75. package/lib/engine/stand-ins/crypto.d.ts +2 -0
  76. package/lib/engine/stand-ins/crypto.js +65 -0
  77. package/lib/engine/stand-ins/process.d.ts +2 -0
  78. package/lib/engine/stand-ins/process.js +22 -0
  79. package/lib/engine/stand-ins/streams.d.ts +1 -0
  80. package/lib/engine/stand-ins/streams.js +31 -0
  81. package/lib/engine/stand-ins/structured-clone.d.ts +1 -0
  82. package/lib/engine/stand-ins/structured-clone.js +121 -0
  83. package/lib/engine/streamed.d.ts +6 -0
  84. package/lib/engine/streamed.js +29 -0
  85. package/lib/engine/transfer.d.ts +3 -0
  86. package/lib/engine/transfer.js +77 -0
  87. package/lib/errors.d.ts +25 -0
  88. package/lib/errors.js +59 -0
  89. package/lib/index.d.ts +3 -0
  90. package/lib/index.js +2 -0
  91. package/lib/methods.d.ts +5 -0
  92. package/lib/methods.js +47 -0
  93. package/lib/native-objects.d.ts +2 -0
  94. package/lib/native-objects.js +24 -0
  95. package/lib/specs/core.nitro.d.ts +79 -0
  96. package/lib/specs/core.nitro.js +1 -0
  97. package/lib/specs/files.nitro.d.ts +36 -0
  98. package/lib/specs/files.nitro.js +1 -0
  99. package/lib/specs/host.nitro.d.ts +24 -0
  100. package/lib/specs/host.nitro.js +1 -0
  101. package/lib/transferred.d.ts +15 -0
  102. package/lib/transferred.js +6 -0
  103. package/lib/types.d.ts +136 -0
  104. package/lib/types.js +1 -0
  105. package/lib/worklets/app-turns.d.ts +1 -0
  106. package/lib/worklets/app-turns.js +44 -0
  107. package/lib/worklets/client.d.ts +30 -0
  108. package/lib/worklets/client.js +144 -0
  109. package/lib/worklets/index.d.ts +4 -0
  110. package/lib/worklets/index.js +2 -0
  111. package/lib/worklets/instances.d.ts +15 -0
  112. package/lib/worklets/instances.js +194 -0
  113. package/lib/worklets/json-part.d.ts +1 -0
  114. package/lib/worklets/json-part.js +42 -0
  115. package/lib/worklets/runtime.d.ts +25 -0
  116. package/lib/worklets/runtime.js +80 -0
  117. package/lib/worklets/transfer-queue.d.ts +7 -0
  118. package/lib/worklets/transfer-queue.js +79 -0
  119. package/lib/worklets/types.d.ts +26 -0
  120. package/lib/worklets/types.js +1 -0
  121. package/nitro.json +32 -0
  122. package/nitrogen/generated/.gitattributes +1 -0
  123. package/nitrogen/generated/android/NarsilReactNative+autolinking.cmake +85 -0
  124. package/nitrogen/generated/android/NarsilReactNative+autolinking.gradle +27 -0
  125. package/nitrogen/generated/android/NarsilReactNativeOnLoad.cpp +69 -0
  126. package/nitrogen/generated/android/NarsilReactNativeOnLoad.hpp +34 -0
  127. package/nitrogen/generated/android/kotlin/com/margelo/nitro/narsil/NarsilReactNativeOnLoad.kt +35 -0
  128. package/nitrogen/generated/ios/NarsilReactNative+autolinking.rb +62 -0
  129. package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.cpp +17 -0
  130. package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.hpp +27 -0
  131. package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Umbrella.hpp +38 -0
  132. package/nitrogen/generated/ios/NarsilReactNativeAutolinking.mm +55 -0
  133. package/nitrogen/generated/ios/NarsilReactNativeAutolinking.swift +16 -0
  134. package/nitrogen/generated/shared/c++/DirectoryEntry.hpp +91 -0
  135. package/nitrogen/generated/shared/c++/FileStatus.hpp +103 -0
  136. package/nitrogen/generated/shared/c++/GraphMemory.hpp +120 -0
  137. package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.cpp +21 -0
  138. package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.hpp +62 -0
  139. package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.cpp +21 -0
  140. package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.hpp +62 -0
  141. package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.cpp +35 -0
  142. package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.hpp +100 -0
  143. package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.cpp +37 -0
  144. package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.hpp +86 -0
  145. package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.cpp +34 -0
  146. package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.hpp +78 -0
  147. package/nitrogen/generated/shared/c++/MemoryView.hpp +91 -0
  148. package/nitrogen/generated/shared/c++/NativeConnectOutcome.hpp +87 -0
  149. package/nitrogen/generated/shared/c++/NativeScores.hpp +87 -0
  150. package/nitrogen/generated/shared/c++/NativeSearchHits.hpp +91 -0
  151. package/nitrogen/generated/shared/c++/ProcessMemory.hpp +87 -0
  152. package/nitrogen/generated/shared/c++/StoreMemory.hpp +142 -0
  153. package/package.json +87 -3
  154. package/react-native.config.js +8 -0
  155. package/src/client.ts +44 -0
  156. package/src/constants.ts +9 -0
  157. package/src/engine/byte-files.ts +99 -0
  158. package/src/engine/core.ts +162 -0
  159. package/src/engine/files.ts +186 -0
  160. package/src/engine/host.ts +111 -0
  161. package/src/engine/install.ts +31 -0
  162. package/src/engine/native-error.ts +10 -0
  163. package/src/engine/stand-ins/atomics.ts +288 -0
  164. package/src/engine/stand-ins/crypto.ts +81 -0
  165. package/src/engine/stand-ins/process.ts +30 -0
  166. package/src/engine/stand-ins/streams.ts +45 -0
  167. package/src/engine/stand-ins/structured-clone.ts +118 -0
  168. package/src/engine/streamed.ts +40 -0
  169. package/src/engine/transfer.ts +72 -0
  170. package/src/errors.ts +73 -0
  171. package/src/index.ts +3 -0
  172. package/src/methods.ts +52 -0
  173. package/src/native-objects.ts +30 -0
  174. package/src/specs/core.nitro.ts +140 -0
  175. package/src/specs/files.nitro.ts +42 -0
  176. package/src/specs/host.nitro.ts +30 -0
  177. package/src/transferred.ts +18 -0
  178. package/src/types.ts +164 -0
  179. package/src/worklets/app-turns.ts +48 -0
  180. package/src/worklets/client.ts +165 -0
  181. package/src/worklets/index.ts +4 -0
  182. package/src/worklets/instances.ts +231 -0
  183. package/src/worklets/json-part.ts +38 -0
  184. package/src/worklets/runtime.ts +190 -0
  185. package/src/worklets/transfer-queue.ts +98 -0
  186. package/src/worklets/types.ts +37 -0
@@ -0,0 +1,144 @@
1
+ import { isWorkletFunction } from 'react-native-worklets';
2
+ import { rebuiltError, rebuiltValue } from '../errors';
3
+ import { FORWARDED_METHODS, STREAMED_METHODS } from '../methods';
4
+ import { loadNativeObjects } from '../native-objects';
5
+ import { jsonOfPart } from './json-part';
6
+ import { boxNativeObjects, sendCall, sendCallPart, sendCallPartText, sendDropCallParts, sendListen, sendOpen, startEngineRuntime, } from './runtime';
7
+ import { enqueueCall } from './transfer-queue';
8
+ const pendingCalls = new Map();
9
+ const handlersByInstance = new Map();
10
+ let nextCallId = 1;
11
+ function settleCall(callId, outcome) {
12
+ const pending = pendingCalls.get(callId);
13
+ if (pending === undefined)
14
+ return;
15
+ pendingCalls.delete(callId);
16
+ if (outcome.ok)
17
+ pending.resolve(outcome.value);
18
+ else
19
+ pending.reject(rebuiltError(outcome.error));
20
+ }
21
+ function deliverEvent(instanceId, event, payload) {
22
+ const handlers = handlersByInstance.get(instanceId)?.get(event);
23
+ if (handlers === undefined)
24
+ return;
25
+ const rebuilt = rebuiltValue(payload);
26
+ for (const handler of [...handlers]) {
27
+ try {
28
+ handler(rebuilt);
29
+ }
30
+ catch (error) {
31
+ setTimeout(() => {
32
+ throw error;
33
+ }, 0);
34
+ }
35
+ }
36
+ }
37
+ function request(send) {
38
+ return new Promise((resolve, reject) => {
39
+ const callId = nextCallId++;
40
+ pendingCalls.set(callId, { resolve, reject });
41
+ try {
42
+ send(callId);
43
+ }
44
+ catch (error) {
45
+ pendingCalls.delete(callId);
46
+ reject(error);
47
+ }
48
+ });
49
+ }
50
+ function phoneNarsil(runtime, instanceId) {
51
+ const handlers = new Map();
52
+ handlersByInstance.set(instanceId, handlers);
53
+ function call(method, args) {
54
+ return new Promise((resolve, reject) => {
55
+ const callId = nextCallId++;
56
+ pendingCalls.set(callId, { resolve, reject });
57
+ enqueueCall(args, {
58
+ sendPart: (argIndex, items) => {
59
+ const text = jsonOfPart(items);
60
+ if (text === null)
61
+ sendCallPart(runtime, callId, argIndex, items);
62
+ else
63
+ sendCallPartText(runtime, callId, argIndex, text);
64
+ },
65
+ sendCall: (values, partedArgs) => sendCall(runtime, callId, settleCall, instanceId, method, values, partedArgs),
66
+ dropParts: () => sendDropCallParts(runtime, callId),
67
+ fail: error => {
68
+ pendingCalls.delete(callId);
69
+ reject(error);
70
+ },
71
+ });
72
+ });
73
+ }
74
+ const client = {};
75
+ for (const method of [...FORWARDED_METHODS, ...STREAMED_METHODS]) {
76
+ client[method] = (...args) => call(method, args);
77
+ }
78
+ client.on = (event, handler) => {
79
+ let registered = handlers.get(event);
80
+ if (registered === undefined) {
81
+ registered = new Set();
82
+ handlers.set(event, registered);
83
+ }
84
+ const first = registered.size === 0;
85
+ registered.add(handler);
86
+ if (first)
87
+ sendListen(runtime, instanceId, event, deliverEvent, true);
88
+ };
89
+ client.off = (event, handler) => {
90
+ const registered = handlers.get(event);
91
+ if (registered === undefined || !registered.delete(handler))
92
+ return;
93
+ if (registered.size > 0)
94
+ return;
95
+ handlers.delete(event);
96
+ sendListen(runtime, instanceId, event, deliverEvent, false);
97
+ };
98
+ client.shutdown = async () => {
99
+ try {
100
+ await call('shutdown', []);
101
+ }
102
+ finally {
103
+ handlers.clear();
104
+ handlersByInstance.delete(instanceId);
105
+ }
106
+ };
107
+ return client;
108
+ }
109
+ /**
110
+ * Starts a Narsil engine on its own Worklets runtime and resolves with a
111
+ * client whose methods send each request to that runtime, so the engine's
112
+ * JavaScript work leaves the app's JS thread free. Every engine that this
113
+ * function starts shares one runtime, and each engine keeps its own indexes.
114
+ * The app needs `react-native-worklets` with Bundle Mode turned on. The
115
+ * client copies each batch that an insert sends across to the engine's
116
+ * runtime before the engine indexes it, as JSON text when the batch holds
117
+ * plain data. On a physical iPhone and a physical Android phone, inserts took
118
+ * 6% to 57% longer than on the app's own JS runtime, depending on the number
119
+ * of documents and on durability, and an import from a file, which sends only
120
+ * the file's path across, took up to 11% longer. In return, the longest pause
121
+ * that a 10 ms timer on the app's JS thread saw during an insert of 50,000
122
+ * documents fell from 111 ms to 30 ms on the Android phone and from 144 ms to
123
+ * 74 ms on the iPhone.
124
+ *
125
+ * @param config - The engine settings that cross to the engine's runtime as
126
+ * data. `durability` defaults to a write-ahead log in the `narsil` directory
127
+ * of the app's private storage.
128
+ * @param setup - A worklet that returns the settings that hold functions,
129
+ * such as embedding adapters, plugins, or an id generator.
130
+ * @returns The client, once the engine has started on its runtime.
131
+ * @throws A `NarsilError` with `NATIVE_DEPENDENCY_MISSING` when the app's
132
+ * build lacks the native module, as in Expo Go.
133
+ *
134
+ * @public
135
+ */
136
+ export async function createNarsil(config = {}, setup) {
137
+ if (setup !== undefined && !isWorkletFunction(setup)) {
138
+ throw new TypeError("The setup function must be a worklet, so start its body with the 'worklet' directive");
139
+ }
140
+ const natives = await loadNativeObjects();
141
+ const runtime = startEngineRuntime(await boxNativeObjects(natives));
142
+ const instanceId = await request(callId => sendOpen(runtime, callId, settleCall, config, setup));
143
+ return phoneNarsil(runtime, instanceId);
144
+ }
@@ -0,0 +1,4 @@
1
+ export { isNarsilError, type PhoneNarsilError } from '../errors';
2
+ export type { PhoneEventHandler, PhoneNarsil } from '../types';
3
+ export { createNarsil } from './client';
4
+ export type { EngineSetup, WorkletNarsilConfig } from './types';
@@ -0,0 +1,2 @@
1
+ export { isNarsilError } from '../errors';
2
+ export { createNarsil } from './client';
@@ -0,0 +1,15 @@
1
+ import { type NarsilEventMap } from '@delali/narsil';
2
+ import { type EngineMethod } from '../methods';
3
+ import type { CallOutcome } from '../transferred';
4
+ import type { EngineSetup, WorkletNarsilConfig } from './types';
5
+ type Settle = (callId: number, outcome: CallOutcome) => void;
6
+ type DeliverEvent = (instanceId: number, event: EventName, payload: unknown) => void;
7
+ type EventName = keyof NarsilEventMap;
8
+ export declare function recordEngineStartFailure(error: unknown): void;
9
+ export declare function openOnEngine(callId: number, settle: Settle, config: WorkletNarsilConfig, setup: EngineSetup | undefined): void;
10
+ export declare function stageCallPart(callId: number, argIndex: number, items: readonly unknown[]): void;
11
+ export declare function stageCallPartText(callId: number, argIndex: number, text: string): void;
12
+ export declare function dropCallParts(callId: number): void;
13
+ export declare function callOnEngine(callId: number, settle: Settle, instanceId: number, method: EngineMethod, args: readonly unknown[], partedArgs: readonly number[]): void;
14
+ export declare function listenOnEngine(instanceId: number, event: EventName, forward: DeliverEvent, listening: boolean): void;
15
+ export {};
@@ -0,0 +1,194 @@
1
+ import { createNarsil, ErrorCodes, NarsilError, } from '@delali/narsil';
2
+ import { scheduleOnRN } from 'react-native-worklets';
3
+ import { installedHost } from '../engine/install';
4
+ import { callStreamed, withPhoneDurability } from '../engine/streamed';
5
+ import { transferable, transferredError } from '../engine/transfer';
6
+ import { FORWARDED_METHODS, STREAMED_METHODS } from '../methods';
7
+ const FORWARDED = new Set(FORWARDED_METHODS);
8
+ const STREAMED = new Set(STREAMED_METHODS);
9
+ const UNDELIVERABLE_OUTCOME = {
10
+ ok: false,
11
+ error: {
12
+ transferredError: true,
13
+ name: 'Error',
14
+ message: "The engine's runtime could not send this call's outcome back to the app",
15
+ code: undefined,
16
+ details: undefined,
17
+ },
18
+ };
19
+ const instances = new Map();
20
+ const stagedParts = new Map();
21
+ const failedCalls = new Map();
22
+ const engineStart = { failed: false, error: undefined };
23
+ let nextInstanceId = 1;
24
+ function instanceOf(instanceId) {
25
+ const instance = instances.get(instanceId);
26
+ if (instance === undefined) {
27
+ throw new NarsilError(ErrorCodes.INSTANCE_SHUT_DOWN, 'This Narsil instance has been shut down');
28
+ }
29
+ return instance;
30
+ }
31
+ function failureOf(error) {
32
+ return { ok: false, error: transferredError(error) };
33
+ }
34
+ function deliver(settle, callId, outcome) {
35
+ try {
36
+ scheduleOnRN(settle, callId, outcome());
37
+ return;
38
+ }
39
+ catch (error) {
40
+ try {
41
+ scheduleOnRN(settle, callId, failureOf(error));
42
+ return;
43
+ }
44
+ catch { }
45
+ }
46
+ try {
47
+ scheduleOnRN(settle, callId, UNDELIVERABLE_OUTCOME);
48
+ }
49
+ catch { }
50
+ }
51
+ function report(settle, callId, work) {
52
+ let pending;
53
+ try {
54
+ pending = work();
55
+ }
56
+ catch (error) {
57
+ deliver(settle, callId, () => failureOf(error));
58
+ return;
59
+ }
60
+ pending.then(value => deliver(settle, callId, () => ({ ok: true, value: transferable(value) })), error => deliver(settle, callId, () => failureOf(error)));
61
+ }
62
+ export function recordEngineStartFailure(error) {
63
+ engineStart.failed = true;
64
+ engineStart.error = error;
65
+ }
66
+ async function openInstance(config, setup) {
67
+ if (engineStart.failed)
68
+ throw engineStart.error;
69
+ const fromSetup = setup === undefined ? {} : await setup();
70
+ const merged = { ...withPhoneDurability(config, fromSetup.durability !== undefined), ...fromSetup };
71
+ const narsil = await createNarsil(merged);
72
+ const instanceId = nextInstanceId++;
73
+ instances.set(instanceId, { narsil, listeners: new Map() });
74
+ return instanceId;
75
+ }
76
+ async function shutdown(instanceId) {
77
+ const instance = instances.get(instanceId);
78
+ if (instance === undefined)
79
+ return;
80
+ instances.delete(instanceId);
81
+ for (const [event, listener] of instance.listeners)
82
+ instance.narsil.off(event, listener);
83
+ instance.listeners.clear();
84
+ await instance.narsil.shutdown();
85
+ }
86
+ function dispatch(instanceId, method, args) {
87
+ if (method === 'shutdown')
88
+ return shutdown(instanceId);
89
+ const { narsil } = instanceOf(instanceId);
90
+ if (FORWARDED.has(method)) {
91
+ const target = narsil[method];
92
+ return Promise.resolve(target.apply(narsil, [...args]));
93
+ }
94
+ if (!STREAMED.has(method))
95
+ throw new TypeError(`The Narsil engine has no method called ${String(method)}`);
96
+ return callStreamed(narsil, installedHost(), method, args);
97
+ }
98
+ export function openOnEngine(callId, settle, config, setup) {
99
+ report(settle, callId, () => openInstance(config, setup));
100
+ }
101
+ function failCall(callId, error) {
102
+ stagedParts.delete(callId);
103
+ failedCalls.set(callId, error);
104
+ }
105
+ function stageItems(callId, argIndex, items) {
106
+ if (!Array.isArray(items))
107
+ throw new TypeError('A part of a call reached the engine without its items');
108
+ let parts = stagedParts.get(callId);
109
+ if (parts === undefined) {
110
+ parts = new Map();
111
+ stagedParts.set(callId, parts);
112
+ }
113
+ let staged = parts.get(argIndex);
114
+ if (staged === undefined) {
115
+ staged = [];
116
+ parts.set(argIndex, staged);
117
+ }
118
+ for (const item of items)
119
+ staged.push(item);
120
+ }
121
+ export function stageCallPart(callId, argIndex, items) {
122
+ if (failedCalls.has(callId))
123
+ return;
124
+ try {
125
+ stageItems(callId, argIndex, items);
126
+ }
127
+ catch (error) {
128
+ failCall(callId, error);
129
+ }
130
+ }
131
+ export function stageCallPartText(callId, argIndex, text) {
132
+ if (failedCalls.has(callId))
133
+ return;
134
+ try {
135
+ stageItems(callId, argIndex, JSON.parse(text));
136
+ }
137
+ catch (error) {
138
+ failCall(callId, error);
139
+ }
140
+ }
141
+ export function dropCallParts(callId) {
142
+ stagedParts.delete(callId);
143
+ failedCalls.delete(callId);
144
+ }
145
+ function withStagedParts(callId, args, partedArgs) {
146
+ const parts = stagedParts.get(callId);
147
+ const failure = failedCalls.get(callId);
148
+ stagedParts.delete(callId);
149
+ if (failedCalls.delete(callId))
150
+ throw failure;
151
+ const values = [...args];
152
+ for (const argIndex of partedArgs)
153
+ values[argIndex] = parts?.get(argIndex) ?? [];
154
+ return values;
155
+ }
156
+ export function callOnEngine(callId, settle, instanceId, method, args, partedArgs) {
157
+ report(settle, callId, () => dispatch(instanceId, method, partedArgs.length === 0 ? args : withStagedParts(callId, args, partedArgs)));
158
+ }
159
+ function forwardEvent(forward, instanceId, event, payload) {
160
+ try {
161
+ scheduleOnRN(forward, instanceId, event, transferable(payload));
162
+ }
163
+ catch { }
164
+ }
165
+ function changeListener(instanceId, event, forward, listening) {
166
+ const instance = instances.get(instanceId);
167
+ if (instance === undefined)
168
+ return;
169
+ const existing = instance.listeners.get(event);
170
+ if (!listening) {
171
+ if (existing === undefined)
172
+ return;
173
+ instance.listeners.delete(event);
174
+ instance.narsil.off(event, existing);
175
+ return;
176
+ }
177
+ if (existing !== undefined)
178
+ return;
179
+ const listener = (payload) => forwardEvent(forward, instanceId, event, payload);
180
+ instance.listeners.set(event, listener);
181
+ try {
182
+ instance.narsil.on(event, listener);
183
+ }
184
+ catch (error) {
185
+ instance.listeners.delete(event);
186
+ throw error;
187
+ }
188
+ }
189
+ export function listenOnEngine(instanceId, event, forward, listening) {
190
+ try {
191
+ changeListener(instanceId, event, forward, listening);
192
+ }
193
+ catch { }
194
+ }
@@ -0,0 +1 @@
1
+ export declare function jsonOfPart(items: readonly unknown[]): string | null;
@@ -0,0 +1,42 @@
1
+ import { WORKLET_JSON_PART_MAX_DEPTH } from '../constants';
2
+ function encodesExactly(value, depth) {
3
+ switch (typeof value) {
4
+ case 'string':
5
+ case 'boolean':
6
+ return true;
7
+ case 'number':
8
+ return Number.isFinite(value) && !Object.is(value, -0);
9
+ case 'object':
10
+ break;
11
+ default:
12
+ return false;
13
+ }
14
+ if (value === null)
15
+ return true;
16
+ if (depth >= WORKLET_JSON_PART_MAX_DEPTH)
17
+ return false;
18
+ const prototype = Object.getPrototypeOf(value);
19
+ if (prototype === Array.prototype) {
20
+ const items = value;
21
+ for (let index = 0; index < items.length; index++) {
22
+ if (!(index in items) || !encodesExactly(items[index], depth + 1))
23
+ return false;
24
+ }
25
+ return true;
26
+ }
27
+ if (prototype !== Object.prototype && prototype !== null)
28
+ return false;
29
+ const record = value;
30
+ for (const key of Object.keys(record)) {
31
+ if (!encodesExactly(record[key], depth + 1))
32
+ return false;
33
+ }
34
+ return true;
35
+ }
36
+ export function jsonOfPart(items) {
37
+ for (const item of items) {
38
+ if (!encodesExactly(item, 1))
39
+ return null;
40
+ }
41
+ return JSON.stringify(items);
42
+ }
@@ -0,0 +1,25 @@
1
+ import type { BoxedHybridObject } from 'react-native-nitro-modules';
2
+ import { type WorkletRuntime } from 'react-native-worklets';
3
+ import { type NativeObjects } from '../engine/host';
4
+ import type { EngineMethod } from '../methods';
5
+ import type { NarsilCore } from '../specs/core.nitro';
6
+ import type { NarsilFiles } from '../specs/files.nitro';
7
+ import type { NarsilHost } from '../specs/host.nitro';
8
+ import type { CallOutcome } from '../transferred';
9
+ import type { EngineSetup, WorkletNarsilConfig } from './types';
10
+ type Settle = (callId: number, outcome: CallOutcome) => void;
11
+ type DeliverEvent = (instanceId: number, event: string, payload: unknown) => void;
12
+ export interface BoxedNativeObjects {
13
+ host: BoxedHybridObject<NarsilHost>;
14
+ files: BoxedHybridObject<NarsilFiles>;
15
+ core: BoxedHybridObject<NarsilCore>;
16
+ }
17
+ export declare function boxNativeObjects(natives: NativeObjects): Promise<BoxedNativeObjects>;
18
+ export declare function startEngineRuntime({ host, files, core }: BoxedNativeObjects): WorkletRuntime;
19
+ export declare function sendOpen(runtime: WorkletRuntime, callId: number, settle: Settle, config: WorkletNarsilConfig, setup: EngineSetup | undefined): void;
20
+ export declare function sendCall(runtime: WorkletRuntime, callId: number, settle: Settle, instanceId: number, method: EngineMethod, args: readonly unknown[], partedArgs: readonly number[]): void;
21
+ export declare function sendCallPart(runtime: WorkletRuntime, callId: number, argIndex: number, items: readonly unknown[]): void;
22
+ export declare function sendCallPartText(runtime: WorkletRuntime, callId: number, argIndex: number, text: string): void;
23
+ export declare function sendDropCallParts(runtime: WorkletRuntime, callId: number): void;
24
+ export declare function sendListen(runtime: WorkletRuntime, instanceId: number, event: string, deliver: DeliverEvent, listening: boolean): void;
25
+ export {};
@@ -0,0 +1,80 @@
1
+ import { createWorkletRuntime, scheduleOnRuntime } from 'react-native-worklets';
2
+ import { ENGINE_RUNTIME_NAME } from '../constants';
3
+ import { adoptAppThreadPriorityOn, installWorkletDispatcherOn, threadPriorityOf, } from '../engine/host';
4
+ import { installEngineHost } from '../engine/install';
5
+ import { callOnEngine, dropCallParts, listenOnEngine, openOnEngine, recordEngineStartFailure, stageCallPart, stageCallPartText, } from './instances';
6
+ let engineRuntime = null;
7
+ export async function boxNativeObjects(natives) {
8
+ const { NitroModules } = await import('react-native-nitro-modules');
9
+ return {
10
+ host: NitroModules.box(natives.host),
11
+ files: NitroModules.box(natives.files),
12
+ core: NitroModules.box(natives.core),
13
+ };
14
+ }
15
+ export function startEngineRuntime({ host, files, core }) {
16
+ if (engineRuntime !== null)
17
+ return engineRuntime;
18
+ const appThreadPriority = threadPriorityOf(host.unbox());
19
+ const runtime = createWorkletRuntime({
20
+ name: ENGINE_RUNTIME_NAME,
21
+ initializer: () => {
22
+ 'worklet';
23
+ try {
24
+ const nativeHost = host.unbox();
25
+ const nativeThreads = installWorkletDispatcherOn(nativeHost);
26
+ installEngineHost({ host: nativeHost, files: files.unbox(), core: core.unbox() }, nativeThreads);
27
+ }
28
+ catch (error) {
29
+ recordEngineStartFailure(error);
30
+ }
31
+ },
32
+ });
33
+ if (appThreadPriority !== null) {
34
+ scheduleOnRuntime(runtime, (priority) => {
35
+ 'worklet';
36
+ try {
37
+ adoptAppThreadPriorityOn(host.unbox(), priority);
38
+ }
39
+ catch { }
40
+ }, appThreadPriority);
41
+ }
42
+ engineRuntime = runtime;
43
+ return runtime;
44
+ }
45
+ export function sendOpen(runtime, callId, settle, config, setup) {
46
+ scheduleOnRuntime(runtime, (id, deliver, settings, prepare) => {
47
+ 'worklet';
48
+ openOnEngine(id, deliver, settings, prepare);
49
+ }, callId, settle, config, setup);
50
+ }
51
+ export function sendCall(runtime, callId, settle, instanceId, method, args, partedArgs) {
52
+ scheduleOnRuntime(runtime, (id, deliver, instance, name, values, parted) => {
53
+ 'worklet';
54
+ callOnEngine(id, deliver, instance, name, values, parted);
55
+ }, callId, settle, instanceId, method, args, partedArgs);
56
+ }
57
+ export function sendCallPart(runtime, callId, argIndex, items) {
58
+ scheduleOnRuntime(runtime, (id, index, part) => {
59
+ 'worklet';
60
+ stageCallPart(id, index, part);
61
+ }, callId, argIndex, items);
62
+ }
63
+ export function sendCallPartText(runtime, callId, argIndex, text) {
64
+ scheduleOnRuntime(runtime, (id, index, part) => {
65
+ 'worklet';
66
+ stageCallPartText(id, index, part);
67
+ }, callId, argIndex, text);
68
+ }
69
+ export function sendDropCallParts(runtime, callId) {
70
+ scheduleOnRuntime(runtime, (id) => {
71
+ 'worklet';
72
+ dropCallParts(id);
73
+ }, callId);
74
+ }
75
+ export function sendListen(runtime, instanceId, event, deliver, listening) {
76
+ scheduleOnRuntime(runtime, (instance, name, forward, on) => {
77
+ 'worklet';
78
+ listenOnEngine(instance, name, forward, on);
79
+ }, instanceId, event, deliver, listening);
80
+ }
@@ -0,0 +1,7 @@
1
+ export interface CallSends {
2
+ sendPart(argIndex: number, items: readonly unknown[]): void;
3
+ sendCall(args: readonly unknown[], partedArgs: readonly number[]): void;
4
+ dropParts(): void;
5
+ fail(error: unknown): void;
6
+ }
7
+ export declare function enqueueCall(args: readonly unknown[], sends: CallSends): void;
@@ -0,0 +1,79 @@
1
+ import { WORKLET_TRANSFER_FIRST_PART_ITEMS, WORKLET_TRANSFER_LARGEST_PART_ITEMS, WORKLET_TRANSFER_PART_TARGET_MS, WORKLET_TRANSFER_TURN_BUDGET_MS, } from '../constants';
2
+ import { afterAppTurn } from './app-turns';
3
+ const waiting = [];
4
+ let sending = false;
5
+ function sendWaiting() {
6
+ sending = true;
7
+ const turnStarted = performance.now();
8
+ while (waiting.length > 0) {
9
+ const transfer = waiting[0];
10
+ let finished;
11
+ try {
12
+ finished = transfer.sendNextStep();
13
+ }
14
+ catch (error) {
15
+ finished = true;
16
+ transfer.fail(error);
17
+ }
18
+ if (finished)
19
+ waiting.shift();
20
+ if (waiting.length > 0 && performance.now() - turnStarted >= WORKLET_TRANSFER_TURN_BUDGET_MS) {
21
+ afterAppTurn(sendWaiting);
22
+ return;
23
+ }
24
+ }
25
+ sending = false;
26
+ }
27
+ function enqueue(transfer) {
28
+ waiting.push(transfer);
29
+ if (!sending)
30
+ sendWaiting();
31
+ }
32
+ function nextPartItems(sentItems, elapsedMs) {
33
+ const perItemMs = elapsedMs / sentItems;
34
+ if (perItemMs <= 0)
35
+ return WORKLET_TRANSFER_LARGEST_PART_ITEMS;
36
+ const fitting = Math.floor(WORKLET_TRANSFER_PART_TARGET_MS / perItemMs);
37
+ return Math.min(WORKLET_TRANSFER_LARGEST_PART_ITEMS, Math.max(1, fitting));
38
+ }
39
+ export function enqueueCall(args, sends) {
40
+ const partedArgs = [];
41
+ for (const [index, value] of args.entries()) {
42
+ if (Array.isArray(value) && value.length > WORKLET_TRANSFER_FIRST_PART_ITEMS)
43
+ partedArgs.push(index);
44
+ }
45
+ let argPosition = 0;
46
+ let offset = 0;
47
+ let partItems = WORKLET_TRANSFER_FIRST_PART_ITEMS;
48
+ let sentAnyPart = false;
49
+ function sendNextStep() {
50
+ const argIndex = partedArgs[argPosition];
51
+ if (argIndex === undefined) {
52
+ const remaining = args.map((value, index) => (partedArgs.includes(index) ? null : value));
53
+ sends.sendCall(remaining, partedArgs);
54
+ return true;
55
+ }
56
+ const items = args[argIndex];
57
+ const part = items.slice(offset, offset + partItems);
58
+ const started = performance.now();
59
+ sends.sendPart(argIndex, part);
60
+ sentAnyPart = true;
61
+ partItems = nextPartItems(part.length, performance.now() - started);
62
+ offset += part.length;
63
+ if (offset >= items.length) {
64
+ argPosition += 1;
65
+ offset = 0;
66
+ }
67
+ return false;
68
+ }
69
+ function fail(error) {
70
+ if (sentAnyPart) {
71
+ try {
72
+ sends.dropParts();
73
+ }
74
+ catch { }
75
+ }
76
+ sends.fail(error);
77
+ }
78
+ enqueue({ sendNextStep, fail });
79
+ }
@@ -0,0 +1,26 @@
1
+ import type { DurabilityConfig, NarsilConfig } from '@delali/narsil';
2
+ /**
3
+ * The engine settings that an app passes to the engine on its own Worklets
4
+ * runtime. Each value has to cross to that runtime as data, so the settings
5
+ * that hold a function, such as an embedding adapter or a plugin, go in an
6
+ * {@link EngineSetup} worklet instead.
7
+ *
8
+ * `durability` defaults to a write-ahead log in a `narsil` directory inside
9
+ * the app's private storage, and `null` keeps the index in memory alone. A
10
+ * relative `directory` resolves against that private storage.
11
+ *
12
+ * @public
13
+ */
14
+ export type WorkletNarsilConfig = Omit<NarsilConfig, 'persistence' | 'invalidation' | 'plugins' | 'idGenerator' | 'embedding' | 'embeddingAdapters' | 'rerankAdapters' | 'events' | 'durability'> & {
15
+ durability?: DurabilityConfig | null;
16
+ };
17
+ /**
18
+ * A worklet that runs on the engine's runtime before the engine starts, and
19
+ * returns the settings that hold functions, such as `embeddingAdapters`,
20
+ * `plugins`, or `idGenerator`. Its values override the matching values of
21
+ * {@link WorkletNarsilConfig}. Mark the function with the `'worklet'`
22
+ * directive, and register a language or a tokenizer inside it.
23
+ *
24
+ * @public
25
+ */
26
+ export type EngineSetup = () => Partial<NarsilConfig> | Promise<Partial<NarsilConfig>>;
@@ -0,0 +1 @@
1
+ export {};
package/nitro.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://nitro.margelo.com/nitro.schema.json",
3
+ "cxxNamespace": ["narsil"],
4
+ "ios": {
5
+ "iosModuleName": "NarsilReactNative"
6
+ },
7
+ "android": {
8
+ "androidNamespace": ["narsil"],
9
+ "androidCxxLibName": "NarsilReactNative"
10
+ },
11
+ "autolinking": {
12
+ "NarsilHost": {
13
+ "all": {
14
+ "language": "c++",
15
+ "implementationClassName": "HybridNarsilHostImpl"
16
+ }
17
+ },
18
+ "NarsilFiles": {
19
+ "all": {
20
+ "language": "c++",
21
+ "implementationClassName": "HybridNarsilFilesImpl"
22
+ }
23
+ },
24
+ "NarsilCore": {
25
+ "all": {
26
+ "language": "c++",
27
+ "implementationClassName": "HybridNarsilCoreImpl"
28
+ }
29
+ }
30
+ },
31
+ "ignorePaths": ["**/node_modules", "examples"]
32
+ }
@@ -0,0 +1 @@
1
+ ** linguist-generated=true