@legendapp/spark 0.0.1-next.2 → 0.0.1-next.3

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 (399) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -4
  3. package/ai-codex.ts +1 -0
  4. package/ai.ts +1 -0
  5. package/app-documents.ts +1 -0
  6. package/config-plugin.d.cts +2 -0
  7. package/contracts.ts +2 -0
  8. package/diagnostics.ts +1 -0
  9. package/expo-config.d.cts +1 -0
  10. package/metro.cjs +4 -1
  11. package/metro.d.cts +12 -0
  12. package/native.cjs +3 -1
  13. package/native.d.cts +6 -0
  14. package/package.json +82 -14
  15. package/schema.json +276 -58
  16. package/settings-observable.ts +1 -0
  17. package/settings-window.ts +1 -0
  18. package/shortcuts-commands.ts +1 -0
  19. package/shortcuts-keyboard.ts +3 -0
  20. package/ui-classnames.ts +1 -0
  21. package/ui-glass.ts +1 -0
  22. package/ui-search.ts +1 -0
  23. package/ui-sidebar.ts +1 -0
  24. package/ui-split-view.ts +1 -0
  25. package/ui-symbol.ts +1 -0
  26. package/vendor/node_modules/@legendapp/spark-ai/package.json +19 -0
  27. package/vendor/node_modules/@legendapp/spark-ai/src/availability.ts +17 -0
  28. package/vendor/node_modules/@legendapp/spark-ai/src/index.ts +15 -0
  29. package/vendor/node_modules/@legendapp/spark-ai/src/invocations.ts +34 -0
  30. package/vendor/node_modules/@legendapp/spark-ai/src/output.ts +62 -0
  31. package/vendor/node_modules/@legendapp/spark-ai/src/run.ts +12 -0
  32. package/vendor/node_modules/@legendapp/spark-ai/src/types.ts +16 -0
  33. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.h +2 -1
  34. package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.mm +126 -24
  35. package/vendor/node_modules/@legendapp/spark-audio/package.json +5 -2
  36. package/vendor/node_modules/@legendapp/spark-audio/src/NativeSparkAudio.ts +5 -1
  37. package/vendor/node_modules/@legendapp/spark-audio/src/desktop.ts +36 -32
  38. package/vendor/node_modules/@legendapp/spark-audio/src/events.ts +11 -0
  39. package/vendor/node_modules/@legendapp/spark-audio/src/hooks.ts +65 -0
  40. package/vendor/node_modules/@legendapp/spark-audio/src/index.web.ts +92 -33
  41. package/vendor/node_modules/@legendapp/spark-audio/src/media-session-unavailable.ts +6 -4
  42. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.ts +52 -19
  43. package/vendor/node_modules/@legendapp/spark-audio/src/media-session.web.ts +18 -10
  44. package/vendor/node_modules/@legendapp/spark-audio/src/media-types.ts +40 -13
  45. package/vendor/node_modules/@legendapp/spark-audio/src/mobile.ts +40 -32
  46. package/vendor/node_modules/@legendapp/spark-audio/src/player.ts +97 -0
  47. package/vendor/node_modules/@legendapp/spark-audio/src/status-listeners.ts +21 -15
  48. package/vendor/node_modules/@legendapp/spark-audio/src/types.ts +9 -4
  49. package/vendor/node_modules/@legendapp/spark-audio/windows/SparkAudio/SparkAudio.h +67 -20
  50. package/vendor/node_modules/@legendapp/spark-auth-session/package.json +4 -3
  51. package/vendor/node_modules/@legendapp/spark-auth-session/src/core.ts +60 -42
  52. package/vendor/node_modules/@legendapp/spark-auth-session/src/index.ts +28 -12
  53. package/vendor/node_modules/@legendapp/spark-auth-session/src/mobile.ts +97 -41
  54. package/vendor/node_modules/@legendapp/spark-auth-session/src/types.ts +20 -9
  55. package/vendor/node_modules/@legendapp/spark-cli/dist/add-desktop.js +50 -7
  56. package/vendor/node_modules/@legendapp/spark-cli/dist/build-mode.js +1 -1
  57. package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +83 -48
  58. package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +60 -0
  59. package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +7 -2
  60. package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +31 -5
  61. package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +69 -23
  62. package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +12 -3
  63. package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +12 -9
  64. package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +1 -1
  65. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-dev-patch.cjs +4 -2
  66. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-metro.cjs +1 -1
  67. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
  68. package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +10 -38
  69. package/vendor/node_modules/@legendapp/spark-cli/dist/local.js +1 -1
  70. package/vendor/node_modules/@legendapp/spark-cli/dist/metro.cjs +26 -5
  71. package/vendor/node_modules/@legendapp/spark-cli/dist/native-metadata.js +26 -0
  72. package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +84 -0
  73. package/vendor/node_modules/@legendapp/spark-cli/dist/native-preparation.js +88 -0
  74. package/vendor/node_modules/@legendapp/spark-cli/dist/notary-profiles.js +75 -0
  75. package/vendor/node_modules/@legendapp/spark-cli/dist/package.js +4 -2
  76. package/vendor/node_modules/@legendapp/spark-cli/dist/platform.js +16 -2
  77. package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +66 -13
  78. package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +84 -4
  79. package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +23 -18
  80. package/vendor/node_modules/@legendapp/spark-cli/dist/runner-install.js +145 -31
  81. package/vendor/node_modules/@legendapp/spark-cli/dist/signing.js +4 -3
  82. package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
  83. package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +4 -1
  84. package/vendor/node_modules/@legendapp/spark-cli/package.json +7 -6
  85. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/App.tsx +6 -12
  86. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/desktop.config.json +5 -3
  87. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +11 -9
  88. package/vendor/node_modules/@legendapp/spark-cli/templates/diff-lite/Git.macos.tsx +2 -2
  89. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.macos.tsx +23 -21
  90. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.windows.tsx +23 -21
  91. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.desktop.ts +11 -8
  92. package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.macos.ts +3 -3
  93. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/Lifecycle.desktop.tsx +8 -14
  94. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/OpenFiles.desktop.tsx +5 -10
  95. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.desktop.ts +11 -8
  96. package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.macos.ts +3 -3
  97. package/vendor/node_modules/@legendapp/spark-cli/templates/music-lite/assets.desktop.ts +2 -2
  98. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Activation.desktop.tsx +2 -2
  99. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Desktop.desktop.ts +37 -15
  100. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/session.ts +5 -6
  101. package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/window-session.ts +5 -4
  102. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/desktop.config.json +5 -3
  103. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/metro.config.js +1 -1
  104. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +13 -11
  105. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/react-native.config.js +1 -1
  106. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +13 -11
  107. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/react-native.config.js +1 -1
  108. package/vendor/node_modules/@legendapp/spark-clipboard/macos/RNDesktopClipboard.mm +10 -16
  109. package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
  110. package/vendor/node_modules/@legendapp/spark-clipboard/src/NativeDesktopClipboard.ts +1 -1
  111. package/vendor/node_modules/@legendapp/spark-clipboard/src/desktop.ts +55 -31
  112. package/vendor/node_modules/@legendapp/spark-clipboard/src/expo.ts +21 -11
  113. package/vendor/node_modules/@legendapp/spark-clipboard/src/options.ts +8 -0
  114. package/vendor/node_modules/@legendapp/spark-clipboard/src/types.ts +10 -0
  115. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.h +38 -15
  116. package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.vcxproj +2 -0
  117. package/vendor/node_modules/@legendapp/spark-codex/RNCodex.podspec +22 -0
  118. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.hpp +23 -0
  119. package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.mm +1529 -0
  120. package/vendor/node_modules/@legendapp/spark-codex/nitro.json +21 -0
  121. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/.gitattributes +1 -0
  122. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex+autolinking.rb +62 -0
  123. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.cpp +17 -0
  124. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.hpp +27 -0
  125. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Umbrella.hpp +38 -0
  126. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.mm +35 -0
  127. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.swift +16 -0
  128. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexAvailability.hpp +95 -0
  129. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexRunResult.hpp +99 -0
  130. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.cpp +24 -0
  131. package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.hpp +71 -0
  132. package/vendor/node_modules/@legendapp/spark-codex/package.json +35 -0
  133. package/vendor/node_modules/@legendapp/spark-codex/react-native.config.js +11 -0
  134. package/vendor/node_modules/@legendapp/spark-codex/src/Codex.nitro.ts +30 -0
  135. package/vendor/node_modules/@legendapp/spark-codex/src/index.ts +65 -0
  136. package/vendor/node_modules/@legendapp/spark-codex/src/native.ts +6 -0
  137. package/vendor/node_modules/@legendapp/spark-commands/package.json +21 -0
  138. package/vendor/node_modules/@legendapp/spark-commands/src/bindings.ts +90 -0
  139. package/vendor/node_modules/@legendapp/spark-commands/src/index.tsx +400 -0
  140. package/vendor/node_modules/@legendapp/spark-commands/src/router.ts +138 -0
  141. package/vendor/node_modules/@legendapp/spark-commands/src/storage.ts +20 -0
  142. package/vendor/node_modules/@legendapp/spark-context-menu/macos/RNContextMenu.mm +7 -8
  143. package/vendor/node_modules/@legendapp/spark-context-menu/package.json +4 -3
  144. package/vendor/node_modules/@legendapp/spark-context-menu/src/NativeContextMenu.ts +1 -1
  145. package/vendor/node_modules/@legendapp/spark-context-menu/src/index.ts +26 -26
  146. package/vendor/node_modules/@legendapp/spark-context-menu/windows/SparkContextMenu/SparkContextMenu.h +3 -2
  147. package/vendor/node_modules/@legendapp/spark-desktop-app/RNDesktopApp.podspec +2 -1
  148. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryJSI.hpp +89 -0
  149. package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryWindows.hpp +22 -0
  150. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/RNDesktopApp.mm +13 -7
  151. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkBinaryJSI.h +48 -0
  152. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.h +4 -0
  153. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.mm +33 -33
  154. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktopError.h +20 -0
  155. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.h +26 -0
  156. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.mm +49 -0
  157. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.h +9 -0
  158. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.mm +70 -0
  159. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkWindow.mm +9 -6
  160. package/vendor/node_modules/@legendapp/spark-desktop-app/macos/recent-documents/RNRecentDocumentController.mm +55 -0
  161. package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +5 -3
  162. package/vendor/node_modules/@legendapp/spark-desktop-app/src/NativeDesktopApp.ts +1 -1
  163. package/vendor/node_modules/@legendapp/spark-desktop-app/src/api.ts +86 -40
  164. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/accelerator.ts +40 -0
  165. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/index.ts +56 -0
  166. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +114 -0
  167. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/native-buffer.ts +22 -0
  168. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/path.ts +24 -0
  169. package/vendor/node_modules/@legendapp/spark-desktop-app/src/events.ts +75 -0
  170. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.ts +2 -0
  171. package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.windows.ts +2 -0
  172. package/vendor/node_modules/@legendapp/spark-desktop-app/src/open-requests.ts +43 -0
  173. package/vendor/node_modules/@legendapp/spark-desktop-app/src/restoration.ts +11 -0
  174. package/vendor/node_modules/@legendapp/spark-desktop-app/src/transport.ts +10 -0
  175. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.cjs +10 -5
  176. package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.d.cts +3 -0
  177. package/vendor/node_modules/@legendapp/spark-desktop-config/autolinking.cjs +35 -0
  178. package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +16 -3
  179. package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
  180. package/vendor/node_modules/@legendapp/spark-desktop-config/expo.d.cts +3 -0
  181. package/vendor/node_modules/@legendapp/spark-desktop-config/identity.cjs +3 -2
  182. package/vendor/node_modules/@legendapp/spark-desktop-config/lifecycle.cjs +20 -0
  183. package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +6 -5
  184. package/vendor/node_modules/@legendapp/spark-desktop-config/schema.json +276 -58
  185. package/vendor/node_modules/@legendapp/spark-desktop-config/updates.cjs +1 -0
  186. package/vendor/node_modules/@legendapp/spark-desktop-config/validation.cjs +35 -0
  187. package/vendor/node_modules/@legendapp/spark-desktop-config/windows.plugin.cjs +2 -1
  188. package/vendor/node_modules/@legendapp/spark-desktop-host/AppDelegate.mm +129 -11
  189. package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
  190. package/vendor/node_modules/@legendapp/spark-desktop-host/windows/application.inc +277 -121
  191. package/vendor/node_modules/@legendapp/spark-desktop-links/common/AuthLoopback.h +5 -7
  192. package/vendor/node_modules/@legendapp/spark-desktop-links/macos/RNDesktopLinks.mm +10 -9
  193. package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
  194. package/vendor/node_modules/@legendapp/spark-desktop-links/src/NativeDesktopLinks.ts +1 -1
  195. package/vendor/node_modules/@legendapp/spark-desktop-links/src/api.ts +27 -33
  196. package/vendor/node_modules/@legendapp/spark-desktop-links/src/expo.ts +3 -4
  197. package/vendor/node_modules/@legendapp/spark-desktop-links/src/native.ts +9 -0
  198. package/vendor/node_modules/@legendapp/spark-desktop-links/windows/SparkLinks/SparkLinks.h +22 -22
  199. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/RNDesktopShortcuts.podspec +1 -0
  200. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/RNDesktopShortcuts.mm +1 -1
  201. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.h +9 -0
  202. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.mm +307 -0
  203. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +8 -5
  204. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/NativeDesktopShortcuts.ts +1 -1
  205. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/api.ts +39 -14
  206. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/NativeKeyboardManager.ts +12 -0
  207. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/codes.ts +108 -0
  208. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/index.ts +99 -0
  209. package/vendor/node_modules/@legendapp/spark-desktop-windows/RNDesktopWindows.podspec +2 -0
  210. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/RNDesktopWindows.mm +229 -103
  211. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/SparkWindowGeometry.h +63 -0
  212. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.h +20 -0
  213. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.mm +27 -0
  214. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.h +12 -0
  215. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +3130 -0
  216. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManagerStartup.mm +31 -0
  217. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/SidebarStartupFallbacks.mm +14 -0
  218. package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +10 -6
  219. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/NativeDesktopWindowManager.ts +1 -1
  220. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +144 -70
  221. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/diagnostics.ts +17 -0
  222. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.ts +3 -1
  223. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.windows.ts +3 -1
  224. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +53 -0
  225. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos.ts +51 -0
  226. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/transport.ts +41 -0
  227. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/types.ts +140 -0
  228. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/validation.ts +162 -0
  229. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/window-manager/NativeWindowManager.ts +28 -0
  230. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/WindowProvider.tsx +20 -0
  231. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/createWindowsNavigator.tsx +78 -0
  232. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/index.ts +6 -0
  233. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/primaryWindowLifecycle.ts +34 -0
  234. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/types.ts +11 -0
  235. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/usePrimaryWindowLifecycle.ts +18 -0
  236. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/useWindowFocusEffect.ts +17 -0
  237. package/vendor/node_modules/@legendapp/spark-documents/package.json +22 -0
  238. package/vendor/node_modules/@legendapp/spark-documents/src/controller.ts +97 -0
  239. package/vendor/node_modules/@legendapp/spark-documents/src/documentTransition.ts +13 -0
  240. package/vendor/node_modules/@legendapp/spark-documents/src/hooks.ts +139 -0
  241. package/vendor/node_modules/@legendapp/spark-documents/src/index.tsx +50 -0
  242. package/vendor/node_modules/@legendapp/spark-documents/src/reload.ts +39 -0
  243. package/vendor/node_modules/@legendapp/spark-documents/src/requests.ts +15 -0
  244. package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +4 -3
  245. package/vendor/node_modules/@legendapp/spark-drag-drop/src/DesktopDragViewNativeComponent.ts +1 -1
  246. package/vendor/node_modules/@legendapp/spark-drag-drop/src/contracts.ts +73 -18
  247. package/vendor/node_modules/@legendapp/spark-drag-drop/src/index.tsx +58 -15
  248. package/vendor/node_modules/@legendapp/spark-drag-drop/windows/SparkDragDrop/SparkDragDrop.h +14 -8
  249. package/vendor/node_modules/@legendapp/spark-file-dialog/ios/RNFileDialog.mm +34 -121
  250. package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +12 -5
  251. package/vendor/node_modules/@legendapp/spark-file-dialog/src/NativeFileDialog.ts +1 -5
  252. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.ts +19 -84
  253. package/vendor/node_modules/@legendapp/spark-file-dialog/src/native.ts +12 -0
  254. package/vendor/node_modules/@legendapp/spark-file-dialog/src/options.ts +34 -14
  255. package/vendor/node_modules/@legendapp/spark-file-dialog/src/types.ts +26 -0
  256. package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h +42 -58
  257. package/vendor/node_modules/@legendapp/spark-file-system/RNDesktopFileSystem.podspec +1 -0
  258. package/vendor/node_modules/@legendapp/spark-file-system/macos/RNDesktopFileSystem.mm +32 -21
  259. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.h +24 -0
  260. package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.mm +215 -0
  261. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.h +9 -0
  262. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.mm +82 -0
  263. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h +47 -0
  264. package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.mm +306 -0
  265. package/vendor/node_modules/@legendapp/spark-file-system/package.json +8 -6
  266. package/vendor/node_modules/@legendapp/spark-file-system/src/NativeDesktopFileSystem.ts +1 -1
  267. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/NativeFileScanner.ts +11 -0
  268. package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/index.ts +93 -0
  269. package/vendor/node_modules/@legendapp/spark-file-system/src/handles.ts +48 -14
  270. package/vendor/node_modules/@legendapp/spark-file-system/src/index.ts +134 -45
  271. package/vendor/node_modules/@legendapp/spark-file-system/src/path.ts +1 -9
  272. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h +102 -26
  273. package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.vcxproj +2 -0
  274. package/vendor/node_modules/@legendapp/spark-global-shortcuts/RNDesktopGlobalShortcuts.podspec +1 -0
  275. package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm +7 -4
  276. package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +4 -3
  277. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts +1 -1
  278. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/index.ts +39 -10
  279. package/vendor/node_modules/@legendapp/spark-global-shortcuts/windows/SparkGlobalShortcuts/SparkGlobalShortcuts.h +5 -4
  280. package/vendor/node_modules/@legendapp/spark-message-dialog/macos/RNDesktopMessageDialog.mm +2 -2
  281. package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
  282. package/vendor/node_modules/@legendapp/spark-message-dialog/src/NativeDesktopMessageDialog.ts +1 -1
  283. package/vendor/node_modules/@legendapp/spark-message-dialog/src/index.ts +48 -11
  284. package/vendor/node_modules/@legendapp/spark-native-menu/ios/RNNativeMenu.mm +141 -609
  285. package/vendor/node_modules/@legendapp/spark-native-menu/package.json +9 -4
  286. package/vendor/node_modules/@legendapp/spark-native-menu/src/NativeMenu.ts +2 -7
  287. package/vendor/node_modules/@legendapp/spark-native-menu/src/api.ts +76 -121
  288. package/vendor/node_modules/@legendapp/spark-native-menu/src/hooks.ts +97 -0
  289. package/vendor/node_modules/@legendapp/spark-native-menu/src/windows-menus.ts +43 -37
  290. package/vendor/node_modules/@legendapp/spark-notifications/macos/RNDesktopNotifications.mm +75 -30
  291. package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
  292. package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts +1 -1
  293. package/vendor/node_modules/@legendapp/spark-notifications/src/index.ts +130 -42
  294. package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +46 -8
  295. package/vendor/node_modules/@legendapp/spark-processes/macos/RNDesktopProcesses.mm +94 -21
  296. package/vendor/node_modules/@legendapp/spark-processes/package.json +4 -3
  297. package/vendor/node_modules/@legendapp/spark-processes/src/NativeDesktopProcesses.ts +1 -1
  298. package/vendor/node_modules/@legendapp/spark-processes/src/index.ts +124 -28
  299. package/vendor/node_modules/@legendapp/spark-processes/src/types.ts +34 -0
  300. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h +83 -34
  301. package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj +2 -0
  302. package/vendor/node_modules/@legendapp/spark-secure-storage/macos/RNDesktopSecureStorage.mm +49 -40
  303. package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
  304. package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts +1 -1
  305. package/vendor/node_modules/@legendapp/spark-secure-storage/src/desktop.ts +16 -14
  306. package/vendor/node_modules/@legendapp/spark-secure-storage/src/expo.ts +0 -1
  307. package/vendor/node_modules/@legendapp/spark-secure-storage/src/options.ts +3 -2
  308. package/vendor/node_modules/@legendapp/spark-secure-storage/src/unavailable.ts +6 -3
  309. package/vendor/node_modules/@legendapp/spark-settings/package.json +10 -3
  310. package/vendor/node_modules/@legendapp/spark-settings/src/fileStorage.ts +11 -0
  311. package/vendor/node_modules/@legendapp/spark-settings/src/index.ts +7 -9
  312. package/vendor/node_modules/@legendapp/spark-settings/src/storage/index.ts +142 -0
  313. package/vendor/node_modules/@legendapp/spark-settings/src/store.ts +63 -21
  314. package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +20 -0
  315. package/vendor/node_modules/@legendapp/spark-settings-ui/src/index.tsx +436 -0
  316. package/vendor/node_modules/@legendapp/spark-settings-ui/src/options.ts +10 -0
  317. package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
  318. package/vendor/node_modules/@legendapp/spark-sqlite/src/database.ts +182 -0
  319. package/vendor/node_modules/@legendapp/spark-sqlite/src/index.ts +19 -6
  320. package/vendor/node_modules/@legendapp/spark-sqlite/src/types.ts +22 -0
  321. package/vendor/node_modules/@legendapp/spark-system/macos/RNDesktopSystem.mm +36 -11
  322. package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
  323. package/vendor/node_modules/@legendapp/spark-system/src/NativeDesktopSystem.ts +1 -1
  324. package/vendor/node_modules/@legendapp/spark-system/src/index.ts +89 -32
  325. package/vendor/node_modules/@legendapp/spark-system/windows/SparkSystem/SparkSystem.h +8 -4
  326. package/vendor/node_modules/@legendapp/spark-tray/macos/RNDesktopTray.mm +20 -8
  327. package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
  328. package/vendor/node_modules/@legendapp/spark-tray/src/NativeDesktopTray.ts +1 -1
  329. package/vendor/node_modules/@legendapp/spark-tray/src/index.ts +91 -34
  330. package/vendor/node_modules/@legendapp/spark-tray/windows/SparkTray/SparkTray.h +45 -15
  331. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkButton.mm +2 -1
  332. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkTextInput.mm +10 -2
  333. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.h +15 -0
  334. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +1182 -0
  335. package/vendor/node_modules/@legendapp/spark-ui/macos/glass-effect-view/RNGlassEffectView.mm +72 -0
  336. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.h +15 -0
  337. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.mm +194 -0
  338. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.h +15 -0
  339. package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.mm +180 -0
  340. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.h +15 -0
  341. package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.mm +155 -0
  342. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.h +18 -0
  343. package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.mm +645 -0
  344. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.h +15 -0
  345. package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.mm +164 -0
  346. package/vendor/node_modules/@legendapp/spark-ui/package.json +16 -4
  347. package/vendor/node_modules/@legendapp/spark-ui/src/SparkSelectNativeComponent.ts +3 -1
  348. package/vendor/node_modules/@legendapp/spark-ui/src/SparkTextInputNativeComponent.ts +6 -2
  349. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +31 -0
  350. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +77 -0
  351. package/vendor/node_modules/@legendapp/spark-ui/src/availability.ts +10 -0
  352. package/vendor/node_modules/@legendapp/spark-ui/src/classnames/index.ts +8 -0
  353. package/vendor/node_modules/@legendapp/spark-ui/src/control.ts +42 -0
  354. package/vendor/node_modules/@legendapp/spark-ui/src/desktop.tsx +56 -0
  355. package/vendor/node_modules/@legendapp/spark-ui/src/glass-effect-view/index.tsx +15 -0
  356. package/vendor/node_modules/@legendapp/spark-ui/src/index.android.tsx +35 -21
  357. package/vendor/node_modules/@legendapp/spark-ui/src/index.ios.tsx +55 -26
  358. package/vendor/node_modules/@legendapp/spark-ui/src/index.tsx +1 -27
  359. package/vendor/node_modules/@legendapp/spark-ui/src/index.web.tsx +45 -14
  360. package/vendor/node_modules/@legendapp/spark-ui/src/index.windows.tsx +1 -42
  361. package/vendor/node_modules/@legendapp/spark-ui/src/mobile-input.tsx +11 -0
  362. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSegmentedControlNativeComponent.ts +17 -0
  363. package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSelectNativeComponent.ts +17 -0
  364. package/vendor/node_modules/@legendapp/spark-ui/src/select.ts +5 -3
  365. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/SFSymbolNativeComponent.ts +13 -0
  366. package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/index.tsx +27 -0
  367. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarItemNativeComponent.ts +25 -0
  368. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarNativeComponent.ts +24 -0
  369. package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/index.tsx +104 -0
  370. package/vendor/node_modules/@legendapp/spark-ui/src/specialized.ts +22 -0
  371. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/TextInputSearchNativeComponent.ts +33 -0
  372. package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/index.tsx +34 -0
  373. package/vendor/node_modules/@legendapp/spark-ui/src/types.ts +30 -19
  374. package/vendor/node_modules/@legendapp/spark-ui/uniwind.ts +3 -1
  375. package/vendor/node_modules/@legendapp/spark-ui/uniwind.web.ts +3 -1
  376. package/vendor/node_modules/@legendapp/spark-ui/windows/SparkUI/SparkUI.h +16 -11
  377. package/vendor/node_modules/@legendapp/spark-updates/macos/RNDesktopUpdates.mm +21 -8
  378. package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
  379. package/vendor/node_modules/@legendapp/spark-updates/src/NativeDesktopUpdates.ts +3 -2
  380. package/vendor/node_modules/@legendapp/spark-updates/src/index.ts +71 -15
  381. package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
  382. package/vendor/node_modules/@legendapp/spark-webview/src/WebView.tsx +11 -0
  383. package/vendor/node_modules/@legendapp/spark-webview/src/adapter.ts +55 -0
  384. package/vendor/node_modules/@legendapp/spark-webview/src/index.ts +2 -2
  385. package/vendor/node_modules/@legendapp/spark-webview/src/types.ts +32 -0
  386. package/vendor/node_modules/@legendapp/spark-webview/src/view-props.ts +28 -0
  387. package/vendor/node_modules/@legendapp/spark-window-options/index.cjs +51 -14
  388. package/vendor/node_modules/@legendapp/spark-window-options/index.d.ts +21 -8
  389. package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
  390. package/windows-macos.ts +1 -0
  391. package/expo-metro.cjs +0 -1
  392. package/message-dialog.ts +0 -1
  393. package/universal.cjs +0 -1
  394. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/accelerator.ts +0 -22
  395. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows-options.ts +0 -11
  396. package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.windows.ts +0 -15
  397. package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/accelerator.ts +0 -22
  398. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.h +0 -4
  399. package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.mm +0 -53
@@ -0,0 +1,56 @@
1
+ /** Contracts shared by Spark-owned APIs. This module never loads native code. */
2
+ export type SparkErrorCode =
3
+ | "E_INVALID_ARGUMENT" | "E_INVALID_DATA" | "E_UNAVAILABLE" | "E_UNSUPPORTED_PLATFORM"
4
+ | "E_UNSUPPORTED_OPTION" | "E_MODULE_UNAVAILABLE" | "E_PERMISSION_DENIED"
5
+ | "E_NOT_FOUND" | "E_NOT_EMPTY" | "E_ALREADY_EXISTS" | "E_BUSY" | "E_CLOSED"
6
+ | "E_ABORTED" | "E_TIMEOUT" | "E_NATIVE";
7
+
8
+ export class SparkError extends Error {
9
+ readonly code: SparkErrorCode;
10
+ constructor(code: SparkErrorCode, message: string, options?: ErrorOptions) {
11
+ super(message, options);
12
+ this.name = "SparkError";
13
+ this.code = code;
14
+ }
15
+ }
16
+
17
+ export type Availability =
18
+ | { available: true }
19
+ | { available: false; reason: "unsupported-platform" | "missing-module" | "host-restriction" };
20
+ export interface Subscription { remove(): void }
21
+ export interface AsyncRegistration { remove(): Promise<void> }
22
+
23
+ /** Stop callbacks immediately; join concurrent cleanup, and permit retry on failure. */
24
+ export function asyncRegistration(stop: () => void, cleanup: () => Promise<void>): AsyncRegistration {
25
+ let stopped = false;
26
+ let pending: Promise<void> | undefined;
27
+ return { remove() {
28
+ if (!stopped) { stopped = true; stop(); }
29
+ return pending ??= Promise.resolve().then(cleanup).catch(error => {
30
+ pending = undefined;
31
+ throw error;
32
+ });
33
+ } };
34
+ }
35
+
36
+ export function parseNativeResult<T>(json: string, validate: (value: unknown) => value is T): T {
37
+ let value: unknown;
38
+ try { value = JSON.parse(json); }
39
+ catch (cause) { throw new SparkError("E_INVALID_DATA", "Native response is not valid JSON", { cause }); }
40
+ if (!validate(value)) throw new SparkError("E_INVALID_DATA", "Native response does not match its contract");
41
+ return value;
42
+ }
43
+
44
+ /** Normalize errors added by Spark's native adapters, preserving the original failure. */
45
+ export function nativeError(cause: unknown): SparkError {
46
+ if (cause instanceof SparkError) return cause;
47
+ const original = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "";
48
+ const aliases: Record<string, SparkErrorCode> = { E_PERMISSION: "E_PERMISSION_DENIED", E_EXISTS: "E_ALREADY_EXISTS" };
49
+ const known: readonly string[] = ["E_INVALID_ARGUMENT", "E_INVALID_DATA", "E_UNAVAILABLE", "E_UNSUPPORTED_PLATFORM", "E_UNSUPPORTED_OPTION", "E_MODULE_UNAVAILABLE", "E_PERMISSION_DENIED", "E_NOT_FOUND", "E_NOT_EMPTY", "E_ALREADY_EXISTS", "E_BUSY", "E_CLOSED", "E_ABORTED", "E_TIMEOUT", "E_NATIVE"];
50
+ const code = aliases[original] ?? (known.includes(original) ? original as SparkErrorCode : "E_NATIVE");
51
+ return new SparkError(code, cause instanceof Error ? cause.message : "Native operation failed", { cause });
52
+ }
53
+ export function invokeNative<T>(operation: () => T | Promise<T>): Promise<T> {
54
+ try { return Promise.resolve(operation()).catch(cause => { throw nativeError(cause); }); }
55
+ catch (cause) { return Promise.reject(nativeError(cause)); }
56
+ }
@@ -0,0 +1,114 @@
1
+ import { SparkError } from "./index";
2
+ import { nativePath } from "./path";
3
+ import { parseAccelerator, type Accelerator } from "./accelerator";
4
+ export type MenuIcon = { type: "symbol"; name: string } | { type: "image"; path: string };
5
+ export type MenuRole = "new" | "open" | "save" | "saveAs" | "clearRecentDocuments" | "about" | "settings" | "services" | "hide" | "hideOthers" | "showAll" | "quit" | "undo" | "redo" | "cut" | "copy" | "paste" | "selectAll" | "minimize" | "zoom" | "close" | "toggleFullscreen";
6
+ export type MenuLocation = "app" | "file" | "edit" | "view" | "window" | "help";
7
+ export type MenuTarget = { id: string } | { role: MenuRole } | { menu: MenuLocation };
8
+ export type MenuPlacement = { before: MenuTarget; after?: never } | { after: MenuTarget; before?: never };
9
+ interface MenuEntry { target?: MenuTarget; placement?: MenuPlacement; id: string; label: string; disabled?: boolean; hidden?: boolean; icon?: MenuIcon }
10
+ export type MenuItem =
11
+ | { type: "separator" }
12
+ | (MenuEntry & { type: "action"; shortcut?: string })
13
+ | (MenuEntry & { type: "checkbox"; checked: boolean; shortcut?: string })
14
+ | (MenuEntry & { type: "submenu"; items: readonly MenuItem[] })
15
+ | (Omit<MenuEntry, "label"> & { type: "role"; role: MenuRole; label?: string; shortcut?: string })
16
+ | (MenuEntry & { type: "slider"; min: number; max: number; value: number; suffix?: string });
17
+ export type MenuAction = { type: "action"; itemId: string } | { type: "valueChanged"; itemId: string; value: number };
18
+ /** Internal recursive subset helper for feature surfaces that support fewer menu item arms. */
19
+ export type MenuItemTree<Leaf extends Exclude<MenuItem, { type: "submenu" }>> = Leaf | (Omit<Extract<MenuItem, { type: "submenu" }>, "items"> & { items: readonly MenuItemTree<Leaf>[] });
20
+ /** Internal transport shape; never reexported from feature entry points. */
21
+ export interface MenuWireItem {
22
+ id?: string; title?: string; target?: MenuTarget; placement?: MenuPlacement; _sparkOwner?: string; _sparkIdentity?: string; enabled?: boolean; hidden?: boolean; checked?: boolean; separator?: boolean;
23
+ shortcut?: Accelerator; items?: MenuWireItem[]; role?: MenuRole;
24
+ systemImageName?: string; imagePath?: string; slider?: { min: number; max: number; value: number; suffix?: string };
25
+ }
26
+ export interface MenuSupport { types: readonly MenuItem["type"][]; shortcuts?: boolean; icons?: readonly MenuIcon["type"][]; targeting?: boolean }
27
+ const roles: readonly string[] = ["new", "open", "save", "saveAs", "clearRecentDocuments", "about", "settings", "services", "hide", "hideOthers", "showAll", "quit", "undo", "redo", "cut", "copy", "paste", "selectAll", "minimize", "zoom", "close", "toggleFullscreen"];
28
+ function keys(value: object, allowed: readonly string[]) {
29
+ for (const key of Object.keys(value)) if (!allowed.includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unsupported menu property: ${key}`);
30
+ }
31
+ /** Validate and snapshot the complete tree before a native side effect. */
32
+ export function menuItems(input: readonly MenuItem[], support: MenuSupport, platform: "macos" | "windows"): MenuWireItem[] {
33
+ const ids = new Set<string>();
34
+ const active = new Set<object>();
35
+ function visit(items: readonly MenuItem[], depth: number): MenuWireItem[] {
36
+ if (!Array.isArray(items) || depth > 5 || active.has(items)) throw new SparkError("E_INVALID_ARGUMENT", "Menus must be acyclic arrays with at most five levels");
37
+ active.add(items);
38
+ try {
39
+ return items.flatMap(item => {
40
+ if (!item || typeof item !== "object" || Array.isArray(item)) throw new SparkError("E_INVALID_ARGUMENT", "Expected a menu item");
41
+ if (!support.types.includes(item.type)) throw new SparkError("E_UNSUPPORTED_OPTION", `This surface does not support menu item type: ${item.type}`);
42
+ if (item.type === "separator") { keys(item, ["type"]); return [{ separator: true }]; }
43
+ if (typeof item.id !== "string" || !item.id.length || item.id.length > 200 || item.id.includes("\0") || ids.has(item.id)) throw new SparkError("E_INVALID_ARGUMENT", "Menu item ids must be nonempty and unique");
44
+ ids.add(item.id);
45
+ const common = ["type", "id", "label", "disabled", "hidden", "icon", "target", "placement"];
46
+ keys(item, [...common, ...(item.type === "submenu" ? ["items"] : item.type === "slider" ? ["min", "max", "value", "suffix"] : item.type === "role" ? ["role", "shortcut"] : item.type === "checkbox" ? ["checked", "shortcut"] : ["shortcut"])]);
47
+ if ((item.type !== "role" || item.label !== undefined) && (typeof item.label !== "string" || !item.label.trim())) throw new SparkError("E_INVALID_ARGUMENT", "Menu items need a label");
48
+ if ([item.disabled, item.hidden].some(value => value !== undefined && typeof value !== "boolean")) throw new SparkError("E_INVALID_ARGUMENT", "Menu flags must be boolean");
49
+ const result: MenuWireItem = { id: item.id, title: item.label, enabled: !item.disabled };
50
+ if (item.target !== undefined || item.placement !== undefined) {
51
+ if (!support.targeting) throw new SparkError("E_UNSUPPORTED_OPTION", "This menu surface does not support targeting or placement");
52
+ if (item.target !== undefined) result.target = menuTarget(item.target);
53
+ if (item.placement !== undefined) {
54
+ if (!item.placement || typeof item.placement !== "object" || Array.isArray(item.placement)) throw new SparkError("E_INVALID_ARGUMENT", "Expected menu placement");
55
+ keys(item.placement, ["before", "after"]);
56
+ if ((item.placement.before !== undefined) === (item.placement.after !== undefined)) throw new SparkError("E_INVALID_ARGUMENT", "Placement requires exactly one of before or after");
57
+ result.placement = item.placement.before !== undefined ? { before: menuTarget(item.placement.before) } : { after: menuTarget(item.placement.after!) };
58
+ }
59
+ }
60
+ if (item.type === "checkbox") {
61
+ if (typeof item.checked !== "boolean") throw new SparkError("E_INVALID_ARGUMENT", "Checkbox menus require checked state");
62
+ result.checked = item.checked;
63
+ }
64
+ if (item.type === "submenu") result.items = visit(item.items, depth + 1);
65
+ if (item.type === "role") {
66
+ if (!roles.includes(item.role)) throw new SparkError("E_INVALID_ARGUMENT", "Unknown menu role");
67
+ result.role = item.role;
68
+ }
69
+ if (item.type === "slider") {
70
+ if (![item.min, item.max, item.value].every(value => typeof value === "number" && Number.isFinite(value)) || item.min >= item.max || item.value < item.min || item.value > item.max || (item.suffix !== undefined && typeof item.suffix !== "string")) throw new SparkError("E_INVALID_ARGUMENT", "Invalid menu slider range/value");
71
+ result.slider = { min: item.min, max: item.max, value: item.value, suffix: item.suffix };
72
+ }
73
+ if ("shortcut" in item && item.shortcut !== undefined) {
74
+ if (!support.shortcuts) throw new SparkError("E_UNSUPPORTED_OPTION", "This menu surface does not support shortcuts");
75
+ result.shortcut = parseAccelerator(item.shortcut, platform);
76
+ }
77
+ if (item.icon !== undefined) {
78
+ if (!item.icon || !support.icons?.includes(item.icon.type)) throw new SparkError("E_UNSUPPORTED_OPTION", "This menu surface does not support this icon type");
79
+ if (item.icon.type === "symbol") {
80
+ keys(item.icon, ["type", "name"]);
81
+ if (typeof item.icon.name !== "string" || !item.icon.name.trim()) throw new SparkError("E_INVALID_ARGUMENT", "Expected a symbol name");
82
+ result.systemImageName = item.icon.name;
83
+ } else {
84
+ keys(item.icon, ["type", "path"]);
85
+ if (typeof item.icon.path !== "string" || !item.icon.path.length || item.icon.path.includes("\0")) throw new SparkError("E_INVALID_ARGUMENT", "Expected an image path");
86
+ result.imagePath = nativePath(item.icon.path, platform);
87
+ }
88
+ }
89
+ if (item.hidden && item.target) result.hidden = true;
90
+ return item.hidden && !item.target ? [] : [result];
91
+ });
92
+ } finally { active.delete(items); }
93
+ }
94
+ return visit(input, 1);
95
+ }
96
+ export function selectableMenuIds(items: readonly MenuWireItem[]): Set<string> {
97
+ const ids = new Set<string>();
98
+ function visit(items: readonly MenuWireItem[]) {
99
+ for (const item of items) {
100
+ if (!item.enabled || item.hidden) continue;
101
+ if (item.items) visit(item.items);
102
+ else if (item.id && !item.separator && !item.role && !item.slider) ids.add(item.id);
103
+ }
104
+ }
105
+ visit(items); return ids;
106
+ }
107
+
108
+ function menuTarget(value: MenuTarget): MenuTarget {
109
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).length !== 1) throw new SparkError("E_INVALID_ARGUMENT", "Expected one semantic menu target");
110
+ if ("id" in value && typeof value.id === "string" && value.id.length && value.id.length <= 200 && !value.id.includes("\0")) return { id: value.id };
111
+ if ("role" in value && roles.includes(value.role)) return { role: value.role };
112
+ if ("menu" in value && ["app", "file", "edit", "view", "window", "help"].includes(value.menu)) return { menu: value.menu };
113
+ throw new SparkError("E_INVALID_ARGUMENT", "Invalid semantic menu target");
114
+ }
@@ -0,0 +1,22 @@
1
+ import { SparkError } from "../contracts";
2
+ export type BinaryCall = (method: string, args: string, buffer?: ArrayBuffer, offset?: number, length?: number) => Promise<unknown>;
3
+ type Module = { binaryCall?: BinaryCall; installBinary?: () => string | null | undefined };
4
+ /** Native snapshots the specified view synchronously before enqueueing I/O. */
5
+ export function callBinary(module: object, name: string, method: string, args: object, bytes?: Uint8Array): Promise<unknown> {
6
+ const native = module as Module;
7
+ let call = native.binaryCall;
8
+ if (!call) {
9
+ const globals = globalThis as unknown as Record<string, unknown>;
10
+ if (!globals[name]) {
11
+ const error = native.installBinary?.();
12
+ if (typeof error === "string") throw new SparkError("E_MODULE_UNAVAILABLE", error);
13
+ }
14
+ call = globals[name] as BinaryCall | undefined;
15
+ }
16
+ if (typeof call !== "function") throw new SparkError("E_MODULE_UNAVAILABLE", "Native binary transport is unavailable");
17
+ return call(method, JSON.stringify(args), bytes?.buffer as ArrayBuffer | undefined, bytes?.byteOffset, bytes?.byteLength);
18
+ }
19
+ export function nativeBytes(value: unknown, maximum = Number.MAX_SAFE_INTEGER): Uint8Array {
20
+ if (!(value instanceof ArrayBuffer) || value.byteLength > maximum) throw new SparkError("E_INVALID_DATA", "Invalid native byte buffer");
21
+ return new Uint8Array(value);
22
+ }
@@ -0,0 +1,24 @@
1
+ import { SparkError } from "./index";
2
+ /** Drive-qualified and UNC paths are absolute on Windows; C:foo and \foo are not. */
3
+ export function absolutePath(path: string, platform: string) {
4
+ if (typeof path !== "string") throw new SparkError("E_INVALID_ARGUMENT", "Expected an absolute path or file URL");
5
+ const windows = /^(?:[a-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]+)/i.test(path);
6
+ if (path.includes("\0") ||
7
+ !(path.startsWith("file://") || (platform === "windows" ? windows : path.startsWith("/")))) {
8
+ throw new SparkError("E_INVALID_ARGUMENT", "Expected an absolute path or file URL");
9
+ }
10
+ return path;
11
+ }
12
+
13
+ /** Decode local file URLs once, preserving native absolute paths. */
14
+ export function nativePath(path: string, platform: string): string {
15
+ absolutePath(path, platform);
16
+ if (!path.startsWith("file://")) return path;
17
+ try {
18
+ const url = new URL(path);
19
+ if ((url.hostname && url.hostname !== "localhost") || url.search || url.hash || url.username || url.password || url.port) throw new Error("Expected a local file URL without a query or fragment");
20
+ let decoded = decodeURIComponent(url.pathname);
21
+ if (platform === "windows") decoded = decoded.replace(/^\/(?=[a-z]:)/i, "").replaceAll("/", "\\");
22
+ return absolutePath(decoded, platform);
23
+ } catch (cause) { throw new SparkError("E_INVALID_ARGUMENT", "Expected a native absolute path or local file URL", { cause }); }
24
+ }
@@ -0,0 +1,75 @@
1
+ import { NativeEventEmitter, TurboModuleRegistry } from "react-native";
2
+ import type { Spec } from "./NativeDesktopApp";
3
+ import { SparkError, type Subscription } from "./contracts";
4
+ /** Internal bridge transport. Feature modules validate their own event payloads. */
5
+ export type DesktopEvent = { type: string; id?: string; url?: string; windowId?: string; bytes?: ArrayBuffer; [key: string]: unknown };
6
+ export interface DesktopEventFilter { types: readonly string[]; target?: { field: string; value: string } }
7
+ type Entry = { active: boolean; listener: (event: DesktopEvent) => void };
8
+ type Bucket = { count: number; listeners: Entry[]; targets: Map<string, Map<string, Entry[]>> };
9
+ type Hub = { count: number; buckets: Map<string, Bucket>; subscription: Subscription };
10
+ const hubs = new WeakMap<Spec, Hub>();
11
+ function deliver(listeners: readonly Entry[], event: DesktopEvent) {
12
+ // Arrays are replaced on registration, so dispatch needs no per-event snapshot.
13
+ // One throwing subscriber must not starve the rest of the bucket or escape into the native emitter.
14
+ for (const entry of listeners) {
15
+ if (!entry.active) continue;
16
+ try { entry.listener(event); }
17
+ catch (cause) { console.error(new SparkError("E_NATIVE", "Desktop event listener failed", { cause })); }
18
+ }
19
+ }
20
+ function route(bucket: Bucket | undefined, event: DesktopEvent) {
21
+ if (!bucket) return;
22
+ deliver(bucket.listeners, event);
23
+ for (const [field, targets] of bucket.targets) {
24
+ const value = event[field];
25
+ const listeners = typeof value === "string" ? targets.get(value) : undefined;
26
+ if (listeners) deliver(listeners, event);
27
+ }
28
+ }
29
+ function dispatch(hub: Hub, event: DesktopEvent) {
30
+ route(hub.buckets.get(event.type), event);
31
+ if (event.type !== "*") route(hub.buckets.get("*"), event);
32
+ }
33
+ /** Route once by native event type/resource instead of waking every feature listener. */
34
+ export function onDesktopEvent(listener: (event: DesktopEvent) => void, filter?: DesktopEventFilter): Subscription {
35
+ const native = TurboModuleRegistry.get<Spec>("NativeDesktopApp");
36
+ if (!native) throw new SparkError("E_MODULE_UNAVAILABLE", "Desktop event module is unavailable");
37
+ let hub = hubs.get(native);
38
+ if (!hub) {
39
+ const created: Hub = { count: 0, buckets: new Map(), subscription: undefined! };
40
+ created.subscription = new NativeEventEmitter(native).addListener("desktop", (event: unknown) => {
41
+ if (event && typeof event === "object" && typeof (event as DesktopEvent).type === "string") dispatch(created, event as DesktopEvent);
42
+ });
43
+ hubs.set(native, created); hub = created;
44
+ }
45
+ const owner = hub, entry: Entry = { active: true, listener };
46
+ const types = filter ? [...new Set(filter.types)] : ["*"];
47
+ const target = filter?.target ? { ...filter.target } : undefined;
48
+ owner.count++;
49
+ for (const type of types) {
50
+ const bucket = owner.buckets.get(type) ?? { count: 0, listeners: [], targets: new Map() };
51
+ owner.buckets.set(type, bucket); bucket.count++;
52
+ if (!target) bucket.listeners = [...bucket.listeners, entry];
53
+ else {
54
+ const values = bucket.targets.get(target.field) ?? new Map<string, Entry[]>();
55
+ bucket.targets.set(target.field, values);
56
+ values.set(target.value, [...(values.get(target.value) ?? []), entry]);
57
+ }
58
+ }
59
+ return { remove() {
60
+ if (!entry.active) return;
61
+ entry.active = false;
62
+ for (const type of types) {
63
+ const bucket = owner.buckets.get(type)!;
64
+ if (!target) bucket.listeners = bucket.listeners.filter(value => value !== entry);
65
+ else {
66
+ const values = bucket.targets.get(target.field)!;
67
+ const remaining = values.get(target.value)!.filter(value => value !== entry);
68
+ if (remaining.length) values.set(target.value, remaining); else values.delete(target.value);
69
+ if (!values.size) bucket.targets.delete(target.field);
70
+ }
71
+ if (--bucket.count === 0) owner.buckets.delete(type);
72
+ }
73
+ if (--owner.count === 0) { owner.subscription.remove(); hubs.delete(native); }
74
+ } };
75
+ }
@@ -1 +1,3 @@
1
1
  export * from "./api";
2
+
3
+ export { finishWindowRestoration } from "./restoration";
@@ -1 +1,3 @@
1
1
  export * from "./api";
2
+
3
+ export { finishWindowRestoration } from "./restoration";
@@ -0,0 +1,43 @@
1
+ import { Platform } from "react-native";
2
+ import { SparkError, type Subscription } from "./contracts";
3
+ import { nativePath } from "./contracts/path";
4
+ import { onDesktopEvent } from "./events";
5
+ import { callAppNative } from "./transport";
6
+ export type OpenRequest = { type: "file"; id: string; path: string } | { type: "url"; id: string; url: string };
7
+ function request(value: unknown): OpenRequest {
8
+ if (!value || typeof value !== "object") throw new SparkError("E_INVALID_DATA", "Invalid native open request");
9
+ const event = value as { type?: unknown; id?: unknown; url?: unknown };
10
+ if (typeof event.id !== "string" || !event.id || typeof event.url !== "string" || !/^[a-z][a-z0-9+.-]*:/i.test(event.url) || event.url.includes("\0")) throw new SparkError("E_INVALID_DATA", "Invalid native open request");
11
+ if (event.type === "openURL") return { type: "url", id: event.id, url: event.url };
12
+ if (event.type === "openFile" && event.url.startsWith("file://")) {
13
+ try { return { type: "file", id: event.id, path: nativePath(event.url, Platform.OS) }; }
14
+ catch (cause) { throw new SparkError("E_INVALID_DATA", "Invalid file-open URL", { cause }); }
15
+ }
16
+ throw new SparkError("E_INVALID_DATA", "Invalid native open request type");
17
+ }
18
+ /** Each subscription replays retained launch requests, deduplicating concurrent live delivery. */
19
+ export async function subscribeToOpenRequests(listener: (event: OpenRequest) => void): Promise<Subscription> {
20
+ if (typeof listener !== "function") throw new SparkError("E_INVALID_ARGUMENT", "Expected open-request listener");
21
+ if (Platform.OS !== "macos" && Platform.OS !== "windows") throw new SparkError("E_UNSUPPORTED_PLATFORM", "Desktop open requests require a desktop host");
22
+ const seen = new Set<string>(); let removed = false, replaying = true;
23
+ function prune() { while (seen.size > 200) seen.delete(seen.values().next().value!); }
24
+ function deliver(event: OpenRequest) {
25
+ if (removed || seen.has(event.id)) return;
26
+ seen.add(event.id); if (!replaying) prune();
27
+ // A listener that throws must not abort the launch-queue replay for the rest of the batch.
28
+ try { listener(event); } catch (cause) { console.error(new SparkError("E_NATIVE", "Open request listener failed", { cause })); }
29
+ }
30
+ const subscription = onDesktopEvent(event => {
31
+ if (event.type !== "openURL" && event.type !== "openFile") return;
32
+ let value: OpenRequest;
33
+ try { value = request(event); } catch { return; }
34
+ deliver(value);
35
+ }, { types: ["openURL", "openFile"] });
36
+ try {
37
+ const values = await callAppNative("pendingURLs", (value): value is unknown[] => Array.isArray(value));
38
+ const requests = values.map(request);
39
+ for (const event of requests) deliver(event);
40
+ replaying = false; prune();
41
+ } catch (error) { removed = true; subscription.remove(); throw error; }
42
+ return { remove() { if (!removed) { removed = true; subscription.remove(); } } };
43
+ }
@@ -0,0 +1,11 @@
1
+ import { Platform, TurboModuleRegistry, type TurboModule } from "react-native";
2
+ interface RestorationNative extends TurboModule { finishWindowRestoration(): Promise<string> }
3
+ import { SparkError, invokeNative, parseNativeResult } from "./contracts";
4
+ /** Call after application restoration to discard startup shells no document adopted. */
5
+ export async function finishWindowRestoration(): Promise<void> {
6
+ if (Platform.OS !== "macos") throw new SparkError("E_UNSUPPORTED_PLATFORM", "Native window-shell restoration requires macOS");
7
+ const native = TurboModuleRegistry.get<RestorationNative>("NativeWindowManager");
8
+ if (!native) throw new SparkError("E_MODULE_UNAVAILABLE", "Native window restoration is unavailable");
9
+ const result = parseNativeResult(await invokeNative(() => native.finishWindowRestoration()), (value): value is { success: boolean } => !!value && typeof value === "object" && typeof (value as any).success === "boolean");
10
+ if (!result.success) throw new SparkError("E_NATIVE", "Could not finish native window restoration");
11
+ }
@@ -0,0 +1,10 @@
1
+ import { Platform, TurboModuleRegistry } from "react-native";
2
+ import type { Spec } from "./NativeDesktopApp";
3
+ import { SparkError, invokeNative, parseNativeResult } from "./contracts";
4
+ /** Internal native transport; each feature supplies its own response validator. */
5
+ export async function callAppNative<T>(method: string, validate: (value: unknown) => value is T, args: object = {}): Promise<T> {
6
+ if (Platform.OS !== "macos" && Platform.OS !== "windows") throw new SparkError("E_UNSUPPORTED_PLATFORM", "Desktop application APIs require a desktop host");
7
+ const native = TurboModuleRegistry.get<Spec>("NativeDesktopApp");
8
+ if (!native) throw new SparkError("E_MODULE_UNAVAILABLE", "Desktop application module is unavailable");
9
+ return parseNativeResult(await invokeNative(() => native.call(method, JSON.stringify(args))), validate);
10
+ }
@@ -9,6 +9,7 @@ const {
9
9
  } = require("expo-desktop-config-plugins");
10
10
  const { identity } = require("./identity.cjs");
11
11
  const { resolveEntitlements } = require("./entitlements.cjs");
12
+ const { version: sparkVersion } = require("@legendapp/spark/package.json");
12
13
 
13
14
  module.exports = function withSparkDesktop(config) {
14
15
  if (config.platforms?.length === 1 && config.platforms[0] === "windows") return require("./windows.plugin.cjs")(config);
@@ -27,10 +28,7 @@ module.exports = function withSparkDesktop(config) {
27
28
  });
28
29
  config = withAppDelegate(config, (mod) => {
29
30
  // Own this adapter; application customizations belong in configuration/plugins.
30
- mod.modResults.contents = fs.readFileSync(
31
- require.resolve("@legendapp/spark-desktop-host/AppDelegate.mm"),
32
- "utf8",
33
- );
31
+ mod.modResults.contents = fs.readFileSync(require.resolve("@legendapp/spark-desktop-host/AppDelegate.mm"), "utf8");
34
32
  return mod;
35
33
  });
36
34
  config = withInfoPlist(config, (mod) => {
@@ -38,11 +36,12 @@ module.exports = function withSparkDesktop(config) {
38
36
  for (const key of ["SUFeedURL", "SUPublicEDKey", "SUEnableAutomaticChecks", "SUAutomaticallyUpdate", "SUAllowsAutomaticUpdates", "SUEnableSystemProfiling", "SURequireSignedFeed", "SUVerifyUpdateBeforeExtraction"])
39
37
  if (!(key in generated)) delete mod.modResults[key];
40
38
  Object.assign(mod.modResults, generated);
41
- mod.modResults.SparkFrameworkVersion = "0.1.0-prototype.0";
39
+ mod.modResults.SparkFrameworkVersion = sparkVersion;
42
40
  mod.modResults.NSAppTransportSecurity = { NSAllowsLocalNetworking: true };
43
41
  return mod;
44
42
  });
45
43
  return withPodfile(config, (mod) => {
44
+ require("./autolinking.cjs").installAutolinkingPatch(mod.modRequest.projectRoot);
46
45
  require("./fabric-lifecycle.cjs").installSurfaceLifecyclePatch(mod.modRequest.projectRoot);
47
46
  require("./keyboard-events.cjs").installKeyboardEventsPatch(mod.modRequest.projectRoot);
48
47
  // The beta template passes the resolved package to use_react_native!, but
@@ -51,6 +50,12 @@ module.exports = function withSparkDesktop(config) {
51
50
  "react_native_post_install(installer)",
52
51
  'react_native_post_install(installer, "#{config[:reactNativePath]}-macos")',
53
52
  );
53
+ // Bun/pnpm may resolve RN and RN macOS into different physical directories.
54
+ // Appending "-macos" to RN's resolved path is only valid for flat installs.
55
+ const macOSPath = JSON.stringify(path.relative(path.join(mod.modRequest.projectRoot, "macos"), path.dirname(require.resolve("react-native-macos/package.json", {
56
+ paths: [mod.modRequest.projectRoot],
57
+ })))).replace(/#\{/g, "\\#{");
58
+ mod.modResults.contents = mod.modResults.contents.replaceAll('"#{config[:reactNativePath]}-macos"', macOSPath);
54
59
  const selectionFile = statePath(mod.modRequest.projectRoot, "native-selection.json", "macos");
55
60
  const included = fs.existsSync(selectionFile) ? JSON.parse(fs.readFileSync(selectionFile, "utf8")).included : [];
56
61
  const updatePackage = included.find(pkg => pkg.name === "@legendapp/spark-updates");
@@ -0,0 +1,3 @@
1
+ import type { ConfigPlugin } from "@expo/config-plugins";
2
+ declare const withSparkDesktop: ConfigPlugin;
3
+ export = withSparkDesktop;
@@ -0,0 +1,35 @@
1
+ // Expo 54's file scanner ignores Bun file: dependencies' symlinked podspecs.
2
+ const fs = require("node:fs");
3
+ const path = require("node:path");
4
+ const before = ` return (await fs_1.default.promises.readdir(targetPath, { withFileTypes: true }))
5
+ .filter((entry) => entry.isFile() && filter(entry.name))`;
6
+ const after = ` const entries = await fs_1.default.promises.readdir(targetPath, { withFileTypes: true });
7
+ const files = await Promise.all(entries.map(async (entry) => {
8
+ if (!filter(entry.name)) return null;
9
+ if (entry.isFile()) return entry;
10
+ if (!entry.isSymbolicLink()) return null;
11
+ const stat = await fs_1.default.promises.stat(path_1.default.join(targetPath, entry.name)).catch(() => null);
12
+ return stat?.isFile() ? entry : null;
13
+ }));
14
+ return files
15
+ .filter((entry) => entry !== null)`;
16
+ function patchAutolinkingSource(source) {
17
+ if (source.includes(after) && !source.includes(before)) return source;
18
+ if (source.split(before).length !== 2 || source.includes(after))
19
+ throw new Error("Expo autolinking source changed; review the symlink compatibility patch before building.");
20
+ return source.replace(before, after);
21
+ }
22
+ function installAutolinkingPatch(root) {
23
+ const manifest = require.resolve("expo-modules-autolinking/package.json", { paths: [root] });
24
+ const version = JSON.parse(fs.readFileSync(manifest, "utf8")).version;
25
+ if (version !== "3.0.27") throw new Error(`spark's autolinking patch requires expo-modules-autolinking@3.0.27; found ${version}.`);
26
+ const file = path.join(path.dirname(manifest), "build/utils.js");
27
+ const source = fs.readFileSync(file, "utf8");
28
+ const patched = patchAutolinkingSource(source);
29
+ if (source !== patched) {
30
+ const temporary = `${file}.spark-${process.pid}.tmp`;
31
+ fs.writeFileSync(temporary, patched);
32
+ fs.renameSync(temporary, file); // Do not modify package-cache hardlinks.
33
+ }
34
+ }
35
+ module.exports = { patchAutolinkingSource, installAutolinkingPatch };
@@ -1,3 +1,4 @@
1
+ const { validateOwned, validatePlatforms } = require("./validation.cjs");
1
2
  const fs = require("node:fs");
2
3
  const path = require("node:path");
3
4
  const { createHash } = require("node:crypto");
@@ -20,6 +21,7 @@ function statePath(root, name, target = process.env.SPARK_PLATFORM) {
20
21
  return path.join(root, ".spark", "platforms", target, name);
21
22
  }
22
23
  function selectTarget(platforms, target) {
24
+ validatePlatforms(platforms);
23
25
  const selected = target ?? (platforms.includes(process.platform === "win32" ? "windows" : "macos") ? (process.platform === "win32" ? "windows" : "macos") : platforms[0]);
24
26
  if (!platforms.includes(selected)) throw new Error(`Platform ${selected} is not supported by this project`);
25
27
  return selected;
@@ -46,7 +48,11 @@ function readConfig(root, target = process.env.SPARK_PLATFORM) {
46
48
  if (previous === undefined) delete process.env.SPARK_PLATFORM; else process.env.SPARK_PLATFORM = previous;
47
49
  }
48
50
  }
49
- if (!fs.existsSync(file)) return JSON.parse(fs.readFileSync(path.join(root, "app.json"), "utf8"));
51
+ if (!fs.existsSync(file)) {
52
+ const value = JSON.parse(fs.readFileSync(path.join(root, "app.json"), "utf8"));
53
+ if (!value?.expo || typeof value.expo !== "object" || Array.isArray(value.expo)) throw new Error("app.json needs an expo configuration object");
54
+ return { ...value, expo: { ...value.expo, platforms: validatePlatforms(value.expo.platforms ?? ["macos"]) } };
55
+ }
50
56
  const result = toExpo(JSON.parse(fs.readFileSync(file, "utf8")), target);
51
57
  return result;
52
58
  }
@@ -85,7 +91,8 @@ function toExpo(value, target) {
85
91
  for (const key of Object.keys(value)) if (!allowed.includes(key)) throw new Error(`Unknown desktop configuration field: ${key}`);
86
92
  for (const key of ["name", "projectId", "version"]) if (typeof value[key] !== "string" || !value[key].trim()) throw new Error(`Desktop config needs ${key}`);
87
93
  const platforms = value.platforms ?? ["macos"];
88
- if (!Array.isArray(platforms) || !platforms.length || new Set(platforms).size !== platforms.length || platforms.some(p => !["macos", "windows", "ios", "android", "web"].includes(p))) throw new Error("platforms must list unique supported targets: macos, windows, ios, android, web");
94
+ validatePlatforms(platforms);
95
+ validateOwned(value);
89
96
  const selected = selectTarget(platforms, target);
90
97
  if (platforms.includes("macos") && (!value.macos || typeof value.macos.bundleIdentifier !== "string" || !/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(value.macos.bundleIdentifier))) throw new Error("macos.bundleIdentifier must be a reverse-DNS identifier");
91
98
  validateWindow(value.window ?? {});
@@ -110,7 +117,12 @@ function toExpo(value, target) {
110
117
  windows: backend.windows ?? { namespace: "DesktopApp", displayName: value.name, packageGuid: guid, projectGuid: guid },
111
118
  plugins: [...(["macos", "windows"].includes(selected) ? ["@legendapp/spark/config-plugin"] : []), ...(backend.plugins ?? []).filter(p => (Array.isArray(p) ? p[0] : p) !== "@legendapp/spark-desktop-config" && (Array.isArray(p) ? p[0] : p) !== "@legendapp/spark/config-plugin")],
112
119
  };
113
- identity(config);
120
+ const nativeIdentity = identity(config);
121
+ // Materialize the framework-owned associations before Expo's base mods run.
122
+ // This also avoids the desktop backend's broken array-valued scheme coercion.
123
+ if (selected === "macos" && nativeIdentity.CFBundleURLTypes) {
124
+ config.macos.infoPlist.CFBundleURLTypes = nativeIdentity.CFBundleURLTypes;
125
+ }
114
126
  return { expo: config };
115
127
  }
116
128
  function merge(base, override) {
@@ -146,6 +158,7 @@ function prepareConfig(root) {
146
158
  return result;
147
159
  }
148
160
  function writeUpdates(root, updates) {
161
+ require("./updates.cjs").updateConfiguration({ extra: { spark: { updates } } });
149
162
  const file = path.join(root, filename);
150
163
  if (fs.existsSync(file)) {
151
164
  const value = JSON.parse(fs.readFileSync(file, "utf8")); value.updates = updates;
@@ -1,13 +1,93 @@
1
- export function readConfig(root: string, target?: string): any;
2
- export function prepareConfig(root: string): any;
3
- export function toExpo(value: unknown, target?: string): any;
4
- export function writeUpdates(root: string, updates: unknown): void;
5
-
1
+ import type { ExpoConfig } from "@expo/config-types";
2
+ import type { WindowConfiguration } from "@legendapp/spark-window-options";
3
+ export type { WindowConfiguration } from "@legendapp/spark-window-options";
4
+ export type SparkPlatform = "macos" | "windows" | "ios" | "android" | "web";
5
+ export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
6
+ export interface UpdateConfiguration { feedURL: string; publicKey: string }
7
+ export interface DocumentType { name: string; contentTypes: string[]; role?: "Editor" | "Viewer" }
8
+ export type HelperTarget = "macos-arm64" | "macos-x64" | "windows-arm64" | "windows-x64";
9
+ export type HelperConfiguration = string | Partial<Record<HelperTarget, { directory: string; executable: string }>>;
10
+ export interface MacOSConfiguration {
11
+ bundleIdentifier: string;
12
+ buildNumber?: string;
13
+ infoPlist?: Record<string, JsonValue>;
14
+ entitlements?: Record<string, JsonValue>;
15
+ lifecycle?: MacOSLifecycle;
16
+ }
17
+ export interface SigningConfiguration {
18
+ macos?: { identity?: string; teamId?: string; entitlementsByPath?: Record<string, Record<string, JsonValue>> };
19
+ }
20
+ /** Spark-owned settings carried inside Expo's extra.spark. */
21
+ export interface SparkSettings {
22
+ projectId: string;
23
+ window?: WindowConfiguration;
24
+ documentTypes?: DocumentType[];
25
+ menuBarOnly?: boolean;
26
+ updates?: UpdateConfiguration;
27
+ include?: string[];
28
+ signing?: SigningConfiguration;
29
+ helpers?: Record<string, HelperConfiguration>;
30
+ supportedPlatforms?: SparkPlatform[];
31
+ }
32
+ /** Expo retains ownership of its configuration; Spark only adds desktop fields. */
33
+ export type ExpoOverrides = Partial<Omit<ExpoConfig, "platforms" | "extra">> & {
34
+ platforms?: SparkPlatform[];
35
+ extra?: Record<string, unknown>;
36
+ macos?: MacOSConfiguration;
37
+ windows?: { namespace?: string; displayName?: string; packageGuid?: string; projectGuid?: string; [key: string]: unknown };
38
+ autolinking?: { exclude?: string[]; [key: string]: unknown };
39
+ };
40
+ export interface SparkApplicationConfig extends Omit<SparkSettings, "supportedPlatforms"> {
41
+ $schema?: string;
42
+ name: string;
43
+ version: string;
44
+ extends?: never;
45
+ platforms?: SparkPlatform[];
46
+ macos?: MacOSConfiguration;
47
+ scheme?: string | string[];
48
+ expo?: ExpoOverrides;
49
+ expoByPlatform?: Partial<Record<SparkPlatform, ExpoOverrides>>;
50
+ }
51
+ export type SparkExpoProjectConfig = Omit<SparkApplicationConfig, "extends" | "name" | "version" | "platforms"> & {
52
+ extends: "expo";
53
+ name?: string;
54
+ version?: string;
55
+ platforms: SparkPlatform[];
56
+ };
57
+ export type SparkConfig = SparkApplicationConfig | SparkExpoProjectConfig;
58
+ export type ResolvedExpoConfig = Omit<ExpoOverrides, "platforms" | "extra"> & {
59
+ platforms: SparkPlatform[];
60
+ extra?: Record<string, unknown> & { spark?: SparkSettings };
61
+ };
62
+ export interface ResolvedConfig { expo: ResolvedExpoConfig }
63
+ export type GeneratedExpoConfig = ResolvedExpoConfig & { name: string; slug: string; version: string; extra: Record<string, unknown> & { spark: SparkSettings } };
64
+ export function readConfig(root: string, target?: string): ResolvedConfig;
65
+ export function prepareConfig(root: string): ResolvedConfig;
66
+ /** Accepts untrusted JSON; validates Spark-owned settings before returning Expo configuration. */
67
+ export function toExpo(value: unknown, target?: string): { expo: GeneratedExpoConfig };
68
+ export function writeUpdates(root: string, updates: UpdateConfiguration): void;
6
69
  export function isUniversal(root: string): boolean;
7
70
  export function isExpoProject(root: string): boolean;
8
71
  export function statePath(root: string, name: string, target?: string): string;
9
- export function selectTarget(platforms: string[], target?: string): string;
10
- export function expoConfig(root: string): any;
72
+ export function selectTarget(platforms: readonly string[], target?: string): SparkPlatform;
73
+ export function expoConfig(root: string): ResolvedExpoConfig;
74
+ export function supportedPlatforms(root: string): SparkPlatform[];
75
+ export function developmentConfig(value: unknown, target?: string): GeneratedExpoConfig;
76
+ export function applySelection(root: string, expo: ResolvedExpoConfig): ResolvedExpoConfig;
11
77
 
12
- export function supportedPlatforms(root: string): string[];
13
- export function developmentConfig(value: any, target?: string): any;
78
+ /** macOS native startup policy. Omitted fields keep Spark's normal host behavior. */
79
+ export type MacOSLifecycle = {
80
+ appearance?: "system" | "light" | "dark";
81
+ /** Linked NSObject classes conforming to SparkStartupPlugin. No automatic discovery. */
82
+ plugins?: string[];
83
+ mainWindow?: {
84
+ hidden?: boolean;
85
+ glass?: boolean;
86
+ autosaveName?: string;
87
+ backgroundColors?: { light: string; dark: string };
88
+ closeBehavior?: "close" | "hide" | "request";
89
+ reopenBehavior?: "default" | "manual" | "visibleWindows";
90
+ toolbarStyle?: "unified" | "expanded";
91
+ titlebarSeparatorStyle?: "automatic" | "none" | "line" | "shadow";
92
+ };
93
+ };
@@ -0,0 +1,3 @@
1
+ import type { ExpoOverrides } from "./config.cjs";
2
+ /** Compose Spark's desktop overlay without replacing Expo's configuration model. */
3
+ export function withSparkExpo<Context>(original: ExpoOverrides | ((context: Context) => ExpoOverrides), root: string): (context: Context) => ExpoOverrides;